Compare commits
No commits in common. "42e59badae7c1e8726f7b7a4df671ec26349152b" and "7847a22a3e9ebd85e0f07d76e3db80d02f7110e0" have entirely different histories.
42e59badae
...
7847a22a3e
24 changed files with 332 additions and 1651 deletions
|
@ -22,7 +22,6 @@ DEFINES += QT_STATICPLUGIN
|
||||||
|
|
||||||
SOURCES += src/harbour-fernschreiber.cpp \
|
SOURCES += src/harbour-fernschreiber.cpp \
|
||||||
src/appsettings.cpp \
|
src/appsettings.cpp \
|
||||||
src/boolfiltermodel.cpp \
|
|
||||||
src/chatpermissionfiltermodel.cpp \
|
src/chatpermissionfiltermodel.cpp \
|
||||||
src/chatlistmodel.cpp \
|
src/chatlistmodel.cpp \
|
||||||
src/chatmodel.cpp \
|
src/chatmodel.cpp \
|
||||||
|
@ -106,21 +105,14 @@ DISTFILES += qml/harbour-fernschreiber.qml \
|
||||||
qml/components/messageContent/MessageGame.qml \
|
qml/components/messageContent/MessageGame.qml \
|
||||||
qml/components/messageContent/MessageLocation.qml \
|
qml/components/messageContent/MessageLocation.qml \
|
||||||
qml/components/messageContent/MessagePhoto.qml \
|
qml/components/messageContent/MessagePhoto.qml \
|
||||||
qml/components/messageContent/MessagePhotoAlbum.qml \
|
|
||||||
qml/components/messageContent/MessagePoll.qml \
|
qml/components/messageContent/MessagePoll.qml \
|
||||||
qml/components/messageContent/MessageSticker.qml \
|
qml/components/messageContent/MessageSticker.qml \
|
||||||
qml/components/messageContent/MessageVenue.qml \
|
qml/components/messageContent/MessageVenue.qml \
|
||||||
qml/components/messageContent/MessageVideoAlbum.qml \
|
|
||||||
qml/components/messageContent/MessageVideoNote.qml \
|
qml/components/messageContent/MessageVideoNote.qml \
|
||||||
qml/components/messageContent/MessageVideo.qml \
|
qml/components/messageContent/MessageVideo.qml \
|
||||||
qml/components/messageContent/MessageVoiceNote.qml \
|
qml/components/messageContent/MessageVoiceNote.qml \
|
||||||
qml/components/messageContent/SponsoredMessage.qml \
|
qml/components/messageContent/SponsoredMessage.qml \
|
||||||
qml/components/messageContent/WebPagePreview.qml \
|
qml/components/messageContent/WebPagePreview.qml \
|
||||||
qml/components/messageContent/mediaAlbumPage/FullscreenOverlay.qml \
|
|
||||||
qml/components/messageContent/mediaAlbumPage/PhotoComponent.qml \
|
|
||||||
qml/components/messageContent/mediaAlbumPage/VideoComponent.qml \
|
|
||||||
qml/components/messageContent/mediaAlbumPage/ZoomArea.qml \
|
|
||||||
qml/components/messageContent/mediaAlbumPage/ZoomImage.qml \
|
|
||||||
qml/components/settingsPage/Accordion.qml \
|
qml/components/settingsPage/Accordion.qml \
|
||||||
qml/components/settingsPage/AccordionItem.qml \
|
qml/components/settingsPage/AccordionItem.qml \
|
||||||
qml/components/settingsPage/ResponsiveGrid.qml \
|
qml/components/settingsPage/ResponsiveGrid.qml \
|
||||||
|
@ -138,7 +130,6 @@ DISTFILES += qml/harbour-fernschreiber.qml \
|
||||||
qml/pages/CoverPage.qml \
|
qml/pages/CoverPage.qml \
|
||||||
qml/pages/DebugPage.qml \
|
qml/pages/DebugPage.qml \
|
||||||
qml/pages/InitializationPage.qml \
|
qml/pages/InitializationPage.qml \
|
||||||
qml/pages/MediaAlbumPage.qml \
|
|
||||||
qml/pages/NewChatPage.qml \
|
qml/pages/NewChatPage.qml \
|
||||||
qml/pages/OverviewPage.qml \
|
qml/pages/OverviewPage.qml \
|
||||||
qml/pages/AboutPage.qml \
|
qml/pages/AboutPage.qml \
|
||||||
|
@ -220,7 +211,6 @@ INSTALLS += telegram 86.png 108.png 128.png 172.png 256.png \
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
src/appsettings.h \
|
src/appsettings.h \
|
||||||
src/boolfiltermodel.h \
|
|
||||||
src/chatpermissionfiltermodel.h \
|
src/chatpermissionfiltermodel.h \
|
||||||
src/chatlistmodel.h \
|
src/chatlistmodel.h \
|
||||||
src/chatmodel.h \
|
src/chatmodel.h \
|
||||||
|
|
|
@ -32,7 +32,6 @@ ListItem {
|
||||||
property int messageIndex
|
property int messageIndex
|
||||||
property int messageViewCount
|
property int messageViewCount
|
||||||
property var myMessage
|
property var myMessage
|
||||||
property var messageAlbumMessageIds
|
|
||||||
property var reactions
|
property var reactions
|
||||||
property bool canReplyToMessage
|
property bool canReplyToMessage
|
||||||
readonly property bool isAnonymous: myMessage.sender_id["@type"] === "messageSenderChat"
|
readonly property bool isAnonymous: myMessage.sender_id["@type"] === "messageSenderChat"
|
||||||
|
@ -69,7 +68,7 @@ ListItem {
|
||||||
property var chatReactions
|
property var chatReactions
|
||||||
property var messageReactions
|
property var messageReactions
|
||||||
|
|
||||||
highlighted: (down || (isSelected && messageAlbumMessageIds.length === 0) || additionalOptionsOpened || wasNavigatedTo) && !menuOpen
|
highlighted: (down || isSelected || additionalOptionsOpened || wasNavigatedTo) && !menuOpen
|
||||||
openMenuOnPressAndHold: !messageListItem.precalculatedValues.pageIsSelecting
|
openMenuOnPressAndHold: !messageListItem.precalculatedValues.pageIsSelecting
|
||||||
|
|
||||||
signal replyToMessage()
|
signal replyToMessage()
|
||||||
|
@ -269,20 +268,20 @@ ListItem {
|
||||||
Connections {
|
Connections {
|
||||||
target: chatModel
|
target: chatModel
|
||||||
onMessagesReceived: {
|
onMessagesReceived: {
|
||||||
messageBackground.isUnread = messageIndex > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
|
messageBackground.isUnread = index > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
|
||||||
}
|
}
|
||||||
onMessagesIncrementalUpdate: {
|
onMessagesIncrementalUpdate: {
|
||||||
messageBackground.isUnread = messageIndex > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
|
messageBackground.isUnread = index > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
|
||||||
}
|
}
|
||||||
onNewMessageReceived: {
|
onNewMessageReceived: {
|
||||||
messageBackground.isUnread = messageIndex > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
|
messageBackground.isUnread = index > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
|
||||||
}
|
}
|
||||||
onUnreadCountUpdated: {
|
onUnreadCountUpdated: {
|
||||||
messageBackground.isUnread = messageIndex > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
|
messageBackground.isUnread = index > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage";
|
||||||
}
|
}
|
||||||
onLastReadSentMessageUpdated: {
|
onLastReadSentMessageUpdated: {
|
||||||
Debug.log("[ChatModel] Messages in this chat were read, new last read: ", lastReadSentIndex, ", updating description for index ", index, ", status: ", (messageIndex <= lastReadSentIndex));
|
Debug.log("[ChatModel] Messages in this chat were read, new last read: ", lastReadSentIndex, ", updating description for index ", index, ", status: ", (index <= lastReadSentIndex));
|
||||||
messageDateText.text = getMessageStatusText(myMessage, messageIndex, lastReadSentIndex, messageDateText.useElapsed);
|
messageDateText.text = getMessageStatusText(myMessage, index, lastReadSentIndex, messageDateText.useElapsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +302,7 @@ ListItem {
|
||||||
pageStack.currentPage === chatPage) {
|
pageStack.currentPage === chatPage) {
|
||||||
Debug.log("Available reactions for this message: " + reactions);
|
Debug.log("Available reactions for this message: " + reactions);
|
||||||
messageListItem.messageReactions = reactions;
|
messageListItem.messageReactions = reactions;
|
||||||
showItemCompletelyTimer.requestedIndex = messageIndex;
|
showItemCompletelyTimer.requestedIndex = index;
|
||||||
showItemCompletelyTimer.start();
|
showItemCompletelyTimer.start();
|
||||||
} else {
|
} else {
|
||||||
messageListItem.messageReactions = null;
|
messageListItem.messageReactions = null;
|
||||||
|
@ -324,13 +323,6 @@ ListItem {
|
||||||
interval: 200
|
interval: 200
|
||||||
triggeredOnStart: false
|
triggeredOnStart: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (requestedIndex === messageIndex) {
|
|
||||||
chatView.highlightMoveDuration = -1;
|
|
||||||
chatView.highlightResizeDuration = -1;
|
|
||||||
chatView.scrollToIndex(requestedIndex);
|
|
||||||
chatView.highlightMoveDuration = 0;
|
|
||||||
chatView.highlightResizeDuration = 0;
|
|
||||||
}
|
|
||||||
Debug.log("Show item completely timer triggered, requested index: " + requestedIndex + ", current index: " + index)
|
Debug.log("Show item completely timer triggered, requested index: " + requestedIndex + ", current index: " + index)
|
||||||
if (requestedIndex === index) {
|
if (requestedIndex === index) {
|
||||||
var p = chatView.contentItem.mapFromItem(reactionsColumn, 0, 0)
|
var p = chatView.contentItem.mapFromItem(reactionsColumn, 0, 0)
|
||||||
|
@ -384,10 +376,8 @@ ListItem {
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (messageListItem.hasContentComponent) {
|
if (messageListItem.hasContentComponent) {
|
||||||
var type = myMessage.content["@type"];
|
var type = myMessage.content["@type"];
|
||||||
var albumComponentPart = (myMessage.media_album_id !== "0" && ['messagePhoto', 'messageVideo'].indexOf(type) !== -1) ? 'Album' : '';
|
|
||||||
console.log('delegateComponentLoadingTimer', myMessage.media_album_id, albumComponentPart)
|
|
||||||
extraContentLoader.setSource(
|
extraContentLoader.setSource(
|
||||||
"../components/messageContent/" + type.charAt(0).toUpperCase() + type.substring(1) + albumComponentPart + ".qml",
|
"../components/messageContent/" + type.charAt(0).toUpperCase() + type.substring(1) + ".qml",
|
||||||
{
|
{
|
||||||
messageListItem: messageListItem
|
messageListItem: messageListItem
|
||||||
})
|
})
|
||||||
|
@ -451,10 +441,8 @@ ListItem {
|
||||||
}
|
}
|
||||||
height: messageTextColumn.height + precalculatedValues.paddingMediumDouble
|
height: messageTextColumn.height + precalculatedValues.paddingMediumDouble
|
||||||
width: precalculatedValues.backgroundWidth
|
width: precalculatedValues.backgroundWidth
|
||||||
|
property bool isUnread: index > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage"
|
||||||
property bool isUnread: messageIndex > chatModel.getLastReadMessageIndex() && myMessage['@type'] !== "sponsoredMessage"
|
|
||||||
color: Theme.colorScheme === Theme.LightOnDark ? (isOwnMessage ? Theme.highlightBackgroundColor : (isUnread ? Theme.secondaryHighlightColor : Theme.secondaryColor)) : (isOwnMessage ? Theme.highlightBackgroundColor : (isUnread ? Theme.backgroundGlowColor : Theme.overlayBackgroundColor))
|
color: Theme.colorScheme === Theme.LightOnDark ? (isOwnMessage ? Theme.highlightBackgroundColor : (isUnread ? Theme.secondaryHighlightColor : Theme.secondaryColor)) : (isOwnMessage ? Theme.highlightBackgroundColor : (isUnread ? Theme.backgroundGlowColor : Theme.overlayBackgroundColor))
|
||||||
|
|
||||||
radius: parent.width / 50
|
radius: parent.width / 50
|
||||||
opacity: isUnread ? 0.5 : 0.2
|
opacity: isUnread ? 0.5 : 0.2
|
||||||
visible: appSettings.showStickersAsImages || (myMessage.content['@type'] !== "messageSticker" && myMessage.content['@type'] !== "messageAnimatedEmoji")
|
visible: appSettings.showStickersAsImages || (myMessage.content['@type'] !== "messageSticker" && myMessage.content['@type'] !== "messageAnimatedEmoji")
|
||||||
|
@ -475,13 +463,7 @@ ListItem {
|
||||||
id: userText
|
id: userText
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: messageListItem.isOwnMessage
|
text: messageListItem.isOwnMessage ? qsTr("You") : Emoji.emojify( myMessage['@type'] === "sponsoredMessage" ? tdLibWrapper.getChat(myMessage.sponsor_chat_id).title : ( messageListItem.isAnonymous ? page.chatInformation.title : Functions.getUserName(messageListItem.userInformation) ), font.pixelSize)
|
||||||
? qsTr("You")
|
|
||||||
: Emoji.emojify( myMessage['@type'] === "sponsoredMessage"
|
|
||||||
? tdLibWrapper.getChat(myMessage.sponsor_chat_id).title
|
|
||||||
: ( messageListItem.isAnonymous
|
|
||||||
? page.chatInformation.title
|
|
||||||
: Functions.getUserName(messageListItem.userInformation) ), font.pixelSize)
|
|
||||||
font.pixelSize: Theme.fontSizeExtraSmall
|
font.pixelSize: Theme.fontSizeExtraSmall
|
||||||
font.weight: Font.ExtraBold
|
font.weight: Font.ExtraBold
|
||||||
color: messageListItem.textColor
|
color: messageListItem.textColor
|
||||||
|
@ -664,8 +646,7 @@ ListItem {
|
||||||
id: extraContentLoader
|
id: extraContentLoader
|
||||||
width: parent.width * getContentWidthMultiplier()
|
width: parent.width * getContentWidthMultiplier()
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
readonly property var defaultExtraContentHeight: messageListItem.hasContentComponent ? chatView.getContentComponentHeight(model.content_type, myMessage.content, width, model.album_message_ids.length) : 0
|
height: item ? item.height : (messageListItem.hasContentComponent ? chatView.getContentComponentHeight(model.content_type, myMessage.content, width) : 0)
|
||||||
height: item ? item.height : defaultExtraContentHeight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
|
@ -690,7 +671,7 @@ ListItem {
|
||||||
running: true
|
running: true
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
messageDateText.text = getMessageStatusText(myMessage, messageIndex, chatView.lastReadSentIndex, messageDateText.useElapsed);
|
messageDateText.text = getMessageStatusText(myMessage, index, chatView.lastReadSentIndex, messageDateText.useElapsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,13 +684,13 @@ ListItem {
|
||||||
font.pixelSize: Theme.fontSizeTiny
|
font.pixelSize: Theme.fontSizeTiny
|
||||||
color: messageListItem.isOwnMessage ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
color: messageListItem.isOwnMessage ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
||||||
horizontalAlignment: messageListItem.textAlign
|
horizontalAlignment: messageListItem.textAlign
|
||||||
text: getMessageStatusText(myMessage, messageIndex, chatView.lastReadSentIndex, messageDateText.useElapsed)
|
text: getMessageStatusText(myMessage, index, chatView.lastReadSentIndex, messageDateText.useElapsed)
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: !messageListItem.precalculatedValues.pageIsSelecting
|
enabled: !messageListItem.precalculatedValues.pageIsSelecting
|
||||||
onClicked: {
|
onClicked: {
|
||||||
messageDateText.useElapsed = !messageDateText.useElapsed;
|
messageDateText.useElapsed = !messageDateText.useElapsed;
|
||||||
messageDateText.text = getMessageStatusText(myMessage, messageIndex, chatView.lastReadSentIndex, messageDateText.useElapsed);
|
messageDateText.text = getMessageStatusText(myMessage, index, chatView.lastReadSentIndex, messageDateText.useElapsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ Loader {
|
||||||
id: loader
|
id: loader
|
||||||
property var minithumbnail
|
property var minithumbnail
|
||||||
property bool highlighted
|
property bool highlighted
|
||||||
property int fillMode: tdLibImage.fillMode
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: !!minithumbnail
|
active: !!minithumbnail
|
||||||
sourceComponent: Component {
|
sourceComponent: Component {
|
||||||
|
@ -33,7 +32,7 @@ Loader {
|
||||||
id: minithumbnailImage
|
id: minithumbnailImage
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: "data:image/jpg;base64,"+minithumbnail.data
|
source: "data:image/jpg;base64,"+minithumbnail.data
|
||||||
fillMode: loader.fillMode
|
fillMode: tdLibImage.fillMode
|
||||||
opacity: status === Image.Ready ? 1.0 : 0.0
|
opacity: status === Image.Ready ? 1.0 : 0.0
|
||||||
cache: false
|
cache: false
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
|
@ -44,12 +43,12 @@ Loader {
|
||||||
effect: PressEffect { source: minithumbnailImage }
|
effect: PressEffect { source: minithumbnailImage }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// this had a visible impact on performance
|
|
||||||
// FastBlur {
|
FastBlur {
|
||||||
// anchors.fill: parent
|
anchors.fill: parent
|
||||||
// source: minithumbnailImage
|
source: minithumbnailImage
|
||||||
// radius: Theme.paddingLarge
|
radius: Theme.paddingLarge
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ Item {
|
||||||
|
|
||||||
readonly property bool hasVisibleThumbnail: thumbnailImage.opacity !== 1.0
|
readonly property bool hasVisibleThumbnail: thumbnailImage.opacity !== 1.0
|
||||||
&& !(videoThumbnailLoader.item && videoThumbnailLoader.item.opacity === 1.0)
|
&& !(videoThumbnailLoader.item && videoThumbnailLoader.item.opacity === 1.0)
|
||||||
property alias fillMode: thumbnailImage.fillMode
|
|
||||||
layer {
|
layer {
|
||||||
enabled: highlighted
|
enabled: highlighted
|
||||||
effect: PressEffect { source: tdlibThumbnail }
|
effect: PressEffect { source: tdlibThumbnail }
|
||||||
|
@ -67,7 +67,6 @@ Item {
|
||||||
|
|
||||||
TDLibMinithumbnail {
|
TDLibMinithumbnail {
|
||||||
id: minithumbnailLoader
|
id: minithumbnailLoader
|
||||||
fillMode: thumbnailImage.fillMode
|
|
||||||
active: !!minithumbnail && thumbnailImage.opacity < 1.0
|
active: !!minithumbnail && thumbnailImage.opacity < 1.0
|
||||||
}
|
}
|
||||||
BackgroundImage {
|
BackgroundImage {
|
||||||
|
@ -104,7 +103,6 @@ Item {
|
||||||
sourceSize.width: width
|
sourceSize.width: width
|
||||||
sourceSize.height: height
|
sourceSize.height: height
|
||||||
mimeType: tdlibThumbnail.videoMimeType
|
mimeType: tdlibThumbnail.videoMimeType
|
||||||
fillMode: thumbnailImage.fillMode == Image.PreserveAspectFit ? Thumbnail.PreserveAspectFit : Thumbnail.PreserveAspectCrop
|
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
opacity: status === Thumbnail.Ready ? 1.0 : 0.0
|
opacity: status === Thumbnail.Ready ? 1.0 : 0.0
|
||||||
Behavior on opacity { FadeAnimation {} }
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
|
|
@ -20,6 +20,7 @@ import QtQuick 2.6
|
||||||
import Sailfish.Silica 1.0
|
import Sailfish.Silica 1.0
|
||||||
import QtMultimedia 5.6
|
import QtMultimedia 5.6
|
||||||
import "../"
|
import "../"
|
||||||
|
import "../../js/functions.js" as Functions
|
||||||
import "../../js/debug.js" as Debug
|
import "../../js/debug.js" as Debug
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|
|
@ -22,25 +22,28 @@ import "../"
|
||||||
|
|
||||||
MessageContentBase {
|
MessageContentBase {
|
||||||
|
|
||||||
height: Math.max(Theme.itemSizeExtraSmall, Math.min(Math.round(width * 0.66666666), width / getAspectRatio()))
|
function calculateBiggest() {
|
||||||
readonly property alias photoData: photo.photo;
|
var candidateBiggest = rawMessage.content.photo.sizes[rawMessage.content.photo.sizes.length - 1];
|
||||||
|
if (candidateBiggest.width === 0 && rawMessage.content.photo.sizes.length > 1) {
|
||||||
onClicked: {
|
for (var i = (rawMessage.content.photo.sizes.length - 2); i >= 0; i--) {
|
||||||
pageStack.push(Qt.resolvedUrl("../../pages/MediaAlbumPage.qml"), {
|
candidateBiggest = rawMessage.content.photo.sizes[i];
|
||||||
"messages" : [rawMessage],
|
if (candidateBiggest.width > 0) {
|
||||||
})
|
|
||||||
}
|
|
||||||
function getAspectRatio() {
|
|
||||||
var candidate = photoData.sizes[photoData.sizes.length - 1];
|
|
||||||
if (candidate.width === 0 && photoData.sizes.length > 1) {
|
|
||||||
for (var i = (photoData.sizes.length - 2); i >= 0; i--) {
|
|
||||||
candidate = photoData.sizes[i];
|
|
||||||
if (candidate.width > 0) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return candidate.width / candidate.height;
|
return candidateBiggest;
|
||||||
|
}
|
||||||
|
|
||||||
|
height: Math.max(Theme.itemSizeExtraSmall, Math.min(defaultHeight, width / (biggest.width/biggest.height)))
|
||||||
|
readonly property int defaultHeight: Math.round(width * 0.66666666)
|
||||||
|
readonly property var biggest: calculateBiggest();
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
pageStack.push(Qt.resolvedUrl("../../pages/ImagePage.qml"), {
|
||||||
|
"photoData" : photo.photo,
|
||||||
|
// "pictureFileInformation" : photo.fileInformation
|
||||||
|
})
|
||||||
}
|
}
|
||||||
TDLibPhoto {
|
TDLibPhoto {
|
||||||
id: photo
|
id: photo
|
||||||
|
@ -48,4 +51,7 @@ MessageContentBase {
|
||||||
photo: rawMessage.content.photo
|
photo: rawMessage.content.photo
|
||||||
highlighted: parent.highlighted
|
highlighted: parent.highlighted
|
||||||
}
|
}
|
||||||
|
BackgroundImage {
|
||||||
|
visible: !rawMessage.content.photo.minithumbnail && photo.image.status !== Image.Ready
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,207 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2020 Sebastian J. Wolf and other contributors
|
|
||||||
|
|
||||||
This file is part of Fernschreiber.
|
|
||||||
|
|
||||||
Fernschreiber is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Fernschreiber is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
import QtQuick 2.6
|
|
||||||
import Sailfish.Silica 1.0
|
|
||||||
import "../"
|
|
||||||
|
|
||||||
MessageContentBase {
|
|
||||||
id: messageContent
|
|
||||||
property string chatId
|
|
||||||
readonly property int heightUnit: Math.round(width * 0.66666666)
|
|
||||||
readonly property var albumId: rawMessage.media_album_id
|
|
||||||
property var albumMessageIds: messageListItem ? messageListItem.messageAlbumMessageIds : []//overlayFlickable.messageAlbumMessageIds
|
|
||||||
onAlbumMessageIdsChanged: albumMessages = getMessages() //chatModel.getMessagesForAlbum(messageContent.albumId)
|
|
||||||
property var albumMessages: getMessages()//chatModel.getMessagesForAlbum(messageContent.albumId)
|
|
||||||
property bool firstLarge: albumMessages.length % 2 !== 0;
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
height: defaultExtraContentHeight//(firstLarge ? heightUnit * 0.75 : 0 ) + heightUnit * 0.25 * albumMessageIds.length
|
|
||||||
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if(messageListItem.precalculatedValues.pageIsSelecting) {
|
|
||||||
page.toggleMessageSelection(rawMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
openDetail(-1);
|
|
||||||
}
|
|
||||||
function getMessages() {
|
|
||||||
var msgs = [rawMessage];
|
|
||||||
if(messageContent.albumId === '0' || messageContent.albumMessageIds.length < 2) {
|
|
||||||
return msgs;
|
|
||||||
}
|
|
||||||
// var othermsgIds =
|
|
||||||
// getMessages from tdlib isn't faster
|
|
||||||
// if(rawMessage && rawMessage.chat_id) {
|
|
||||||
// var messages = [];
|
|
||||||
// return albumMessageIds.map(function(msgId){
|
|
||||||
// if(msgId === rawMessage.id) {
|
|
||||||
// return rawMessage;
|
|
||||||
// }
|
|
||||||
// return tdLibWrapper.getMessage(rawMessage.chat_id, msgId);
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
chatModel.getMessagesForAlbum(messageContent.albumId, 1).forEach(function(msg){
|
|
||||||
msgs.push(msg);
|
|
||||||
});
|
|
||||||
//
|
|
||||||
return msgs; //chatModel.getMessagesForAlbum(messageContent.albumId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openDetail(index) {
|
|
||||||
console.log('open detail', index || 0);
|
|
||||||
|
|
||||||
|
|
||||||
pageStack.push(Qt.resolvedUrl("../../pages/MediaAlbumPage.qml"), {
|
|
||||||
"messages" : albumMessages,
|
|
||||||
"index": index || 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Connections { // TODO: needed?
|
|
||||||
target: tdLibWrapper
|
|
||||||
|
|
||||||
onReceivedMessage: {
|
|
||||||
if (albumMessageIds.indexOf(messageId)) {
|
|
||||||
// albumMessages = getMessages()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: photoPreviewComponent
|
|
||||||
MessagePhoto {
|
|
||||||
// width: parent.width
|
|
||||||
// height: parent.height
|
|
||||||
messageListItem: messageContent.messageListItem
|
|
||||||
overlayFlickable: messageContent.overlayFlickable
|
|
||||||
rawMessage: albumMessages[modelIndex]
|
|
||||||
highlighted: mediaBackgroundItem.highlighted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Component {
|
|
||||||
id: videoPreviewComponent
|
|
||||||
Item {
|
|
||||||
property bool highlighted: mediaBackgroundItem.highlighted
|
|
||||||
anchors.fill: parent
|
|
||||||
clip: true
|
|
||||||
TDLibThumbnail {
|
|
||||||
id: tdLibImage
|
|
||||||
width: parent.width //don't use anchors here for easier custom scaling
|
|
||||||
height: parent.height
|
|
||||||
highlighted: parent.highlighted
|
|
||||||
thumbnail: albumMessages[modelIndex].content.video.thumbnail
|
|
||||||
minithumbnail: albumMessages[modelIndex].content.video.minithumbnail
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
anchors {
|
|
||||||
fill: videoIcon
|
|
||||||
leftMargin: -Theme.paddingSmall
|
|
||||||
topMargin: -Theme.paddingSmall
|
|
||||||
bottomMargin: -Theme.paddingSmall
|
|
||||||
rightMargin: -Theme.paddingLarge
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
radius: Theme.paddingSmall
|
|
||||||
color: Theme.rgba(Theme.overlayBackgroundColor, 0.4)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Icon {
|
|
||||||
id: videoIcon
|
|
||||||
source: "image://theme/icon-m-video"
|
|
||||||
width: Theme.iconSizeSmall
|
|
||||||
height: Theme.iconSizeSmall
|
|
||||||
highlighted: parent.highlighted
|
|
||||||
anchors {
|
|
||||||
right: parent.right
|
|
||||||
rightMargin: Theme.paddingSmall
|
|
||||||
bottom: parent.bottom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Flow {
|
|
||||||
id: contentGrid
|
|
||||||
property int firstWidth: firstLarge ? contentGrid.width : normalWidth
|
|
||||||
property int firstHeight: firstLarge ? heightUnit - contentGrid.spacing : normalHeight
|
|
||||||
property int normalWidth: (contentGrid.width - contentGrid.spacing) / 2
|
|
||||||
property int normalHeight: (heightUnit / 2) - contentGrid.spacing
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: Theme.paddingMedium
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: albumMessages
|
|
||||||
delegate: BackgroundItem {
|
|
||||||
id: mediaBackgroundItem
|
|
||||||
property bool isLarge: firstLarge && model.index === 0
|
|
||||||
width: model.index === 0 ? contentGrid.firstWidth : contentGrid.normalWidth
|
|
||||||
height: model.index === 0 ? contentGrid.firstHeight : contentGrid.normalHeight
|
|
||||||
|
|
||||||
readonly property bool isSelected: messageListItem.precalculatedValues.pageIsSelecting && page.selectedMessages.some(function(existingMessage) {
|
|
||||||
return existingMessage.id === albumMessages[index].id
|
|
||||||
});
|
|
||||||
highlighted: isSelected || down || messageContent.highlighted
|
|
||||||
onClicked: {
|
|
||||||
if(messageListItem.precalculatedValues.pageIsSelecting) {
|
|
||||||
page.toggleMessageSelection(albumMessages[index]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
openDetail(index);
|
|
||||||
}
|
|
||||||
onPressAndHold: {
|
|
||||||
page.toggleMessageSelection(albumMessages[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
anchors.fill: parent
|
|
||||||
// asynchronous: true
|
|
||||||
|
|
||||||
readonly property int modelIndex: index
|
|
||||||
sourceComponent: albumMessages[index].content["@type"] === 'messageVideo' ? videoPreviewComponent : photoPreviewComponent
|
|
||||||
opacity: status === Loader.Ready
|
|
||||||
Behavior on opacity {FadeAnimator{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO video:
|
|
||||||
rawMessage.content.video.thumbnail
|
|
||||||
TDLibPhoto {
|
|
||||||
id: photo
|
|
||||||
anchors.fill: parent
|
|
||||||
photo: rawMessage.content.photo
|
|
||||||
highlighted: parent.highlighted
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
Rectangle {
|
|
||||||
visible: mediaBackgroundItem.isSelected
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
}
|
|
||||||
color: 'transparent'
|
|
||||||
border.color: Theme.highlightColor
|
|
||||||
border.width: Theme.paddingSmall
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -26,12 +26,7 @@ import "../../js/debug.js" as Debug
|
||||||
MessageContentBase {
|
MessageContentBase {
|
||||||
id: videoMessageComponent
|
id: videoMessageComponent
|
||||||
|
|
||||||
property var videoData: ( rawMessage.content['@type'] === "messageVideo" )
|
property var videoData: ( rawMessage.content['@type'] === "messageVideo" ) ? rawMessage.content.video : ( ( rawMessage.content['@type'] === "messageAnimation" ) ? rawMessage.content.animation : rawMessage.content.video_note )
|
||||||
? rawMessage.content.video
|
|
||||||
: (
|
|
||||||
( rawMessage.content['@type'] === "messageAnimation" )
|
|
||||||
? rawMessage.content.animation
|
|
||||||
: rawMessage.content.video_note )
|
|
||||||
property string videoUrl;
|
property string videoUrl;
|
||||||
property int previewFileId;
|
property int previewFileId;
|
||||||
property int videoFileId;
|
property int videoFileId;
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2020 Sebastian J. Wolf and other contributors
|
|
||||||
|
|
||||||
This file is part of Fernschreiber.
|
|
||||||
|
|
||||||
Fernschreiber is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Fernschreiber is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
MessagePhotoAlbum {}
|
|
|
@ -1,279 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2020 Sebastian J. Wolf and other contributors
|
|
||||||
|
|
||||||
This file is part of Fernschreiber.
|
|
||||||
|
|
||||||
Fernschreiber is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Fernschreiber is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
import QtQuick 2.6
|
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
import Sailfish.Silica 1.0
|
|
||||||
import "../../../js/functions.js" as Functions
|
|
||||||
|
|
||||||
|
|
||||||
Item {
|
|
||||||
// id
|
|
||||||
id: overlay
|
|
||||||
// property declarations
|
|
||||||
property int pageCount
|
|
||||||
property int currentIndex
|
|
||||||
property alias text: captionLabel.text
|
|
||||||
property bool active: true
|
|
||||||
property var message
|
|
||||||
readonly property color gradientColor: '#bb000000'
|
|
||||||
readonly property int gradientPadding: Theme.itemSizeMedium
|
|
||||||
// signal declarations
|
|
||||||
// JavaScript functions
|
|
||||||
// object properties
|
|
||||||
anchors.fill: parent
|
|
||||||
opacity: active ? 1 : 0
|
|
||||||
Behavior on opacity { FadeAnimator {} }
|
|
||||||
// large property bindings
|
|
||||||
// child objects
|
|
||||||
// states
|
|
||||||
// transitions
|
|
||||||
|
|
||||||
onActiveChanged: {
|
|
||||||
console.log('overlay active', active)
|
|
||||||
}
|
|
||||||
|
|
||||||
function forwardMessage() {
|
|
||||||
var neededPermissions = Functions.getMessagesNeededForwardPermissions([message]);
|
|
||||||
pageStack.push(Qt.resolvedUrl("../../../pages/ChatSelectionPage.qml"), {
|
|
||||||
myUserId: tdLibWrapper.getUserInformation().id,
|
|
||||||
headerDescription: qsTr("Forward %Ln messages", "dialog header", 1),
|
|
||||||
payload: {fromChatId: message.chat_id, messageIds:[message.id], neededPermissions: neededPermissions},
|
|
||||||
state: "forwardMessages"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// "header"
|
|
||||||
|
|
||||||
LinearGradient {
|
|
||||||
id: topGradient
|
|
||||||
property int startY: 0;
|
|
||||||
// Behavior on startY { NumberAnimation {duration: 2000} }
|
|
||||||
start: Qt.point(0, Math.min(height-gradientPadding*2, startY))
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
top: parent.top
|
|
||||||
bottom: closeButton.bottom
|
|
||||||
|
|
||||||
bottomMargin: -gradientPadding
|
|
||||||
}
|
|
||||||
|
|
||||||
gradient: Gradient {
|
|
||||||
GradientStop { position: 0.0; color: gradientColor }
|
|
||||||
GradientStop { position: 1.0; color: 'transparent' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
IconButton {
|
|
||||||
id: closeButton
|
|
||||||
icon.source: "image://theme/icon-m-cancel?" + (pressed
|
|
||||||
? Theme.highlightColor
|
|
||||||
: Theme.lightPrimaryColor)
|
|
||||||
onClicked: pageStack.pop()
|
|
||||||
anchors {
|
|
||||||
right: parent.right
|
|
||||||
top: parent.top
|
|
||||||
margins: Theme.horizontalPageMargin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SilicaFlickable {
|
|
||||||
id: captionFlickable
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
// leftMargin: Theme.horizontalPageMargin
|
|
||||||
right: closeButton.left
|
|
||||||
top: parent.top
|
|
||||||
// topMargin: Theme.horizontalPageMargin
|
|
||||||
}
|
|
||||||
interactive: captionLabel.expanded && contentHeight > height
|
|
||||||
clip: true
|
|
||||||
height: Math.min(contentHeight, parent.height / 4)
|
|
||||||
contentHeight: captionLabel.height + Theme.horizontalPageMargin
|
|
||||||
flickableDirection: Flickable.VerticalFlick
|
|
||||||
VerticalScrollDecorator {
|
|
||||||
opacity: visible ? 1.0 : 0.0
|
|
||||||
flickable: captionFlickable
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: captionLabel
|
|
||||||
property bool expandable: expanded || height < contentHeight
|
|
||||||
property bool expanded
|
|
||||||
|
|
||||||
height: text ?
|
|
||||||
expanded
|
|
||||||
? contentHeight
|
|
||||||
: Theme.itemSizeMedium
|
|
||||||
: 0;
|
|
||||||
// maximumLineCount: expanded ? 0 : 3
|
|
||||||
color: Theme.primaryColor
|
|
||||||
// text: model.modelData.content.caption.text
|
|
||||||
text: Emoji.emojify(Functions.enhanceMessageText(message.content.caption, false), Theme.fontSizeExtraSmall)
|
|
||||||
onTextChanged: expanded = false
|
|
||||||
font.pixelSize: Theme.fontSizeExtraSmall
|
|
||||||
wrapMode: Text.WrapAnywhere
|
|
||||||
bottomPadding: expanded ? Theme.paddingLarge : 0
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
leftMargin: Theme.horizontalPageMargin
|
|
||||||
rightMargin: Theme.paddingLarge
|
|
||||||
right: parent.right
|
|
||||||
top: parent.top
|
|
||||||
topMargin: Theme.horizontalPageMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on height { NumberAnimation {duration: 300} }
|
|
||||||
Behavior on text {
|
|
||||||
SequentialAnimation {
|
|
||||||
FadeAnimation {
|
|
||||||
target: captionLabel
|
|
||||||
to: 0.0
|
|
||||||
duration: 300
|
|
||||||
}
|
|
||||||
PropertyAction {}
|
|
||||||
FadeAnimation {
|
|
||||||
target: captionLabel
|
|
||||||
to: 1.0
|
|
||||||
duration: 300
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
OpacityRampEffect {
|
|
||||||
sourceItem: captionLabel
|
|
||||||
enabled: !captionLabel.expanded
|
|
||||||
direction: OpacityRamp.TopToBottom
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: captionLabel
|
|
||||||
enabled: captionLabel.expandable
|
|
||||||
onClicked: {
|
|
||||||
captionLabel.expanded = !captionLabel.expanded
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// "footer"
|
|
||||||
LinearGradient {
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
top: buttons.top
|
|
||||||
bottom: parent.bottom
|
|
||||||
topMargin: -gradientPadding
|
|
||||||
}
|
|
||||||
|
|
||||||
gradient: Gradient {
|
|
||||||
GradientStop { position: 0.0; color: 'transparent' }
|
|
||||||
GradientStop { position: 1.0; color: gradientColor }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loader {
|
|
||||||
asynchronous: true
|
|
||||||
active: overlay.pageCount > 1
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
verticalCenter: buttons.bottom
|
|
||||||
}
|
|
||||||
sourceComponent: Component {
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: pageIndicatorRow
|
|
||||||
height: Theme.paddingSmall
|
|
||||||
spacing: height
|
|
||||||
Repeater {
|
|
||||||
id: pageIndicator
|
|
||||||
model: overlay.pageCount
|
|
||||||
Rectangle {
|
|
||||||
property bool active: model.index === overlay.currentIndex
|
|
||||||
width: pageIndicatorRow.height
|
|
||||||
height: pageIndicatorRow.height
|
|
||||||
color: active ? Theme.lightPrimaryColor : Theme.rgba(Theme.lightSecondaryColor, Theme.opacityLow)
|
|
||||||
Behavior on color { ColorAnimation {} }
|
|
||||||
radius: Theme.paddingSmall
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: buttons
|
|
||||||
height: Theme.itemSizeSmall
|
|
||||||
width: childrenRect.width
|
|
||||||
spacing: Theme.paddingLarge
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
bottom: parent.bottom
|
|
||||||
bottomMargin: Theme.paddingLarge
|
|
||||||
}
|
|
||||||
|
|
||||||
// IconButton {
|
|
||||||
// icon.source: "image://theme/icon-m-cancel?" + (pressed
|
|
||||||
// ? Theme.highlightColor
|
|
||||||
// : Theme.lightPrimaryColor)
|
|
||||||
// onClicked: pageStack.pop()
|
|
||||||
|
|
||||||
// }
|
|
||||||
IconButton {
|
|
||||||
icon.source: "image://theme/icon-m-downloads?" + (pressed
|
|
||||||
? Theme.highlightColor
|
|
||||||
: Theme.lightPrimaryColor)
|
|
||||||
onClicked: pageStack.pop()
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
width: Theme.itemSizeSmall
|
|
||||||
height: Theme.itemSizeSmall
|
|
||||||
}
|
|
||||||
|
|
||||||
IconButton {
|
|
||||||
enabled: message.can_be_forwarded
|
|
||||||
opacity: enabled ? 1.0 : 0.2
|
|
||||||
icon.source: "image://theme/icon-m-share?" + (pressed
|
|
||||||
? Theme.highlightColor
|
|
||||||
: Theme.lightPrimaryColor)
|
|
||||||
onClicked: forwardMessage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: 'hasCaption'
|
|
||||||
when: captionLabel.height > 0
|
|
||||||
PropertyChanges { target: topGradient;
|
|
||||||
startY: captionFlickable.height
|
|
||||||
}
|
|
||||||
AnchorChanges {
|
|
||||||
target: topGradient
|
|
||||||
// anchors.top: captionLabel.verticalCenter
|
|
||||||
anchors.bottom: captionFlickable.bottom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
transitions:
|
|
||||||
Transition {
|
|
||||||
AnchorAnimation { duration: 200 }
|
|
||||||
NumberAnimation { properties: "startY"; duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
|
|
||||||
import QtQuick 2.6
|
|
||||||
|
|
||||||
ZoomImage {
|
|
||||||
photoData: model.modelData.content.photo
|
|
||||||
onClicked: {
|
|
||||||
console.log('clicked', zoomed)
|
|
||||||
if(zoomed) {
|
|
||||||
zoomOut(true)
|
|
||||||
page.overlayActive = true
|
|
||||||
} else {
|
|
||||||
page.overlayActive = !page.overlayActive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,181 +0,0 @@
|
||||||
import QtQuick 2.6
|
|
||||||
import Sailfish.Silica 1.0
|
|
||||||
import WerkWolf.Fernschreiber 1.0
|
|
||||||
import QtMultimedia 5.6
|
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
import "../../"
|
|
||||||
|
|
||||||
Video {
|
|
||||||
id: video
|
|
||||||
property var videoData: model.modelData.content.video
|
|
||||||
readonly property bool isPlaying: playbackState === MediaPlayer.PlayingState
|
|
||||||
readonly property bool isCurrent: index === page.index
|
|
||||||
property bool shouldPlay
|
|
||||||
autoLoad: true
|
|
||||||
source: file.isDownloadingCompleted ? file.path : ''
|
|
||||||
onIsCurrentChanged: {
|
|
||||||
if(!isCurrent) {
|
|
||||||
pause()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onStatusChanged: {
|
|
||||||
if(status === MediaPlayer.EndOfMedia) {
|
|
||||||
page.overlayActive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TDLibThumbnail {
|
|
||||||
id: tdLibImage
|
|
||||||
|
|
||||||
property bool active: !file.isDownloadingCompleted || (!video.isPlaying && (video.position === 0 || video.status === MediaPlayer.EndOfMedia))
|
|
||||||
opacity: active ? 1 : 0
|
|
||||||
visible: active || opacity > 0
|
|
||||||
|
|
||||||
width: parent.width //don't use anchors here for easier custom scaling
|
|
||||||
height: parent.height
|
|
||||||
// highlighted: parent.highlighted
|
|
||||||
thumbnail: videoData.thumbnail
|
|
||||||
minithumbnail: videoData.minithumbnail
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TDLibFile {
|
|
||||||
id: file
|
|
||||||
autoLoad: false
|
|
||||||
tdlib: tdLibWrapper
|
|
||||||
fileInformation: videoData.video
|
|
||||||
property real progress: isDownloadingCompleted ? 1.0 : (downloadedSize / size)
|
|
||||||
onDownloadingCompletedChanged: {
|
|
||||||
if(isDownloadingCompleted) {
|
|
||||||
video.source = file.path
|
|
||||||
if(video.shouldPlay) {
|
|
||||||
video.play()
|
|
||||||
delayedOverlayHide.start()
|
|
||||||
video.shouldPlay = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: 'dl: '+file.downloadedSize
|
|
||||||
+ ' \ns: '+file.size
|
|
||||||
+ ' \nes: '+file.expectedSize
|
|
||||||
+ ' \nd:'+file.isDownloadingActive
|
|
||||||
+ ' \nc:'+file.isDownloadingCompleted
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: page.overlayActive = !page.overlayActive
|
|
||||||
}
|
|
||||||
|
|
||||||
RadialGradient { // white videos = invisible button. I can't tell since which SFOS version the opaque button is available, so:
|
|
||||||
id: buttonBg
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: Theme.itemSizeLarge; height: Theme.itemSizeLarge
|
|
||||||
property color baseColor: Theme.rgba(palette.overlayBackgroundColor, 0.2)
|
|
||||||
|
|
||||||
enabled: videoUI.active || !file.isDownloadingCompleted
|
|
||||||
opacity: enabled ? 1 : 0
|
|
||||||
Behavior on opacity { FadeAnimator {} }
|
|
||||||
gradient: Gradient {
|
|
||||||
|
|
||||||
GradientStop { position: 0.0; color: buttonBg.baseColor }
|
|
||||||
GradientStop { position: 0.3; color: buttonBg.baseColor }
|
|
||||||
GradientStop { position: 0.5; color: 'transparent' }
|
|
||||||
}
|
|
||||||
|
|
||||||
IconButton {
|
|
||||||
anchors.fill: parent
|
|
||||||
icon.source: "image://theme/icon-l-"+(video.isPlaying || video.shouldPlay ? 'pause' : 'play')+"?" + (pressed
|
|
||||||
? Theme.highlightColor
|
|
||||||
: Theme.lightPrimaryColor)
|
|
||||||
onClicked: {
|
|
||||||
if (!file.isDownloadingCompleted) {
|
|
||||||
video.shouldPlay = !video.shouldPlay;
|
|
||||||
if(video.shouldPlay) {
|
|
||||||
file.load()
|
|
||||||
} else {
|
|
||||||
file.cancel()
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (video.isPlaying) {
|
|
||||||
video.pause()
|
|
||||||
} else {
|
|
||||||
video.play()
|
|
||||||
delayedOverlayHide.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProgressCircle {
|
|
||||||
property bool active: file.isDownloadingActive
|
|
||||||
opacity: active ? 1 : 0
|
|
||||||
Behavior on opacity { FadeAnimator {} }
|
|
||||||
anchors.centerIn: parent
|
|
||||||
value: file.progress
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
id: videoUI
|
|
||||||
property bool active: overlay.active// && file.isDownloadingCompleted
|
|
||||||
anchors.fill: parent
|
|
||||||
opacity: active ? 1 : 0
|
|
||||||
Behavior on opacity { FadeAnimator {} }
|
|
||||||
|
|
||||||
Slider {
|
|
||||||
id: slider
|
|
||||||
value: video.position
|
|
||||||
minimumValue: 0
|
|
||||||
maximumValue: video.duration || 0.1
|
|
||||||
enabled: parent.active && video.seekable
|
|
||||||
width: parent.width
|
|
||||||
handleVisible: false
|
|
||||||
animateValue: true
|
|
||||||
stepSize: 500
|
|
||||||
anchors {
|
|
||||||
bottom: parent.bottom
|
|
||||||
bottomMargin: Theme.itemSizeMedium
|
|
||||||
}
|
|
||||||
valueText: value > 0 || down ? Format.formatDuration((value)/1000, Formatter.Duration) : ''
|
|
||||||
leftMargin: Theme.horizontalPageMargin
|
|
||||||
rightMargin: Theme.horizontalPageMargin
|
|
||||||
onDownChanged: {
|
|
||||||
if(!down) {
|
|
||||||
video.seek(value)
|
|
||||||
value = Qt.binding(function() { return video.position })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
anchors {
|
|
||||||
right: parent.right
|
|
||||||
rightMargin: Theme.horizontalPageMargin
|
|
||||||
bottom: parent.bottom
|
|
||||||
topMargin: Theme.paddingSmall
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeExtraSmall
|
|
||||||
text: file.isDownloadingCompleted
|
|
||||||
? Format.formatDuration((parent.maximumValue - parent.value)/1000, Formatter.Duration)
|
|
||||||
: (video.videoData.duration
|
|
||||||
? Format.formatDuration(video.videoData.duration, Formatter.Duration) + ', '
|
|
||||||
: '') + Format.formatFileSize(file.size || file.expectedSize)
|
|
||||||
color: Theme.secondaryColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: delayedOverlayHide
|
|
||||||
interval: 500
|
|
||||||
onTriggered: {
|
|
||||||
if(video.isPlaying) {
|
|
||||||
page.overlayActive = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,148 +0,0 @@
|
||||||
import QtQuick 2.6
|
|
||||||
import Sailfish.Silica 1.0
|
|
||||||
|
|
||||||
SilicaFlickable {
|
|
||||||
// id
|
|
||||||
id: flickable
|
|
||||||
// property declarations
|
|
||||||
property real zoom
|
|
||||||
property bool zoomed
|
|
||||||
// override if needed
|
|
||||||
property bool zoomEnabled: true
|
|
||||||
property real minimumZoom: fitZoom
|
|
||||||
property real maximumZoom: 4 //Math.max(fitZoom, 1) * 3
|
|
||||||
|
|
||||||
default property alias zoomContentItem: zoomContentItem.data
|
|
||||||
property alias implicitContentWidth: zoomContentItem.implicitWidth
|
|
||||||
property alias implicitContentHeight: zoomContentItem.implicitHeight
|
|
||||||
// factor for "PreserveAspectFit"
|
|
||||||
readonly property real fitZoom: implicitContentWidth > 0 && implicitContentHeight > 0
|
|
||||||
? Math.min(maximumZoom, width / implicitContentWidth, height / implicitContentHeight)
|
|
||||||
: 1.0
|
|
||||||
readonly property int minimumBoundaryAxis: (implicitContentWidth / implicitContentHeight) > (width / height) ? Qt.Horizontal : Qt.Vertical
|
|
||||||
|
|
||||||
// JavaScript functions
|
|
||||||
function zoomOut(animated) {
|
|
||||||
if (zoomed) {
|
|
||||||
if(animated) { zoomOutAnimation.start() }
|
|
||||||
else {
|
|
||||||
zoom = fitZoom
|
|
||||||
zoomed = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// object properties
|
|
||||||
contentWidth: Math.max(width, zoomContentItem.width)
|
|
||||||
contentHeight: Math.max(height, zoomContentItem.height)
|
|
||||||
enabled: !zoomOutAnimation.running && implicitContentWidth > 0 && implicitContentHeight > 0
|
|
||||||
flickableDirection: Flickable.HorizontalAndVerticalFlick
|
|
||||||
interactive: zoomed
|
|
||||||
// According to Jolla, otherwise pinching would sometimes not work:
|
|
||||||
pressDelay: 0
|
|
||||||
Binding { // Update zoom on orientation changes and set as default
|
|
||||||
target: flickable
|
|
||||||
when: !zoomed
|
|
||||||
property: "zoom"
|
|
||||||
value: minimumZoom
|
|
||||||
}
|
|
||||||
// child objects
|
|
||||||
|
|
||||||
PinchArea {
|
|
||||||
id: pinchArea
|
|
||||||
parent: flickable.contentItem
|
|
||||||
width: flickable.contentWidth
|
|
||||||
height: flickable.contentHeight
|
|
||||||
enabled: zoomEnabled && minimumZoom !== maximumZoom && flickable.enabled
|
|
||||||
onPinchUpdated: {
|
|
||||||
scrollDecoratorTimer.restart()
|
|
||||||
var f = flickable;
|
|
||||||
var requestedZoomFactor = 1.0 + pinch.scale - pinch.previousScale;
|
|
||||||
var previousWidth = f.contentWidth
|
|
||||||
var previousHeight = f.contentHeight
|
|
||||||
var targetWidth
|
|
||||||
var targetHeight
|
|
||||||
var targetZoom = requestedZoomFactor * f.zoom;
|
|
||||||
if (targetZoom < f.minimumZoom) {
|
|
||||||
f.zoom = f.minimumZoom;
|
|
||||||
f.zoomed = false;
|
|
||||||
f.contentX = 0;
|
|
||||||
f.contentY = 0;
|
|
||||||
return
|
|
||||||
} else if(targetZoom >= f.maximumZoom) {
|
|
||||||
f.zoom = f.maximumZoom;
|
|
||||||
targetHeight = f.implicitContentHeight * f.zoom
|
|
||||||
targetWidth = f.implicitContentWidth * f.zoom
|
|
||||||
}
|
|
||||||
else if(targetZoom < f.maximumZoom) {
|
|
||||||
if (f.minimumBoundaryAxis == Qt.Horizontal) {
|
|
||||||
targetWidth = f.contentWidth * requestedZoomFactor
|
|
||||||
f.zoom = targetWidth / f.implicitContentWidth
|
|
||||||
targetHeight = f.implicitContentHeight * f.zoom
|
|
||||||
} else {
|
|
||||||
targetHeight = f.contentHeight * requestedZoomFactor
|
|
||||||
f.zoom = targetHeight / f.implicitContentHeight
|
|
||||||
targetWidth = f.implicitContentWidth * f.zoom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// calculate center difference
|
|
||||||
f.contentX += pinch.previousCenter.x - pinch.center.x
|
|
||||||
f.contentY += pinch.previousCenter.y - pinch.center.y
|
|
||||||
// move to new (zoomed) center. this jumps a tiny bit, but is bearable:
|
|
||||||
if (targetWidth > f.width)
|
|
||||||
f.contentX -= (previousWidth - targetWidth)/(previousWidth/pinch.previousCenter.x)
|
|
||||||
if (targetHeight > f.height)
|
|
||||||
f.contentY -= (previousHeight - targetHeight)/(previousHeight/pinch.previousCenter.y)
|
|
||||||
|
|
||||||
f.zoomed = true
|
|
||||||
}
|
|
||||||
onPinchFinished: {
|
|
||||||
returnToBounds()
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
id: zoomContentItem
|
|
||||||
anchors.centerIn: parent
|
|
||||||
implicitWidth: flickable.width
|
|
||||||
implicitHeight: flickable.height
|
|
||||||
width: Math.ceil(implicitWidth * zoom)
|
|
||||||
height: Math.ceil(implicitHeight * zoom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// enable zoom to minimumZoom on click
|
|
||||||
ParallelAnimation {
|
|
||||||
id: zoomOutAnimation
|
|
||||||
NumberAnimation {
|
|
||||||
target: flickable
|
|
||||||
properties: "contentX, contentY"
|
|
||||||
to: 0
|
|
||||||
}
|
|
||||||
NumberAnimation {
|
|
||||||
target: flickable
|
|
||||||
property: "zoom"
|
|
||||||
to: fitZoom
|
|
||||||
}
|
|
||||||
onRunningChanged: {
|
|
||||||
if(!running) {
|
|
||||||
zoomed = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// show scroll decorators when scrolling OR zooming
|
|
||||||
Timer {
|
|
||||||
id: scrollDecoratorTimer
|
|
||||||
readonly property bool moving: flickable.moving
|
|
||||||
readonly property bool showing: moving || running
|
|
||||||
onMovingChanged: restart()
|
|
||||||
interval: 300
|
|
||||||
}
|
|
||||||
|
|
||||||
VerticalScrollDecorator {
|
|
||||||
flickable: flickable
|
|
||||||
opacity: scrollDecoratorTimer.showing ? 1.0 : 0.0
|
|
||||||
}
|
|
||||||
HorizontalScrollDecorator {
|
|
||||||
flickable: flickable
|
|
||||||
opacity: scrollDecoratorTimer.showing ? 1.0 : 0.0
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,127 +0,0 @@
|
||||||
import QtQuick 2.0
|
|
||||||
import Sailfish.Silica 1.0
|
|
||||||
import WerkWolf.Fernschreiber 1.0
|
|
||||||
import "../../"
|
|
||||||
|
|
||||||
ZoomArea {
|
|
||||||
// id
|
|
||||||
id: zoomArea
|
|
||||||
property var photoData //albumMessages[index].content.photo
|
|
||||||
property bool active: true
|
|
||||||
property alias image: image
|
|
||||||
|
|
||||||
signal clicked
|
|
||||||
|
|
||||||
maximumZoom: Math.max(Screen.width, Screen.height) / 200
|
|
||||||
// maximumZoom: Math.max(fitZoom, 1) * 3
|
|
||||||
implicitContentWidth: image.implicitWidth
|
|
||||||
implicitContentHeight: image.implicitHeight
|
|
||||||
zoomEnabled: image.status == Image.Ready
|
|
||||||
|
|
||||||
onActiveChanged: {
|
|
||||||
if (!active) {
|
|
||||||
zoomOut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
// var photoData = albumMessages[index].content.photo;
|
|
||||||
if (photoData) {
|
|
||||||
|
|
||||||
var biggestIndex = -1
|
|
||||||
for (var i = 0; i < photoData.sizes.length; i++) {
|
|
||||||
if (biggestIndex === -1 || photoData.sizes[i].width > photoData.sizes[biggestIndex].width) {
|
|
||||||
biggestIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (biggestIndex > -1) {
|
|
||||||
// imageDelegate.imageWidth = photoData.sizes[biggestIndex].width;
|
|
||||||
// imageDelegate.imageHeight = photoData.sizes[biggestIndex].height;
|
|
||||||
image.sourceSize.width = photoData.sizes[biggestIndex].width
|
|
||||||
image.sourceSize.height = photoData.sizes[biggestIndex].height
|
|
||||||
image.fileInformation = photoData.sizes[biggestIndex].photo
|
|
||||||
|
|
||||||
console.log('loading photo', JSON.stringify(image.fileInformation))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TDLibImage {
|
|
||||||
id: image
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: parent.height
|
|
||||||
source: file.isDownloadingCompleted ? file.path : ""
|
|
||||||
// enabled: true //!!file.fileId
|
|
||||||
// anchors.fill: parent
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
asynchronous: true
|
|
||||||
smooth: !(movingVertically || movingHorizontally)
|
|
||||||
|
|
||||||
// sourceSize.width: Screen.height
|
|
||||||
// visible: opacity > 0
|
|
||||||
// opacity: status === Image.Ready ? 1 : 0
|
|
||||||
|
|
||||||
Behavior on opacity { FadeAnimator{} }
|
|
||||||
}
|
|
||||||
// Label {
|
|
||||||
// anchors.fill: parent
|
|
||||||
// text: 'ok?' + image.enabled +' fileid:' +!!(image.file.fileId)
|
|
||||||
// + '\n - dl?' + image.file.isDownloadingActive
|
|
||||||
// + '\n completed?' + image.file.isDownloadingCompleted + ' path:'+ image.file.path
|
|
||||||
// + '\n ' + image.source
|
|
||||||
// wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
|
||||||
// }
|
|
||||||
// Rectangle {
|
|
||||||
// color: 'green'
|
|
||||||
// anchors.fill: image
|
|
||||||
// opacity: 0.3
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Image {
|
|
||||||
// id: image
|
|
||||||
// anchors.fill: parent
|
|
||||||
// smooth: !(movingVertically || movingHorizontally)
|
|
||||||
// sourceSize.width: Screen.height
|
|
||||||
// fillMode: Image.PreserveAspectFit
|
|
||||||
// asynchronous: true
|
|
||||||
// cache: false
|
|
||||||
|
|
||||||
// onSourceChanged: {
|
|
||||||
// zoomOut()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// opacity: status == Image.Ready ? 1 : 0
|
|
||||||
// Behavior on opacity { FadeAnimator{} }
|
|
||||||
// }
|
|
||||||
Item {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: zoomArea.contentWidth
|
|
||||||
height: zoomArea.contentHeight
|
|
||||||
onClicked: zoomArea.clicked()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BusyIndicator {
|
|
||||||
running: image.file.isDownloadingActive && !delayBusyIndicator.running
|
|
||||||
size: BusyIndicatorSize.Large
|
|
||||||
anchors.centerIn: parent
|
|
||||||
parent: zoomArea
|
|
||||||
Timer {
|
|
||||||
id: delayBusyIndicator
|
|
||||||
running: image.file.isDownloadingActive
|
|
||||||
interval: 1000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Rectangle {
|
|
||||||
// color: 'green'
|
|
||||||
// anchors.fill: parent
|
|
||||||
// parent: zoomArea
|
|
||||||
// }
|
|
||||||
}
|
|
|
@ -609,8 +609,7 @@ Page {
|
||||||
Connections {
|
Connections {
|
||||||
target: chatModel
|
target: chatModel
|
||||||
onMessagesReceived: {
|
onMessagesReceived: {
|
||||||
var proxyIndex = chatProxyModel.mapRowFromSource(modelIndex, -1);
|
Debug.log("[ChatPage] Messages received, view has ", chatView.count, " messages, last known message index ", modelIndex, ", own messages were read before index ", lastReadSentIndex);
|
||||||
Debug.log("[ChatPage] Messages received, view has ", chatView.count, " messages, last known message index ", proxyIndex, "("+modelIndex+"), own messages were read before index ", lastReadSentIndex);
|
|
||||||
if (totalCount === 0) {
|
if (totalCount === 0) {
|
||||||
if (chatPage.iterativeInitialization) {
|
if (chatPage.iterativeInitialization) {
|
||||||
chatPage.iterativeInitialization = false;
|
chatPage.iterativeInitialization = false;
|
||||||
|
@ -624,9 +623,9 @@ Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
chatView.lastReadSentIndex = lastReadSentIndex;
|
chatView.lastReadSentIndex = lastReadSentIndex;
|
||||||
chatView.scrollToIndex(proxyIndex);
|
chatView.scrollToIndex(modelIndex);
|
||||||
chatPage.loading = false;
|
chatPage.loading = false;
|
||||||
if (chatOverviewItem.visible && proxyIndex >= (chatView.count - 10)) {
|
if (chatOverviewItem.visible && modelIndex >= (chatView.count - 10)) {
|
||||||
chatView.inCooldown = true;
|
chatView.inCooldown = true;
|
||||||
chatModel.triggerLoadMoreFuture();
|
chatModel.triggerLoadMoreFuture();
|
||||||
}
|
}
|
||||||
|
@ -669,13 +668,10 @@ Page {
|
||||||
chatView.lastReadSentIndex = lastReadSentIndex;
|
chatView.lastReadSentIndex = lastReadSentIndex;
|
||||||
}
|
}
|
||||||
onMessagesIncrementalUpdate: {
|
onMessagesIncrementalUpdate: {
|
||||||
var proxyIndex = chatProxyModel.mapRowFromSource(modelIndex, -1);
|
Debug.log("Incremental update received. View now has ", chatView.count, " messages, view is on index ", modelIndex, ", own messages were read before index ", lastReadSentIndex);
|
||||||
Debug.log("Incremental update received. View now has ", chatView.count, " messages, view is on index ", proxyIndex, "("+modelIndex+"), own messages were read before index ", lastReadSentIndex);
|
|
||||||
chatView.lastReadSentIndex = lastReadSentIndex;
|
chatView.lastReadSentIndex = lastReadSentIndex;
|
||||||
if (!chatPage.isInitialized) {
|
if (!chatPage.isInitialized) {
|
||||||
if (proxyIndex > -1) {
|
chatView.scrollToIndex(modelIndex);
|
||||||
chatView.scrollToIndex(proxyIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (chatView.height > chatView.contentHeight) {
|
if (chatView.height > chatView.contentHeight) {
|
||||||
Debug.log("[ChatPage] Chat content quite small...");
|
Debug.log("[ChatPage] Chat content quite small...");
|
||||||
|
@ -751,26 +747,14 @@ Page {
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
Debug.log("scroll position changed, message index: ", lastQueuedIndex);
|
Debug.log("scroll position changed, message index: ", lastQueuedIndex);
|
||||||
Debug.log("unread count: ", chatInformation.unread_count);
|
Debug.log("unread count: ", chatInformation.unread_count);
|
||||||
var modelIndex = chatProxyModel.mapRowToSource(lastQueuedIndex);
|
var messageToRead = chatModel.getMessage(lastQueuedIndex);
|
||||||
var messageToRead = chatModel.getMessage(modelIndex);
|
|
||||||
if (messageToRead['@type'] === "sponsoredMessage") {
|
if (messageToRead['@type'] === "sponsoredMessage") {
|
||||||
Debug.log("sponsored message to read: ", messageToRead.id);
|
Debug.log("sponsored message to read: ", messageToRead.id);
|
||||||
tdLibWrapper.viewMessage(chatInformation.id, messageToRead.message_id, false);
|
tdLibWrapper.viewMessage(chatInformation.id, messageToRead.message_id, false);
|
||||||
} else if (chatInformation.unread_count > 0 && lastQueuedIndex > -1) {
|
} else if (chatInformation.unread_count > 0 && lastQueuedIndex > -1) {
|
||||||
if (messageToRead) {
|
Debug.log("message to read: ", messageToRead.id);
|
||||||
Debug.log("message to read: ", messageToRead.id);
|
if (messageToRead && messageToRead.id) {
|
||||||
var messageId = messageToRead.id;
|
tdLibWrapper.viewMessage(chatInformation.id, messageToRead.id, false);
|
||||||
var type = messageToRead.content["@type"];
|
|
||||||
if (messageToRead.media_album_id !== '0') {
|
|
||||||
var albumIds = chatModel.getMessageIdsForAlbum(messageToRead.media_album_id);
|
|
||||||
if (albumIds.length > 0) {
|
|
||||||
messageId = albumIds[albumIds.length - 1];
|
|
||||||
Debug.log("message to read last album message id: ", messageId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (messageId) {
|
|
||||||
tdLibWrapper.viewMessage(chatInformation.id, messageId, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
lastQueuedIndex = -1
|
lastQueuedIndex = -1
|
||||||
}
|
}
|
||||||
|
@ -1238,6 +1222,7 @@ Page {
|
||||||
readonly property int messageInReplyToHeight: Theme.fontSizeExtraSmall * 2.571428571 + Theme.paddingSmall;
|
readonly property int messageInReplyToHeight: Theme.fontSizeExtraSmall * 2.571428571 + Theme.paddingSmall;
|
||||||
readonly property int webPagePreviewHeight: ( (textColumnWidth * 2 / 3) + (6 * Theme.fontSizeExtraSmall) + ( 7 * Theme.paddingSmall) )
|
readonly property int webPagePreviewHeight: ( (textColumnWidth * 2 / 3) + (6 * Theme.fontSizeExtraSmall) + ( 7 * Theme.paddingSmall) )
|
||||||
readonly property bool pageIsSelecting: chatPage.isSelecting
|
readonly property bool pageIsSelecting: chatPage.isSelecting
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleScrollPositionChanged() {
|
function handleScrollPositionChanged() {
|
||||||
|
@ -1260,9 +1245,6 @@ Page {
|
||||||
positionViewAtIndex(index, (mode === undefined) ? ListView.Contain : mode)
|
positionViewAtIndex(index, (mode === undefined) ? ListView.Contain : mode)
|
||||||
if(index === chatView.count - 1) {
|
if(index === chatView.count - 1) {
|
||||||
manuallyScrolledToBottom = true;
|
manuallyScrolledToBottom = true;
|
||||||
if(!chatView.atYEnd) {
|
|
||||||
chatView.positionViewAtEnd();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1295,13 +1277,7 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BoolFilterModel {
|
model: chatModel
|
||||||
id: chatProxyModel
|
|
||||||
sourceModel: chatModel
|
|
||||||
filterRoleName: "album_entry_filter"
|
|
||||||
filterValue: false
|
|
||||||
}
|
|
||||||
model: chatProxyModel
|
|
||||||
header: Component {
|
header: Component {
|
||||||
Loader {
|
Loader {
|
||||||
active: !!chatPage.botInformation
|
active: !!chatPage.botInformation
|
||||||
|
@ -1334,8 +1310,7 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getContentComponentHeight(contentType, content, parentWidth, albumEntries) {
|
function getContentComponentHeight(contentType, content, parentWidth) {
|
||||||
var unit;
|
|
||||||
switch(contentType) {
|
switch(contentType) {
|
||||||
case "messageAnimatedEmoji":
|
case "messageAnimatedEmoji":
|
||||||
return content.animated_emoji.sticker.height;
|
return content.animated_emoji.sticker.height;
|
||||||
|
@ -1351,10 +1326,6 @@ Page {
|
||||||
case "messageVenue":
|
case "messageVenue":
|
||||||
return parentWidth * 0.66666666; // 2 / 3;
|
return parentWidth * 0.66666666; // 2 / 3;
|
||||||
case "messagePhoto":
|
case "messagePhoto":
|
||||||
if(albumEntries > 0) {
|
|
||||||
unit = (parentWidth * 0.66666666)
|
|
||||||
return (albumEntries % 2 !== 0 ? unit * 0.75 : 0) + unit * albumEntries * 0.25
|
|
||||||
}
|
|
||||||
var biggest = content.photo.sizes[content.photo.sizes.length - 1];
|
var biggest = content.photo.sizes[content.photo.sizes.length - 1];
|
||||||
var aspectRatio = biggest.width/biggest.height;
|
var aspectRatio = biggest.width/biggest.height;
|
||||||
return Math.max(Theme.itemSizeExtraSmall, Math.min(parentWidth * 0.66666666, parentWidth / aspectRatio));
|
return Math.max(Theme.itemSizeExtraSmall, Math.min(parentWidth * 0.66666666, parentWidth / aspectRatio));
|
||||||
|
@ -1363,10 +1334,6 @@ Page {
|
||||||
case "messageSticker":
|
case "messageSticker":
|
||||||
return content.sticker.height;
|
return content.sticker.height;
|
||||||
case "messageVideo":
|
case "messageVideo":
|
||||||
if(albumEntries > 0) {
|
|
||||||
unit = (parentWidth * 0.66666666)
|
|
||||||
return (albumEntries % 2 !== 0 ? unit * 0.75 : 0) + unit * albumEntries * 0.25
|
|
||||||
}
|
|
||||||
return Functions.getVideoHeight(parentWidth, content.video);
|
return Functions.getVideoHeight(parentWidth, content.video);
|
||||||
case "messageVideoNote":
|
case "messageVideoNote":
|
||||||
return parentWidth
|
return parentWidth
|
||||||
|
@ -1422,11 +1389,10 @@ Page {
|
||||||
chatId: chatModel.chatId
|
chatId: chatModel.chatId
|
||||||
myMessage: model.display
|
myMessage: model.display
|
||||||
messageId: model.message_id
|
messageId: model.message_id
|
||||||
messageAlbumMessageIds: model.album_message_ids
|
|
||||||
messageViewCount: model.view_count
|
messageViewCount: model.view_count
|
||||||
reactions: model.reactions
|
reactions: model.reactions
|
||||||
chatReactions: availableReactions
|
chatReactions: availableReactions
|
||||||
messageIndex: chatProxyModel.mapRowToSource(model.index)
|
messageIndex: model.index
|
||||||
hasContentComponent: !!myMessage.content && chatView.delegateMessagesContent.indexOf(model.content_type) > -1
|
hasContentComponent: !!myMessage.content && chatView.delegateMessagesContent.indexOf(model.content_type) > -1
|
||||||
canReplyToMessage: chatPage.canSendMessages
|
canReplyToMessage: chatPage.canSendMessages
|
||||||
onReplyToMessage: {
|
onReplyToMessage: {
|
||||||
|
@ -1447,21 +1413,9 @@ Page {
|
||||||
id: messageListViewItemSimpleComponent
|
id: messageListViewItemSimpleComponent
|
||||||
MessageListViewItemSimple {}
|
MessageListViewItemSimple {}
|
||||||
}
|
}
|
||||||
Component {
|
sourceComponent: chatView.simpleDelegateMessages.indexOf(model.content_type) > -1 ? messageListViewItemSimpleComponent : messageListViewItemComponent
|
||||||
id: messageListViewItemHiddenComponent
|
|
||||||
Item {
|
|
||||||
property var myMessage: display
|
|
||||||
property bool senderIsUser: myMessage.sender_id["@type"] === "messageSenderUser"
|
|
||||||
property var userInformation: senderIsUser ? tdLibWrapper.getUserInformation(myMessage.sender_id.user_id) : null
|
|
||||||
property bool isOwnMessage: senderIsUser && chatPage.myUserId === myMessage.sender_id.user_id
|
|
||||||
height: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sourceComponent: chatView.simpleDelegateMessages.indexOf(model.content_type) > -1
|
|
||||||
? messageListViewItemSimpleComponent
|
|
||||||
: messageListViewItemComponent
|
|
||||||
}
|
}
|
||||||
VerticalScrollDecorator { flickable: chatView }
|
VerticalScrollDecorator {}
|
||||||
|
|
||||||
ViewPlaceholder {
|
ViewPlaceholder {
|
||||||
id: chatViewPlaceholder
|
id: chatViewPlaceholder
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2020 Sebastian J. Wolf and other contributors
|
|
||||||
|
|
||||||
This file is part of Fernschreiber.
|
|
||||||
|
|
||||||
Fernschreiber is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Fernschreiber is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
// jolla-gallery/pages/FlickableImageView.qml
|
|
||||||
/*
|
|
||||||
|
|
||||||
FullscreenContentPage
|
|
||||||
- PagedView (jolla-gallery/FlickableImageView)
|
|
||||||
- delegate: Loader
|
|
||||||
- SilicaFlickable (Silica.private/ZoomableFlickable) (Sailfish.Gallery/ImageViewer)
|
|
||||||
- PinchArea
|
|
||||||
- dragDetector(?)
|
|
||||||
- image
|
|
||||||
- Item (Sailfish.Gallery/GalleryOverlay)
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.6
|
|
||||||
import Sailfish.Silica 1.0
|
|
||||||
import WerkWolf.Fernschreiber 1.0
|
|
||||||
import "../components"
|
|
||||||
|
|
||||||
import "../components/messageContent/mediaAlbumPage"
|
|
||||||
import "../js/twemoji.js" as Emoji
|
|
||||||
import "../js/functions.js" as Functions
|
|
||||||
|
|
||||||
Page {
|
|
||||||
// id
|
|
||||||
id: page
|
|
||||||
// property declarations
|
|
||||||
|
|
||||||
property alias index: pagedView.currentIndex
|
|
||||||
property alias overlayActive: overlay.active
|
|
||||||
property alias delegate: pagedView.delegate
|
|
||||||
property var messages: [];
|
|
||||||
// message.content.caption.text
|
|
||||||
palette.colorScheme: Theme.LightOnDark
|
|
||||||
clip: status !== PageStatus.Active || pageStack.dragInProgress
|
|
||||||
navigationStyle: PageNavigation.Vertical
|
|
||||||
backgroundColor: 'black'
|
|
||||||
allowedOrientations: Orientation.All
|
|
||||||
// signal declarations
|
|
||||||
// JavaScript functions
|
|
||||||
|
|
||||||
// object (parent) properties
|
|
||||||
// large property bindings
|
|
||||||
// child objects
|
|
||||||
// states
|
|
||||||
// transitions
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// content
|
|
||||||
PagedView {
|
|
||||||
id: pagedView
|
|
||||||
anchors.fill: parent
|
|
||||||
model: messages
|
|
||||||
delegate: Component {
|
|
||||||
Loader {
|
|
||||||
id: loader
|
|
||||||
asynchronous: true
|
|
||||||
visible: status == Loader.Ready
|
|
||||||
width: PagedView.contentWidth
|
|
||||||
height: PagedView.contentHeight
|
|
||||||
|
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
when: model.modelData.content['@type'] === 'messagePhoto'
|
|
||||||
PropertyChanges {
|
|
||||||
target: loader
|
|
||||||
source: "../components/messageContent/mediaAlbumPage/PhotoComponent.qml"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
when: model.modelData.content['@type'] === 'messageVideo'
|
|
||||||
PropertyChanges {
|
|
||||||
target: loader
|
|
||||||
source: "../components/messageContent/mediaAlbumPage/VideoComponent.qml"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// overlay
|
|
||||||
FullscreenOverlay {
|
|
||||||
id: overlay
|
|
||||||
pageCount: messages.length
|
|
||||||
currentIndex: page.index
|
|
||||||
message: messages[currentIndex]
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of Fernschreiber.
|
|
||||||
|
|
||||||
Fernschreiber is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Fernschreiber is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "boolfiltermodel.h"
|
|
||||||
|
|
||||||
#define DEBUG_MODULE BoolFilterModel
|
|
||||||
#include "debuglog.h"
|
|
||||||
|
|
||||||
BoolFilterModel::BoolFilterModel(QObject *parent) : QSortFilterProxyModel(parent)
|
|
||||||
{
|
|
||||||
setDynamicSortFilter(true);
|
|
||||||
// setFilterCaseSensitivity(Qt::CaseInsensitive);
|
|
||||||
// setFilterFixedString(QString());
|
|
||||||
filterValue = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoolFilterModel::setSource(QObject *model)
|
|
||||||
{
|
|
||||||
setSourceModel(qobject_cast<QAbstractItemModel*>(model));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoolFilterModel::setSourceModel(QAbstractItemModel *model)
|
|
||||||
{
|
|
||||||
if (sourceModel() != model) {
|
|
||||||
LOG(model);
|
|
||||||
QSortFilterProxyModel::setSourceModel(model);
|
|
||||||
updateFilterRole();
|
|
||||||
emit sourceChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString BoolFilterModel::getFilterRoleName() const
|
|
||||||
{
|
|
||||||
return filterRoleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoolFilterModel::setFilterRoleName(QString role)
|
|
||||||
{
|
|
||||||
if (filterRoleName != role) {
|
|
||||||
filterRoleName = role;
|
|
||||||
LOG(role);
|
|
||||||
updateFilterRole();
|
|
||||||
emit filterRoleNameChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BoolFilterModel::getFilterValue() const
|
|
||||||
{
|
|
||||||
return filterValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoolFilterModel::setFilterValue(bool value)
|
|
||||||
{
|
|
||||||
if(value != filterValue) {
|
|
||||||
filterValue = value;
|
|
||||||
invalidateFilter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int BoolFilterModel::mapRowFromSource(int i, int fallbackDirection)
|
|
||||||
{
|
|
||||||
QModelIndex myIndex = mapFromSource(sourceModel()->index(i, 0));
|
|
||||||
LOG("mapping index" << i << "to source model:" << myIndex.row() << "valid?" << myIndex.isValid());
|
|
||||||
if(myIndex.isValid()) {
|
|
||||||
return myIndex.row();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(fallbackDirection > 0) {
|
|
||||||
int max = sourceModel()->rowCount();
|
|
||||||
i += 1;
|
|
||||||
while (i < max) {
|
|
||||||
myIndex = mapFromSource(sourceModel()->index(i, 0));
|
|
||||||
|
|
||||||
LOG("fallback ++ " << i << "to source model:" << myIndex.row() << "valid?" << myIndex.isValid());
|
|
||||||
if(myIndex.isValid()) {
|
|
||||||
return myIndex.row();
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
} else if(fallbackDirection < 0) {
|
|
||||||
i -= 1;
|
|
||||||
while (i > -1) {
|
|
||||||
myIndex = mapFromSource(sourceModel()->index(i, 0));
|
|
||||||
LOG("fallback -- " << i << "to source model:" << myIndex.row() << "valid?" << myIndex.isValid());
|
|
||||||
if(myIndex.isValid()) {
|
|
||||||
return myIndex.row();
|
|
||||||
}
|
|
||||||
i -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return myIndex.row(); // may still be -1
|
|
||||||
}
|
|
||||||
|
|
||||||
int BoolFilterModel::mapRowToSource(int i)
|
|
||||||
{
|
|
||||||
QModelIndex sourceIndex = mapToSource(index(i, 0));
|
|
||||||
return sourceIndex.row();
|
|
||||||
}
|
|
||||||
bool BoolFilterModel::filterAcceptsRow(int sourceRow,
|
|
||||||
const QModelIndex &sourceParent) const
|
|
||||||
{
|
|
||||||
// sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(); //.toString().contains( /*string for column 0*/ ))
|
|
||||||
// LOG("Filter Role " << filterRole());
|
|
||||||
// QModelIndex index = this->sourceModel()->index(sourceRow,1,sourceParent);
|
|
||||||
// sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(filterRole()).toBool();
|
|
||||||
// LOG("Filter index DATA"<< sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(filterRole())); //<< index << index.isValid());
|
|
||||||
// LOG("Filter parent " << sourceParent << sourceParent.isValid());
|
|
||||||
// LOG("Filter Model Value" << sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(filterRole()).toBool());
|
|
||||||
// LOG("Filter Model filterValue" << filterValue);
|
|
||||||
// LOG("Filter Model result" << (sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(filterRole()).toBool() == filterValue));
|
|
||||||
// LOG("Filter Model MESSAGE" << sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data());
|
|
||||||
return sourceModel()->index(sourceRow, 0, sourceParent.child(sourceRow, 0)).data(filterRole()).toBool() == filterValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BoolFilterModel::findRole(QAbstractItemModel *model, QString role)
|
|
||||||
{
|
|
||||||
if (model && !role.isEmpty()) {
|
|
||||||
const QByteArray roleName(role.toUtf8());
|
|
||||||
const QHash<int,QByteArray> roleMap(model->roleNames());
|
|
||||||
const QList<int> roles(roleMap.keys());
|
|
||||||
const int n = roles.count();
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
const QByteArray name(roleMap.value(roles.at(i)));
|
|
||||||
if (name == roleName) {
|
|
||||||
LOG(role << roles.at(i));
|
|
||||||
return roles.at(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG("Unknown role" << role);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoolFilterModel::updateFilterRole()
|
|
||||||
{
|
|
||||||
const int role = findRole(sourceModel(), filterRoleName);
|
|
||||||
setFilterRole((role >= 0) ? role : Qt::DisplayRole);
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of Fernschreiber.
|
|
||||||
|
|
||||||
Fernschreiber is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Fernschreiber is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BOOLFILTERMODEL_H
|
|
||||||
#define BOOLFILTERMODEL_H
|
|
||||||
|
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
|
|
||||||
class BoolFilterModel : public QSortFilterProxyModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(QString filterRoleName READ getFilterRoleName WRITE setFilterRoleName NOTIFY filterRoleNameChanged)
|
|
||||||
Q_PROPERTY(bool filterValue READ getFilterValue WRITE setFilterValue NOTIFY filterValueChanged)
|
|
||||||
Q_PROPERTY(QObject* sourceModel READ sourceModel WRITE setSource NOTIFY sourceChanged)
|
|
||||||
|
|
||||||
public:
|
|
||||||
BoolFilterModel(QObject *parent = Q_NULLPTR);
|
|
||||||
|
|
||||||
void setSource(QObject* model);
|
|
||||||
void setSourceModel(QAbstractItemModel *model) Q_DECL_OVERRIDE;
|
|
||||||
|
|
||||||
|
|
||||||
QString getFilterRoleName() const;
|
|
||||||
void setFilterRoleName(QString role);
|
|
||||||
|
|
||||||
bool getFilterValue() const;
|
|
||||||
void setFilterValue(bool value);
|
|
||||||
Q_INVOKABLE int mapRowFromSource(int i, int fallbackDirection);
|
|
||||||
Q_INVOKABLE int mapRowToSource(int i);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void sourceChanged();
|
|
||||||
void filterRoleNameChanged();
|
|
||||||
void filterValueChanged();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void updateFilterRole();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static int findRole(QAbstractItemModel *model, QString role);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString filterRoleName;
|
|
||||||
bool filterValue;
|
|
||||||
protected:
|
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // BOOLFILTERMODEL_H
|
|
|
@ -30,7 +30,6 @@ namespace {
|
||||||
const QString ID("id");
|
const QString ID("id");
|
||||||
const QString CONTENT("content");
|
const QString CONTENT("content");
|
||||||
const QString CHAT_ID("chat_id");
|
const QString CHAT_ID("chat_id");
|
||||||
const QString DATE("date");
|
|
||||||
const QString PHOTO("photo");
|
const QString PHOTO("photo");
|
||||||
const QString SMALL("small");
|
const QString SMALL("small");
|
||||||
const QString UNREAD_COUNT("unread_count");
|
const QString UNREAD_COUNT("unread_count");
|
||||||
|
@ -49,7 +48,6 @@ namespace {
|
||||||
// "view_count": 47
|
// "view_count": 47
|
||||||
// }
|
// }
|
||||||
const QString TYPE_MESSAGE_INTERACTION_INFO("messageInteractionInfo");
|
const QString TYPE_MESSAGE_INTERACTION_INFO("messageInteractionInfo");
|
||||||
const QString MEDIA_ALBUM_ID("media_album_id");
|
|
||||||
const QString INTERACTION_INFO("interaction_info");
|
const QString INTERACTION_INFO("interaction_info");
|
||||||
const QString VIEW_COUNT("view_count");
|
const QString VIEW_COUNT("view_count");
|
||||||
const QString REACTIONS("reactions");
|
const QString REACTIONS("reactions");
|
||||||
|
@ -65,9 +63,7 @@ public:
|
||||||
RoleMessageId,
|
RoleMessageId,
|
||||||
RoleMessageContentType,
|
RoleMessageContentType,
|
||||||
RoleMessageViewCount,
|
RoleMessageViewCount,
|
||||||
RoleMessageReactions,
|
RoleMessageReactions
|
||||||
RoleMessageAlbumEntryFilter,
|
|
||||||
RoleMessageAlbumMessageIds,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum RoleFlag {
|
enum RoleFlag {
|
||||||
|
@ -75,9 +71,7 @@ public:
|
||||||
RoleFlagMessageId = 0x02,
|
RoleFlagMessageId = 0x02,
|
||||||
RoleFlagMessageContentType = 0x04,
|
RoleFlagMessageContentType = 0x04,
|
||||||
RoleFlagMessageViewCount = 0x08,
|
RoleFlagMessageViewCount = 0x08,
|
||||||
RoleFlagMessageReactions = 0x16,
|
RoleFlagMessageReactions = 0x16
|
||||||
RoleFlagMessageAlbumEntryFilter = 0x32,
|
|
||||||
RoleFlagMessageAlbumMessageIds = 0x64
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MessageData(const QVariantMap &data, qlonglong msgid);
|
MessageData(const QVariantMap &data, qlonglong msgid);
|
||||||
|
@ -92,16 +86,12 @@ public:
|
||||||
uint updateViewCount(const QVariantMap &interactionInfo);
|
uint updateViewCount(const QVariantMap &interactionInfo);
|
||||||
uint updateInteractionInfo(const QVariantMap &interactionInfo);
|
uint updateInteractionInfo(const QVariantMap &interactionInfo);
|
||||||
uint updateReactions(const QVariantMap &interactionInfo);
|
uint updateReactions(const QVariantMap &interactionInfo);
|
||||||
uint updateAlbumEntryFilter(const bool isAlbumChild);
|
|
||||||
uint updateAlbumEntryMessageIds(const QVariantList &newAlbumMessageIds);
|
|
||||||
|
|
||||||
QVector<int> diff(const MessageData *message) const;
|
QVector<int> diff(const MessageData *message) const;
|
||||||
QVector<int> setMessageData(const QVariantMap &data);
|
QVector<int> setMessageData(const QVariantMap &data);
|
||||||
QVector<int> setContent(const QVariantMap &content);
|
QVector<int> setContent(const QVariantMap &content);
|
||||||
QVector<int> setReplyMarkup(const QVariantMap &replyMarkup);
|
QVector<int> setReplyMarkup(const QVariantMap &replyMarkup);
|
||||||
QVector<int> setInteractionInfo(const QVariantMap &interactionInfo);
|
QVector<int> setInteractionInfo(const QVariantMap &interactionInfo);
|
||||||
QVector<int> setAlbumEntryFilter(bool isAlbumChild);
|
|
||||||
QVector<int> setAlbumEntryMessageIds(const QVariantList &newAlbumMessageIds);
|
|
||||||
|
|
||||||
int senderUserId() const;
|
int senderUserId() const;
|
||||||
qlonglong senderChatId() const;
|
qlonglong senderChatId() const;
|
||||||
|
@ -114,8 +104,6 @@ public:
|
||||||
QString messageContentType;
|
QString messageContentType;
|
||||||
int viewCount;
|
int viewCount;
|
||||||
QVariantList reactions;
|
QVariantList reactions;
|
||||||
bool albumEntryFilter;
|
|
||||||
QVariantList albumMessageIds;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ChatModel::MessageData::MessageData(const QVariantMap &data, qlonglong msgid) :
|
ChatModel::MessageData::MessageData(const QVariantMap &data, qlonglong msgid) :
|
||||||
|
@ -124,9 +112,7 @@ ChatModel::MessageData::MessageData(const QVariantMap &data, qlonglong msgid) :
|
||||||
messageType(data.value(_TYPE).toString()),
|
messageType(data.value(_TYPE).toString()),
|
||||||
messageContentType(data.value(CONTENT).toMap().value(_TYPE).toString()),
|
messageContentType(data.value(CONTENT).toMap().value(_TYPE).toString()),
|
||||||
viewCount(data.value(INTERACTION_INFO).toMap().value(VIEW_COUNT).toInt()),
|
viewCount(data.value(INTERACTION_INFO).toMap().value(VIEW_COUNT).toInt()),
|
||||||
reactions(data.value(INTERACTION_INFO).toMap().value(REACTIONS).toList()),
|
reactions(data.value(INTERACTION_INFO).toMap().value(REACTIONS).toList())
|
||||||
albumEntryFilter(false),
|
|
||||||
albumMessageIds(QVariantList())
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,12 +134,6 @@ QVector<int> ChatModel::MessageData::flagsToRoles(uint flags)
|
||||||
if (flags & RoleFlagMessageReactions) {
|
if (flags & RoleFlagMessageReactions) {
|
||||||
roles.append(RoleMessageReactions);
|
roles.append(RoleMessageReactions);
|
||||||
}
|
}
|
||||||
if (flags & RoleFlagMessageAlbumEntryFilter) {
|
|
||||||
roles.append(RoleMessageAlbumEntryFilter);
|
|
||||||
}
|
|
||||||
if (flags & RoleFlagMessageAlbumMessageIds) {
|
|
||||||
roles.append(RoleMessageAlbumMessageIds);
|
|
||||||
}
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,12 +169,6 @@ QVector<int> ChatModel::MessageData::diff(const MessageData *message) const
|
||||||
if (message->reactions != reactions) {
|
if (message->reactions != reactions) {
|
||||||
roles.append(RoleMessageReactions);
|
roles.append(RoleMessageReactions);
|
||||||
}
|
}
|
||||||
if (message->albumEntryFilter != albumEntryFilter) {
|
|
||||||
roles.append(RoleMessageAlbumEntryFilter);
|
|
||||||
}
|
|
||||||
if (message->albumMessageIds != albumMessageIds) {
|
|
||||||
roles.append(RoleMessageAlbumMessageIds);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
@ -263,37 +237,6 @@ uint ChatModel::MessageData::updateReactions(const QVariantMap &interactionInfo)
|
||||||
return (reactions == oldReactions) ? 0 : RoleFlagMessageReactions;
|
return (reactions == oldReactions) ? 0 : RoleFlagMessageReactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint ChatModel::MessageData::updateAlbumEntryFilter(const bool isAlbumChild)
|
|
||||||
{
|
|
||||||
LOG("Updating album filter... for id " << messageId << " value:" << isAlbumChild << "previously" << albumEntryFilter);
|
|
||||||
const bool oldAlbumFiltered = albumEntryFilter;
|
|
||||||
albumEntryFilter = isAlbumChild;
|
|
||||||
return (isAlbumChild == oldAlbumFiltered) ? 0 : RoleFlagMessageAlbumEntryFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QVector<int> ChatModel::MessageData::setAlbumEntryFilter(bool isAlbumChild)
|
|
||||||
{
|
|
||||||
LOG("setAlbumEntryFilter");
|
|
||||||
return flagsToRoles(updateAlbumEntryFilter(isAlbumChild));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint ChatModel::MessageData::updateAlbumEntryMessageIds(const QVariantList &newAlbumMessageIds)
|
|
||||||
{
|
|
||||||
LOG("Updating albumMessageIds... id" << messageId);
|
|
||||||
LOG(" Updating albumMessageIds..." << newAlbumMessageIds << "previously" << albumMessageIds << "same?" << (newAlbumMessageIds == albumMessageIds));
|
|
||||||
const QVariantList oldAlbumMessageIds = albumMessageIds;
|
|
||||||
albumMessageIds = newAlbumMessageIds;
|
|
||||||
|
|
||||||
LOG(" Updating albumMessageIds... same again?" << (newAlbumMessageIds == oldAlbumMessageIds));
|
|
||||||
return (newAlbumMessageIds == oldAlbumMessageIds) ? 0 : RoleFlagMessageAlbumMessageIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<int> ChatModel::MessageData::setAlbumEntryMessageIds(const QVariantList &newAlbumMessageIds)
|
|
||||||
{
|
|
||||||
return flagsToRoles(updateAlbumEntryMessageIds(newAlbumMessageIds));
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<int> ChatModel::MessageData::setInteractionInfo(const QVariantMap &info)
|
QVector<int> ChatModel::MessageData::setInteractionInfo(const QVariantMap &info)
|
||||||
{
|
{
|
||||||
return flagsToRoles(updateInteractionInfo(info));
|
return flagsToRoles(updateInteractionInfo(info));
|
||||||
|
@ -352,8 +295,6 @@ QHash<int,QByteArray> ChatModel::roleNames() const
|
||||||
roles.insert(MessageData::RoleMessageContentType, "content_type");
|
roles.insert(MessageData::RoleMessageContentType, "content_type");
|
||||||
roles.insert(MessageData::RoleMessageViewCount, "view_count");
|
roles.insert(MessageData::RoleMessageViewCount, "view_count");
|
||||||
roles.insert(MessageData::RoleMessageReactions, "reactions");
|
roles.insert(MessageData::RoleMessageReactions, "reactions");
|
||||||
roles.insert(MessageData::RoleMessageAlbumEntryFilter, "album_entry_filter");
|
|
||||||
roles.insert(MessageData::RoleMessageAlbumMessageIds, "album_message_ids");
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,8 +314,6 @@ QVariant ChatModel::data(const QModelIndex &index, int role) const
|
||||||
case MessageData::RoleMessageContentType: return message->messageContentType;
|
case MessageData::RoleMessageContentType: return message->messageContentType;
|
||||||
case MessageData::RoleMessageViewCount: return message->viewCount;
|
case MessageData::RoleMessageViewCount: return message->viewCount;
|
||||||
case MessageData::RoleMessageReactions: return message->reactions;
|
case MessageData::RoleMessageReactions: return message->reactions;
|
||||||
case MessageData::RoleMessageAlbumEntryFilter: return message->albumEntryFilter;
|
|
||||||
case MessageData::RoleMessageAlbumMessageIds: return message->albumMessageIds;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
@ -392,7 +331,6 @@ void ChatModel::clear(bool contentOnly)
|
||||||
qDeleteAll(messages);
|
qDeleteAll(messages);
|
||||||
messages.clear();
|
messages.clear();
|
||||||
messageIndexMap.clear();
|
messageIndexMap.clear();
|
||||||
albumMessageMap.clear();
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,7 +356,6 @@ void ChatModel::initialize(const QVariantMap &chatInformation)
|
||||||
this->chatId = chatId;
|
this->chatId = chatId;
|
||||||
this->messages.clear();
|
this->messages.clear();
|
||||||
this->messageIndexMap.clear();
|
this->messageIndexMap.clear();
|
||||||
this->albumMessageMap.clear();
|
|
||||||
this->searchQuery.clear();
|
this->searchQuery.clear();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
emit chatIdChanged();
|
emit chatIdChanged();
|
||||||
|
@ -483,36 +420,6 @@ int ChatModel::getMessageIndex(qlonglong messageId)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantList ChatModel::getMessageIdsForAlbum(qlonglong albumId)
|
|
||||||
{
|
|
||||||
QVariantList foundMessages;
|
|
||||||
if(albumMessageMap.contains(albumId)) { // there should be only one in here
|
|
||||||
QHash< qlonglong, QVariantList >::iterator i = albumMessageMap.find(albumId);
|
|
||||||
return i.value();
|
|
||||||
}
|
|
||||||
return foundMessages;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantList ChatModel::getMessagesForAlbum(qlonglong albumId, int startAt)
|
|
||||||
{
|
|
||||||
LOG("getMessagesForAlbumId" << albumId);
|
|
||||||
QVariantList messageIds = getMessageIdsForAlbum(albumId);
|
|
||||||
int count = messageIds.size();
|
|
||||||
if ( count == 0) {
|
|
||||||
return messageIds;
|
|
||||||
}
|
|
||||||
QVariantList foundMessages;
|
|
||||||
for (int messageNum = startAt; messageNum < count; ++messageNum) {
|
|
||||||
const int position = messageIndexMap.value(messageIds.at(messageNum).toLongLong(), -1);
|
|
||||||
if(position >= 0 && position < messages.size()) {
|
|
||||||
foundMessages.append(messages.at(position)->messageData);
|
|
||||||
} else {
|
|
||||||
LOG("Not found in messages: #"<< messageNum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return foundMessages;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ChatModel::getLastReadMessageIndex()
|
int ChatModel::getLastReadMessageIndex()
|
||||||
{
|
{
|
||||||
LOG("Obtaining last read message index");
|
LOG("Obtaining last read message index");
|
||||||
|
@ -570,8 +477,7 @@ void ChatModel::handleMessagesReceived(const QVariantList &messages, int totalCo
|
||||||
const qlonglong messageId = messageData.value(ID).toLongLong();
|
const qlonglong messageId = messageData.value(ID).toLongLong();
|
||||||
if (messageId && messageData.value(CHAT_ID).toLongLong() == chatId && !messageIndexMap.contains(messageId)) {
|
if (messageId && messageData.value(CHAT_ID).toLongLong() == chatId && !messageIndexMap.contains(messageId)) {
|
||||||
LOG("New message will be added:" << messageId);
|
LOG("New message will be added:" << messageId);
|
||||||
MessageData* message = new MessageData(messageData, messageId);
|
messagesToBeAdded.append(new MessageData(messageData, messageId));
|
||||||
messagesToBeAdded.append(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -579,7 +485,6 @@ void ChatModel::handleMessagesReceived(const QVariantList &messages, int totalCo
|
||||||
|
|
||||||
if (!messagesToBeAdded.isEmpty()) {
|
if (!messagesToBeAdded.isEmpty()) {
|
||||||
insertMessages(messagesToBeAdded);
|
insertMessages(messagesToBeAdded);
|
||||||
setMessagesAlbum(messagesToBeAdded);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// First call only returns a few messages, we need to get a little more than that...
|
// First call only returns a few messages, we need to get a little more than that...
|
||||||
|
@ -635,7 +540,6 @@ void ChatModel::handleNewMessageReceived(qlonglong chatId, const QVariantMap &me
|
||||||
QList<MessageData*> messagesToBeAdded;
|
QList<MessageData*> messagesToBeAdded;
|
||||||
messagesToBeAdded.append(new MessageData(message, messageId));
|
messagesToBeAdded.append(new MessageData(message, messageId));
|
||||||
insertMessages(messagesToBeAdded);
|
insertMessages(messagesToBeAdded);
|
||||||
setMessagesAlbum(messagesToBeAdded);
|
|
||||||
emit newMessageReceived(message);
|
emit newMessageReceived(message);
|
||||||
} else {
|
} else {
|
||||||
LOG("New message in this chat, but not relevant as less recent messages need to be loaded first!");
|
LOG("New message in this chat, but not relevant as less recent messages need to be loaded first!");
|
||||||
|
@ -687,7 +591,6 @@ void ChatModel::handleMessageSendSucceeded(qlonglong messageId, qlonglong oldMes
|
||||||
messages.replace(pos, newMessage);
|
messages.replace(pos, newMessage);
|
||||||
messageIndexMap.remove(oldMessageId);
|
messageIndexMap.remove(oldMessageId);
|
||||||
messageIndexMap.insert(messageId, pos);
|
messageIndexMap.insert(messageId, pos);
|
||||||
// TODO when we support sending album messages, handle ID change in albumMessageMap
|
|
||||||
const QVector<int> changedRoles(newMessage->diff(oldMessage));
|
const QVector<int> changedRoles(newMessage->diff(oldMessage));
|
||||||
delete oldMessage;
|
delete oldMessage;
|
||||||
LOG("Message was replaced at index" << pos);
|
LOG("Message was replaced at index" << pos);
|
||||||
|
@ -732,8 +635,7 @@ void ChatModel::handleMessageContentUpdated(qlonglong chatId, qlonglong messageI
|
||||||
LOG("We know the message that was updated" << messageId);
|
LOG("We know the message that was updated" << messageId);
|
||||||
const int pos = messageIndexMap.value(messageId, -1);
|
const int pos = messageIndexMap.value(messageId, -1);
|
||||||
if (pos >= 0) {
|
if (pos >= 0) {
|
||||||
MessageData* messageData = messages.at(pos);
|
const QVector<int> changedRoles(messages.at(pos)->setContent(newContent));
|
||||||
const QVector<int> changedRoles(messageData->setContent(newContent));
|
|
||||||
LOG("Message was updated at index" << pos);
|
LOG("Message was updated at index" << pos);
|
||||||
const QModelIndex messageIndex(index(pos));
|
const QModelIndex messageIndex(index(pos));
|
||||||
emit dataChanged(messageIndex, messageIndex, changedRoles);
|
emit dataChanged(messageIndex, messageIndex, changedRoles);
|
||||||
|
@ -762,8 +664,7 @@ void ChatModel::handleMessageEditedUpdated(qlonglong chatId, qlonglong messageId
|
||||||
LOG("We know the message that was updated" << messageId);
|
LOG("We know the message that was updated" << messageId);
|
||||||
const int pos = messageIndexMap.value(messageId, -1);
|
const int pos = messageIndexMap.value(messageId, -1);
|
||||||
if (pos >= 0) {
|
if (pos >= 0) {
|
||||||
MessageData* messageData = messages.at(pos);
|
const QVector<int> changedRoles(messages.at(pos)->setReplyMarkup(replyMarkup));
|
||||||
const QVector<int> changedRoles(messageData->setReplyMarkup(replyMarkup));
|
|
||||||
LOG("Message was edited at index" << pos);
|
LOG("Message was edited at index" << pos);
|
||||||
const QModelIndex messageIndex(index(pos));
|
const QModelIndex messageIndex(index(pos));
|
||||||
emit dataChanged(messageIndex, messageIndex, changedRoles);
|
emit dataChanged(messageIndex, messageIndex, changedRoles);
|
||||||
|
@ -808,31 +709,18 @@ void ChatModel::handleMessagesDeleted(qlonglong chatId, const QList<qlonglong> &
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ChatModel::removeRange(int firstDeleted, int lastDeleted)
|
void ChatModel::removeRange(int firstDeleted, int lastDeleted)
|
||||||
{
|
{
|
||||||
if (firstDeleted >= 0 && firstDeleted <= lastDeleted) {
|
if (firstDeleted >= 0 && firstDeleted <= lastDeleted) {
|
||||||
LOG("Removing range" << firstDeleted << "..." << lastDeleted << "| current messages size" << messages.size());
|
LOG("Removing range" << firstDeleted << "..." << lastDeleted << "| current messages size" << messages.size());
|
||||||
beginRemoveRows(QModelIndex(), firstDeleted, lastDeleted);
|
beginRemoveRows(QModelIndex(), firstDeleted, lastDeleted);
|
||||||
QList<qlonglong> rescanAlbumIds;
|
|
||||||
for (int i = firstDeleted; i <= lastDeleted; i++) {
|
for (int i = firstDeleted; i <= lastDeleted; i++) {
|
||||||
MessageData *message = messages.at(i);
|
MessageData *message = messages.at(i);
|
||||||
messageIndexMap.remove(message->messageId);
|
messageIndexMap.remove(message->messageId);
|
||||||
|
|
||||||
qlonglong albumId = message->messageData.value(MEDIA_ALBUM_ID).toLongLong();
|
|
||||||
if(albumId != 0 && albumMessageMap.contains(albumId)) {
|
|
||||||
rescanAlbumIds.append(albumId);
|
|
||||||
}
|
|
||||||
delete message;
|
delete message;
|
||||||
}
|
}
|
||||||
messages.erase(messages.begin() + firstDeleted, messages.begin() + (lastDeleted + 1));
|
messages.erase(messages.begin() + firstDeleted, messages.begin() + (lastDeleted + 1));
|
||||||
// rebuild following messageIndexMap
|
|
||||||
for(int i = firstDeleted; i < messages.size(); ++i) {
|
|
||||||
messageIndexMap.insert(messages.at(i)->messageId, i);
|
|
||||||
}
|
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
|
|
||||||
updateAlbumMessages(rescanAlbumIds, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -869,7 +757,7 @@ void ChatModel::appendMessages(const QList<MessageData*> newMessages)
|
||||||
beginInsertRows(QModelIndex(), oldSize, oldSize + count - 1);
|
beginInsertRows(QModelIndex(), oldSize, oldSize + count - 1);
|
||||||
messages.append(newMessages);
|
messages.append(newMessages);
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
// Append new indices to the map
|
// Appens new indeces to the map
|
||||||
messageIndexMap.insert(newMessages.at(i)->messageId, oldSize + i);
|
messageIndexMap.insert(newMessages.at(i)->messageId, oldSize + i);
|
||||||
}
|
}
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
|
@ -897,90 +785,6 @@ void ChatModel::prependMessages(const QList<MessageData*> newMessages)
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatModel::updateAlbumMessages(qlonglong albumId, bool checkDeleted)
|
|
||||||
{
|
|
||||||
if(albumMessageMap.contains(albumId)) {
|
|
||||||
const QVariantList empty;
|
|
||||||
QHash< qlonglong, QVariantList >::iterator album = albumMessageMap.find(albumId);
|
|
||||||
QVariantList messageIds = album.value();
|
|
||||||
std::sort(messageIds.begin(), messageIds.end());
|
|
||||||
int count;
|
|
||||||
// first: clear deleted messageIds:
|
|
||||||
if(checkDeleted) {
|
|
||||||
QVariantList::iterator it = messageIds.begin();
|
|
||||||
while (it != messageIds.end()) {
|
|
||||||
if (!messageIndexMap.contains(it->toLongLong())) {
|
|
||||||
it = messageIds.erase(it);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// second: remaining ones still exist
|
|
||||||
count = messageIds.size();
|
|
||||||
if(count == 0) {
|
|
||||||
albumMessageMap.remove(albumId);
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
const int position = messageIndexMap.value(messageIds.at(i).toLongLong(), -1);
|
|
||||||
if(position > -1) {
|
|
||||||
// set list for first entry, empty for all others
|
|
||||||
QVector<int> changedRolesFilter;
|
|
||||||
QVector<int> changedRolesIds;
|
|
||||||
|
|
||||||
QModelIndex messageIndex(index(position));
|
|
||||||
if(i == 0) {
|
|
||||||
changedRolesFilter = messages.at(position)->setAlbumEntryFilter(false);
|
|
||||||
changedRolesIds = messages.at(position)->setAlbumEntryMessageIds(messageIds);
|
|
||||||
} else {
|
|
||||||
changedRolesFilter = messages.at(position)->setAlbumEntryFilter(true);
|
|
||||||
changedRolesIds = messages.at(position)->setAlbumEntryMessageIds(empty);
|
|
||||||
}
|
|
||||||
emit dataChanged(messageIndex, messageIndex, changedRolesIds);
|
|
||||||
emit dataChanged(messageIndex, messageIndex, changedRolesFilter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
albumMessageMap.insert(albumId, messageIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatModel::updateAlbumMessages(QList<qlonglong> albumIds, bool checkDeleted)
|
|
||||||
{
|
|
||||||
const int albumsCount = albumIds.size();
|
|
||||||
for (int i = 0; i < albumsCount; i++) {
|
|
||||||
updateAlbumMessages(albumIds.at(i), checkDeleted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatModel::setMessagesAlbum(const QList<MessageData *> newMessages)
|
|
||||||
{
|
|
||||||
const int count = newMessages.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
setMessagesAlbum(newMessages.at(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatModel::setMessagesAlbum(MessageData *message)
|
|
||||||
{
|
|
||||||
qlonglong albumId = message->messageData.value(MEDIA_ALBUM_ID).toLongLong();
|
|
||||||
if (albumId > 0 && (message->messageContentType != "messagePhoto" || message->messageContentType != "messageVideo")) {
|
|
||||||
qlonglong messageId = message->messageId;
|
|
||||||
|
|
||||||
if(albumMessageMap.contains(albumId)) {
|
|
||||||
// find message id within album:
|
|
||||||
QHash< qlonglong, QVariantList >::iterator i = albumMessageMap.find(albumId);
|
|
||||||
if(!i.value().contains(messageId)) {
|
|
||||||
i.value().append(messageId);
|
|
||||||
}
|
|
||||||
} else { // new album id
|
|
||||||
albumMessageMap.insert(albumId, QVariantList() << messageId);
|
|
||||||
}
|
|
||||||
updateAlbumMessages(albumId, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantMap ChatModel::enhanceMessage(const QVariantMap &message)
|
QVariantMap ChatModel::enhanceMessage(const QVariantMap &message)
|
||||||
{
|
{
|
||||||
QVariantMap enhancedMessage = message;
|
QVariantMap enhancedMessage = message;
|
||||||
|
|
|
@ -44,8 +44,6 @@ public:
|
||||||
Q_INVOKABLE void triggerLoadMoreFuture();
|
Q_INVOKABLE void triggerLoadMoreFuture();
|
||||||
Q_INVOKABLE QVariantMap getChatInformation();
|
Q_INVOKABLE QVariantMap getChatInformation();
|
||||||
Q_INVOKABLE QVariantMap getMessage(int index);
|
Q_INVOKABLE QVariantMap getMessage(int index);
|
||||||
Q_INVOKABLE QVariantList getMessageIdsForAlbum(qlonglong albumId);
|
|
||||||
Q_INVOKABLE QVariantList getMessagesForAlbum(qlonglong albumId, int startAt);
|
|
||||||
Q_INVOKABLE int getLastReadMessageIndex();
|
Q_INVOKABLE int getLastReadMessageIndex();
|
||||||
Q_INVOKABLE void setSearchQuery(const QString newSearchQuery);
|
Q_INVOKABLE void setSearchQuery(const QString newSearchQuery);
|
||||||
|
|
||||||
|
@ -87,10 +85,6 @@ private:
|
||||||
void insertMessages(const QList<MessageData*> newMessages);
|
void insertMessages(const QList<MessageData*> newMessages);
|
||||||
void appendMessages(const QList<MessageData*> newMessages);
|
void appendMessages(const QList<MessageData*> newMessages);
|
||||||
void prependMessages(const QList<MessageData*> newMessages);
|
void prependMessages(const QList<MessageData*> newMessages);
|
||||||
void updateAlbumMessages(qlonglong albumId, bool checkDeleted);
|
|
||||||
void updateAlbumMessages(QList<qlonglong> albumIds, bool checkDeleted);
|
|
||||||
void setMessagesAlbum(const QList<MessageData*> newMessages);
|
|
||||||
void setMessagesAlbum(MessageData *message);
|
|
||||||
QVariantMap enhanceMessage(const QVariantMap &message);
|
QVariantMap enhanceMessage(const QVariantMap &message);
|
||||||
int calculateLastKnownMessageId();
|
int calculateLastKnownMessageId();
|
||||||
int calculateLastReadSentMessageId();
|
int calculateLastReadSentMessageId();
|
||||||
|
@ -101,7 +95,6 @@ private:
|
||||||
TDLibWrapper *tdLibWrapper;
|
TDLibWrapper *tdLibWrapper;
|
||||||
QList<MessageData*> messages;
|
QList<MessageData*> messages;
|
||||||
QHash<qlonglong,int> messageIndexMap;
|
QHash<qlonglong,int> messageIndexMap;
|
||||||
QHash<qlonglong, QVariantList> albumMessageMap;
|
|
||||||
QVariantMap chatInformation;
|
QVariantMap chatInformation;
|
||||||
qlonglong chatId;
|
qlonglong chatId;
|
||||||
bool inReload;
|
bool inReload;
|
||||||
|
|
|
@ -51,7 +51,6 @@
|
||||||
#include "processlauncher.h"
|
#include "processlauncher.h"
|
||||||
#include "stickermanager.h"
|
#include "stickermanager.h"
|
||||||
#include "textfiltermodel.h"
|
#include "textfiltermodel.h"
|
||||||
#include "boolfiltermodel.h"
|
|
||||||
#include "tgsplugin.h"
|
#include "tgsplugin.h"
|
||||||
#include "fernschreiberutils.h"
|
#include "fernschreiberutils.h"
|
||||||
#include "knownusersmodel.h"
|
#include "knownusersmodel.h"
|
||||||
|
@ -131,7 +130,6 @@ int main(int argc, char *argv[])
|
||||||
qmlRegisterType<TDLibFile>(uri, 1, 0, "TDLibFile");
|
qmlRegisterType<TDLibFile>(uri, 1, 0, "TDLibFile");
|
||||||
qmlRegisterType<NamedAction>(uri, 1, 0, "NamedAction");
|
qmlRegisterType<NamedAction>(uri, 1, 0, "NamedAction");
|
||||||
qmlRegisterType<TextFilterModel>(uri, 1, 0, "TextFilterModel");
|
qmlRegisterType<TextFilterModel>(uri, 1, 0, "TextFilterModel");
|
||||||
qmlRegisterType<BoolFilterModel>(uri, 1, 0, "BoolFilterModel");
|
|
||||||
qmlRegisterType<ChatPermissionFilterModel>(uri, 1, 0, "ChatPermissionFilterModel");
|
qmlRegisterType<ChatPermissionFilterModel>(uri, 1, 0, "ChatPermissionFilterModel");
|
||||||
qmlRegisterSingletonType<DebugLogJS>(uri, 1, 0, "DebugLog", DebugLogJS::createSingleton);
|
qmlRegisterSingletonType<DebugLogJS>(uri, 1, 0, "DebugLog", DebugLogJS::createSingleton);
|
||||||
|
|
||||||
|
|
|
@ -405,7 +405,11 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>Esta conversación secreta no está lista. El contacto no está conectado.</translation>
|
||||||
|
=======
|
||||||
<translation>Esta charla secreta no está lista. El contacto no está conectado.</translation>
|
<translation>Esta charla secreta no está lista. El contacto no está conectado.</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Closing chat</source>
|
<source>Closing chat</source>
|
||||||
|
@ -496,9 +500,20 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>No hay conversaciones.</translation>
|
||||||
|
=======
|
||||||
<translation>No hay charlas.</translation>
|
<translation>No hay charlas.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ContactSync</name>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation>No se puede sincronizar los contactos con Telegrama.</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>CoverPage</name>
|
<name>CoverPage</name>
|
||||||
<message>
|
<message>
|
||||||
|
@ -662,7 +677,11 @@
|
||||||
<message>
|
<message>
|
||||||
<source>have registered with Telegram</source>
|
<source>have registered with Telegram</source>
|
||||||
<comment>myself</comment>
|
<comment>myself</comment>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>registrado a Telegram</translation>
|
||||||
|
=======
|
||||||
<translation>registrado a Telegrama</translation>
|
<translation>registrado a Telegrama</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>has registered with Telegram</source>
|
<source>has registered with Telegram</source>
|
||||||
|
@ -757,7 +776,11 @@
|
||||||
<message>
|
<message>
|
||||||
<source>deleted the chat photo</source>
|
<source>deleted the chat photo</source>
|
||||||
<comment>myself</comment>
|
<comment>myself</comment>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>borró foto de conversación</translation>
|
||||||
|
=======
|
||||||
<translation>borró foto de charla</translation>
|
<translation>borró foto de charla</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>deleted the chat photo</source>
|
<source>deleted the chat photo</source>
|
||||||
|
@ -766,11 +789,19 @@
|
||||||
<message>
|
<message>
|
||||||
<source>changed the secret chat TTL setting</source>
|
<source>changed the secret chat TTL setting</source>
|
||||||
<comment>myself</comment>
|
<comment>myself</comment>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>cambió ajustes TTL en conversación secreta</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>changed the secret chat TTL setting</source>
|
||||||
|
<translation>cambió ajustes TTL en conversación secreta</translation>
|
||||||
|
=======
|
||||||
<translation>cambió ajustes TTL en charla secreta</translation>
|
<translation>cambió ajustes TTL en charla secreta</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>changed the secret chat TTL setting</source>
|
<source>changed the secret chat TTL setting</source>
|
||||||
<translation>cambió ajustes TTL en charla secreta</translation>
|
<translation>cambió ajustes TTL en charla secreta</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>upgraded this group to a supergroup</source>
|
<source>upgraded this group to a supergroup</source>
|
||||||
|
@ -789,11 +820,19 @@
|
||||||
<message>
|
<message>
|
||||||
<source>created a screenshot in this chat</source>
|
<source>created a screenshot in this chat</source>
|
||||||
<comment>myself</comment>
|
<comment>myself</comment>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>creó pantallazo a esta conversación</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>created a screenshot in this chat</source>
|
||||||
|
<translation>creó pantallazo a esta conversación</translation>
|
||||||
|
=======
|
||||||
<translation>creó pantallazo a esta charla</translation>
|
<translation>creó pantallazo a esta charla</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>created a screenshot in this chat</source>
|
<source>created a screenshot in this chat</source>
|
||||||
<translation>creó pantallazo a esta charla</translation>
|
<translation>creó pantallazo a esta charla</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>sent an unsupported message</source>
|
<source>sent an unsupported message</source>
|
||||||
|
@ -846,21 +885,37 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>has added %1 to the chat</source>
|
<source>has added %1 to the chat</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>ha añadido %1 a conversación</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>has removed %1 from the chat</source>
|
||||||
|
<translation>ha quitado %1 de conversación</translation>
|
||||||
|
=======
|
||||||
<translation>ha añadido %1 a charla</translation>
|
<translation>ha añadido %1 a charla</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>has removed %1 from the chat</source>
|
<source>has removed %1 from the chat</source>
|
||||||
<translation>ha quitado %1 de charla</translation>
|
<translation>ha quitado %1 de charla</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>have added %1 to the chat</source>
|
<source>have added %1 to the chat</source>
|
||||||
<comment>myself</comment>
|
<comment>myself</comment>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>ha añadido %1 a conversación</translation>
|
||||||
|
=======
|
||||||
<translation>ha añadido %1 a charla</translation>
|
<translation>ha añadido %1 a charla</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>have removed %1 from the chat</source>
|
<source>have removed %1 from the chat</source>
|
||||||
<comment>myself</comment>
|
<comment>myself</comment>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>ha quitado %1 de conversación</translation>
|
||||||
|
=======
|
||||||
<translation>ha quitado %1 de charla</translation>
|
<translation>ha quitado %1 de charla</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source>scored %Ln points</source>
|
<source>scored %Ln points</source>
|
||||||
|
@ -1204,7 +1259,11 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Loading chat list...</source>
|
<source>Loading chat list...</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>cargando lista de conversación...</translation>
|
||||||
|
=======
|
||||||
<translation>cargando lista de charla...</translation>
|
<translation>cargando lista de charla...</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Settings</source>
|
<source>Settings</source>
|
||||||
|
@ -1212,7 +1271,11 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>No hay conversaciones.</translation>
|
||||||
|
=======
|
||||||
<translation>No hay charlas.</translation>
|
<translation>No hay charlas.</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>New Chat</source>
|
<source>New Chat</source>
|
||||||
|
@ -1517,11 +1580,19 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Focus text input on chat open</source>
|
<source>Focus text input on chat open</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>Enfocar entrada de texto a conversación</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Focus the text input area when entering a chat</source>
|
||||||
|
<translation>Enfoca área de entrada de texto al ingresar a conversación</translation>
|
||||||
|
=======
|
||||||
<translation>Enfocar entrada de texto de Charla</translation>
|
<translation>Enfocar entrada de texto de Charla</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Focus the text input area when entering a chat</source>
|
<source>Focus the text input area when entering a chat</source>
|
||||||
<translation>Enfoca área de entrada de texto al ingresar a charla</translation>
|
<translation>Enfoca área de entrada de texto al ingresar a charla</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Focus text input area after send</source>
|
<source>Focus text input area after send</source>
|
||||||
|
@ -1537,7 +1608,11 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Fernschreiber will wait a bit before messages are marked as read</source>
|
<source>Fernschreiber will wait a bit before messages are marked as read</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>Si esta habilitado, apl espera un segundo hasta que mensaje que está en pantalla se marque como leído. Si deshabilita esta función, mensajes se marcarán inmediatamente como leído una vez que esté en pantalla sin desplazarse a mensaje</translation>
|
||||||
|
=======
|
||||||
<translation>Si esta habilitado, Apl espera un segundo hasta que mensaje que está en monitor se marque como leído. Si deshabilita esta función, mensajes se marcarán inmediatamente como leído una vez que esté en monitor sin desplazarse a mensaje</translation>
|
<translation>Si esta habilitado, Apl espera un segundo hasta que mensaje que está en monitor se marque como leído. Si deshabilita esta función, mensajes se marcarán inmediatamente como leído una vez que esté en monitor sin desplazarse a mensaje</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Open-with menu integration</source>
|
<source>Open-with menu integration</source>
|
||||||
|
@ -1573,6 +1648,17 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable notification sounds</source>
|
<source>Enable notification sounds</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>Habilitar sonidos notificación</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
|
||||||
|
<translation>Cuando sonidos están habilitados, Ferni utilizará sonido de notificación actual de Sailfish OS para los grupos, que se puede ajustar a configuración del sistema.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Always append message preview to notifications</source>
|
||||||
|
<translation>Agregar vista previa mensaje a notificaciones</translation>
|
||||||
|
=======
|
||||||
<translation>Habilitar sonidos de Notificación</translation>
|
<translation>Habilitar sonidos de Notificación</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
|
@ -1582,6 +1668,7 @@
|
||||||
<message>
|
<message>
|
||||||
<source>Always append message preview to notifications</source>
|
<source>Always append message preview to notifications</source>
|
||||||
<translation>Vista previa de mensaje en Notificaciones</translation>
|
<translation>Vista previa de mensaje en Notificaciones</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
|
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
|
||||||
|
@ -1589,6 +1676,17 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Highlight unread messages</source>
|
<source>Highlight unread messages</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>Resaltar mensajes no leídos</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Highlight Conversations with unread messages</source>
|
||||||
|
<translation>Resaltar conversaciones a mensajes no leídos</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Hide content in notifications</source>
|
||||||
|
<translation>Ocultar contenido a notificaciones</translation>
|
||||||
|
=======
|
||||||
<translation>Resaltar mensajes no Leídos</translation>
|
<translation>Resaltar mensajes no Leídos</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
|
@ -1598,6 +1696,7 @@
|
||||||
<message>
|
<message>
|
||||||
<source>Hide content in notifications</source>
|
<source>Hide content in notifications</source>
|
||||||
<translation>Ocultar contenido de notificaciones</translation>
|
<translation>Ocultar contenido de notificaciones</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Go to quoted message</source>
|
<source>Go to quoted message</source>
|
||||||
|
@ -1651,7 +1750,11 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Show link in forwarded messages</source>
|
<source>Show link in forwarded messages</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>Mostrar enlace a mensajes reenviados</translation>
|
||||||
|
=======
|
||||||
<translation>Mostrar enlace a mensajes Reenviados</translation>
|
<translation>Mostrar enlace a mensajes Reenviados</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Privacy setting for managing whether a link to your account is included in forwarded messages.</source>
|
<source>Privacy setting for managing whether a link to your account is included in forwarded messages.</source>
|
||||||
|
@ -1760,11 +1863,19 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable online-only mode</source>
|
<source>Enable online-only mode</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>Modo solo enlínea</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
|
||||||
|
<translation>Deshabilita almacenamiento en caché sin conexión. Algunas funciones pueden estar limitadas o ausentes en este modo. Se requiere reiniciar Ferni para efecto.</translation>
|
||||||
|
=======
|
||||||
<translation>Modo solo Enlínea</translation>
|
<translation>Modo solo Enlínea</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
|
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
|
||||||
<translation>Deshabilita el almacenamiento en caché sin conexión. Algunas funciones pueden estar limitadas o ausentes en este modo. Se requiere reiniciar Ferni para efecto.</translation>
|
<translation>Deshabilita el almacenamiento en caché sin conexión. Algunas funciones pueden estar limitadas o ausentes en este modo. Se requiere reiniciar Ferni para efecto.</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable storage optimizer</source>
|
<source>Enable storage optimizer</source>
|
||||||
|
@ -1838,7 +1949,11 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Synchronize Contacts with Telegram</source>
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>Sincronizar Telegram</translation>
|
||||||
|
=======
|
||||||
<translation>Sincronizar Telegrama</translation>
|
<translation>Sincronizar Telegrama</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -2219,12 +2334,20 @@
|
||||||
<message>
|
<message>
|
||||||
<source>changed the secret chat TTL setting</source>
|
<source>changed the secret chat TTL setting</source>
|
||||||
<comment>myself; TTL = Time To Live</comment>
|
<comment>myself; TTL = Time To Live</comment>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>cambió ajustes de TTL de conversación secreta</translation>
|
||||||
|
=======
|
||||||
<translation>cambió ajustes de TTL de charla secreta</translation>
|
<translation>cambió ajustes de TTL de charla secreta</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>changed the secret chat TTL setting</source>
|
<source>changed the secret chat TTL setting</source>
|
||||||
<comment>TTL = Time To Live</comment>
|
<comment>TTL = Time To Live</comment>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>cambió ajustes de TTL de conversación secreta</translation>
|
||||||
|
=======
|
||||||
<translation>cambió ajustes de TTL de charla secreta</translation>
|
<translation>cambió ajustes de TTL de charla secreta</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>upgraded this group to a supergroup</source>
|
<source>upgraded this group to a supergroup</source>
|
||||||
|
@ -2243,11 +2366,19 @@
|
||||||
<message>
|
<message>
|
||||||
<source>created a screenshot in this chat</source>
|
<source>created a screenshot in this chat</source>
|
||||||
<comment>myself</comment>
|
<comment>myself</comment>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation>creó pantallazo a conversación</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>created a screenshot in this chat</source>
|
||||||
|
<translation>creó pantallazo a conversación</translation>
|
||||||
|
=======
|
||||||
<translation>creó pantallazo de charla</translation>
|
<translation>creó pantallazo de charla</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>created a screenshot in this chat</source>
|
<source>created a screenshot in this chat</source>
|
||||||
<translation>creó pantallazo a charla</translation>
|
<translation>creó pantallazo a charla</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>sent an unsupported message</source>
|
<source>sent an unsupported message</source>
|
||||||
|
|
|
@ -495,7 +495,11 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Double-tap on a message to choose a reaction</source>
|
<source>Double-tap on a message to choose a reaction</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
=======
|
||||||
<translation>Dvojitým klepnutím na správu vybrať reakciu</translation>
|
<translation>Dvojitým klepnutím na správu vybrať reakciu</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -1634,6 +1638,34 @@
|
||||||
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
|
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
|
||||||
<translation>Citovanú správu otvoriť v čete namiesto v náhľade.</translation>
|
<translation>Citovanú správu otvoriť v čete namiesto v náhľade.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Always append message preview to notifications</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Highlight unread messages</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Highlight Conversations with unread messages</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Hide content in notifications</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Go to quoted message</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>SettingsPage</name>
|
<name>SettingsPage</name>
|
||||||
|
@ -1745,14 +1777,48 @@
|
||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source>%1 day(s)</source>
|
<source>%1 day(s)</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation type="unfinished">
|
||||||
|
<numerusform></numerusform>
|
||||||
|
<numerusform></numerusform>
|
||||||
|
<numerusform></numerusform>
|
||||||
|
=======
|
||||||
<translation>
|
<translation>
|
||||||
<numerusform>%1 deň</numerusform>
|
<numerusform>%1 deň</numerusform>
|
||||||
<numerusform>%1 dni</numerusform>
|
<numerusform>%1 dni</numerusform>
|
||||||
<numerusform>%1 dní</numerusform>
|
<numerusform>%1 dní</numerusform>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>1 week</source>
|
<source>1 week</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>1 month</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>3 months</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>6 months</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>1 year</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Session Timeout</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Inactive sessions will be terminated after this timeframe</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
=======
|
||||||
<translation>1 týždeň</translation>
|
<translation>1 týždeň</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
|
@ -1778,6 +1844,7 @@
|
||||||
<message>
|
<message>
|
||||||
<source>Inactive sessions will be terminated after this timeframe</source>
|
<source>Inactive sessions will be terminated after this timeframe</source>
|
||||||
<translation>Neaktívne relácie budú po tomto časovom rámci ukončené</translation>
|
<translation>Neaktívne relácie budú po tomto časovom rámci ukončené</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
|
|
@ -485,7 +485,11 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Double-tap on a message to choose a reaction</source>
|
<source>Double-tap on a message to choose a reaction</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
=======
|
||||||
<translation>Dubbeltryck på ett meddelande för att välja en reaktion</translation>
|
<translation>Dubbeltryck på ett meddelande för att välja en reaktion</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -1581,6 +1585,33 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Always append message preview to notifications</source>
|
<source>Always append message preview to notifications</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Highlight unread messages</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Highlight Conversations with unread messages</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Hide content in notifications</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Go to quoted message</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
=======
|
||||||
<translation>Visa alltid förhandsgranskning av meddelanden i aviseringar</translation>
|
<translation>Visa alltid förhandsgranskning av meddelanden i aviseringar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
|
@ -1606,6 +1637,7 @@
|
||||||
<message>
|
<message>
|
||||||
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
|
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
|
||||||
<translation>Vid tryck på ett citerat meddelande öppnas det i chatten istället för att visas i ett överlägg.</translation>
|
<translation>Vid tryck på ett citerat meddelande öppnas det i chatten istället för att visas i ett överlägg.</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -1718,13 +1750,46 @@
|
||||||
</message>
|
</message>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<source>%1 day(s)</source>
|
<source>%1 day(s)</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation type="unfinished">
|
||||||
|
<numerusform></numerusform>
|
||||||
|
<numerusform></numerusform>
|
||||||
|
=======
|
||||||
<translation>
|
<translation>
|
||||||
<numerusform>%1 dag</numerusform>
|
<numerusform>%1 dag</numerusform>
|
||||||
<numerusform>%1 dagar</numerusform>
|
<numerusform>%1 dagar</numerusform>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>1 week</source>
|
<source>1 week</source>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>1 month</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>3 months</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>6 months</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>1 year</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Session Timeout</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Inactive sessions will be terminated after this timeframe</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
=======
|
||||||
<translation>1 vecka</translation>
|
<translation>1 vecka</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
|
@ -1750,6 +1815,7 @@
|
||||||
<message>
|
<message>
|
||||||
<source>Inactive sessions will be terminated after this timeframe</source>
|
<source>Inactive sessions will be terminated after this timeframe</source>
|
||||||
<translation>Inaktiva sessioner avslutas efter den här tidsramen</translation>
|
<translation>Inaktiva sessioner avslutas efter den här tidsramen</translation>
|
||||||
|
>>>>>>> fix_ballons/master
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
|
Loading…
Reference in a new issue