diff --git a/harbour-fernschreiber.pro b/harbour-fernschreiber.pro index f72d480..31887de 100644 --- a/harbour-fernschreiber.pro +++ b/harbour-fernschreiber.pro @@ -46,6 +46,8 @@ DISTFILES += qml/harbour-fernschreiber.qml \ qml/components/LocationPreview.qml \ qml/components/MessageListViewItem.qml \ qml/components/MessageListViewItemSimple.qml \ + qml/components/MessageOverlayFlickable.qml \ + qml/components/PinnedMessageItem.qml \ qml/components/PollPreview.qml \ qml/components/StickerPicker.qml \ qml/components/PhotoTextsListItem.qml \ diff --git a/qml/components/AudioPreview.qml b/qml/components/AudioPreview.qml index f55f797..f64bea1 100644 --- a/qml/components/AudioPreview.qml +++ b/qml/components/AudioPreview.qml @@ -25,13 +25,14 @@ Item { id: audioMessageComponent property ListItem messageListItem - property var rawMessage: messageListItem.myMessage + property MessageOverlayFlickable overlayFlickable + property var rawMessage: messageListItem ? messageListItem.myMessage : overlayFlickable.overlayMessage property var audioData: ( rawMessage.content['@type'] === "messageVoiceNote" ) ? rawMessage.content.voice_note : ( ( rawMessage.content['@type'] === "messageAudio" ) ? rawMessage.content.audio : ""); property string audioUrl; property int previewFileId; property int audioFileId; - property bool onScreen: messageListItem.page.status === PageStatus.Active + property bool onScreen: messageListItem ? messageListItem.page.status === PageStatus.Active : true property string audioType : "voiceNote"; width: parent.width diff --git a/qml/components/DocumentPreview.qml b/qml/components/DocumentPreview.qml index ce6731b..a33b153 100644 --- a/qml/components/DocumentPreview.qml +++ b/qml/components/DocumentPreview.qml @@ -26,7 +26,8 @@ Item { height: Theme.itemSizeLarge property ListItem messageListItem - property var rawMessage: messageListItem.myMessage + property MessageOverlayFlickable overlayFlickable + property var rawMessage: messageListItem ? messageListItem.myMessage : overlayFlickable.overlayMessage property var documentData: rawMessage.content.document property bool openRequested: false; diff --git a/qml/components/ImagePreview.qml b/qml/components/ImagePreview.qml index 1daef76..b16a0d6 100644 --- a/qml/components/ImagePreview.qml +++ b/qml/components/ImagePreview.qml @@ -24,7 +24,8 @@ Item { id: imagePreviewItem property ListItem messageListItem - property var rawMessage: messageListItem.myMessage + property MessageOverlayFlickable overlayFlickable + property var rawMessage: messageListItem ? messageListItem.myMessage : overlayFlickable.overlayMessage property var photoData: rawMessage.content.photo; property var pictureFileInformation; diff --git a/qml/components/LocationPreview.qml b/qml/components/LocationPreview.qml index 6ad62f5..4eb234e 100644 --- a/qml/components/LocationPreview.qml +++ b/qml/components/LocationPreview.qml @@ -25,11 +25,12 @@ Item { id: imagePreviewItem property ListItem messageListItem - property var rawMessage: messageListItem.myMessage + property MessageOverlayFlickable overlayFlickable + property var rawMessage: messageListItem ? messageListItem.myMessage : overlayFlickable.overlayMessage property var locationData : ( rawMessage.content['@type'] === "messageLocation" ) ? rawMessage.content.location : ( ( rawMessage.content['@type'] === "messageVenue" ) ? rawMessage.content.venue.location : "" ) - property string chatId: messageListItem.page.chatInformation.id + property string chatId: rawMessage.chat_id property var pictureFileInformation; width: parent.width height: width / 2 diff --git a/qml/components/MessageListViewItem.qml b/qml/components/MessageListViewItem.qml index a1c117b..f30454b 100644 --- a/qml/components/MessageListViewItem.qml +++ b/qml/components/MessageListViewItem.qml @@ -101,6 +101,13 @@ ListItem { } text: qsTr("Select Message") } + MenuItem { + onClicked: { + tdLibWrapper.pinMessage(page.chatInformation.id, myMessage.id); + } + text: qsTr("Pin Message") + visible: canPinMessages() + } MenuItem { onClicked: { var chatId = page.chatInformation.id; @@ -277,11 +284,22 @@ ListItem { height: active ? precalculatedValues.messageInReplyToHeight : 0 property var inReplyToMessage; sourceComponent: Component { - InReplyToRow { - id: messageInReplyToRow - myUserId: page.myUserId - visible: true - inReplyToMessage: messageInReplyToLoader.inReplyToMessage + Item { + width: messageInReplyToRow.width + height: messageInReplyToRow.height + InReplyToRow { + id: messageInReplyToRow + myUserId: page.myUserId + visible: true + inReplyToMessage: messageInReplyToLoader.inReplyToMessage + } + MouseArea { + anchors.fill: parent + onClicked: { + messageOverlayLoader.overlayMessage = messageInReplyToRow.inReplyToMessage; + messageOverlayLoader.active = true; + } + } } } } @@ -357,8 +375,6 @@ ListItem { } } } - - } } } diff --git a/qml/components/MessageOverlayFlickable.qml b/qml/components/MessageOverlayFlickable.qml new file mode 100644 index 0000000..c70f8a1 --- /dev/null +++ b/qml/components/MessageOverlayFlickable.qml @@ -0,0 +1,219 @@ +/* + 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 . +*/ +import QtQuick 2.6 +import Sailfish.Silica 1.0 +import "../components" +import "../js/functions.js" as Functions +import "../js/twemoji.js" as Emoji + +Flickable { + id: messageOverlayFlickable + anchors.fill: parent + boundsBehavior: Flickable.StopAtBounds + contentHeight: messageContentColumn.height + clip: true + + property var overlayMessage; + property bool showHeader: true + readonly property var userInformation: tdLibWrapper.getUserInformation(overlayMessage.sender_user_id); + readonly property bool isOwnMessage: tdLibWrapper.getUserInformation().id === overlayMessage.sender_user_id; + readonly property string extraContentComponentName: (typeof overlayMessage.content !== "undefined" && typeof chatView.contentComponentNames[overlayMessage.content['@type']] !== "undefined" ) + ? chatView.contentComponentNames[overlayMessage.content['@type']] : "" + signal requestClose; + + function getOriginalAuthor(forwardInformation, fontSize) { + switch (forwardInformation.origin["@type"]) { + case "messageForwardOriginChannel": + var otherChatInformation = tdLibWrapper.getChat(forwardInformation.origin.chat_id); + return Emoji.emojify(otherChatInformation.title, fontSize); + case "messageForwardOriginUser": + var otherUserInformation = tdLibWrapper.getUserInformation(forwardInformation.origin.sender_user_id); + return Emoji.emojify(Functions.getUserName(otherUserInformation), fontSize); + default: + return Emoji.emojify(forwardInformation.origin.sender_name, fontSize); + } + } + + Component.onCompleted: { + delegateComponentLoadingTimer.start(); + } + + Timer { + id: delegateComponentLoadingTimer + interval: 500 + repeat: false + running: false + onTriggered: { + if (typeof overlayMessage.content !== "undefined") { + if (messageOverlayFlickable.extraContentComponentName !== "") { + overlayExtraContentLoader.setSource( + "../components/" + messageOverlayFlickable.extraContentComponentName + ".qml", + { + overlayFlickable: messageOverlayFlickable + }) + } else { + if (typeof overlayMessage.content.web_page !== "undefined") { + overlayWebPagePreviewLoader.active = true; + } + } + } + } + } + + Rectangle { + id: messageContentBackground + color: Theme.overlayBackgroundColor + opacity: 0.7 + width: parent.width + height: messageContentColumn.height >= messageOverlayFlickable.height ? messageContentColumn.height : messageOverlayFlickable.height + MouseArea { + anchors.fill: parent + onClicked: { + messageOverlayFlickable.requestClose(); + } + } + } + + Column { + id: messageContentColumn + spacing: Theme.paddingMedium + anchors.top: parent.top + anchors.topMargin: Theme.paddingMedium + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width - ( 2 * Theme.horizontalPageMargin ) + + Row { + visible: messageOverlayFlickable.showHeader + width: parent.width + spacing: Theme.paddingMedium + ProfileThumbnail { + id: overlayMessagePictureThumbnail + photoData: (typeof messageOverlayFlickable.userInformation.profile_photo !== "undefined") ? messageOverlayFlickable.userInformation.profile_photo.small : ({}) + replacementStringHint: overlayMessageUserText.text + width: Theme.itemSizeLarge + height: Theme.itemSizeLarge + } + Text { + id: overlayMessageUserText + + width: parent.width - overlayMessagePictureThumbnail.width + anchors.verticalCenter: parent.verticalCenter + text: messageOverlayFlickable.isOwnMessage ? qsTr("You") : Emoji.emojify(Functions.getUserName(messageOverlayFlickable.userInformation), font.pixelSize) + font.pixelSize: Theme.fontSizeExtraLarge + font.weight: Font.ExtraBold + color: Theme.primaryColor + maximumLineCount: 1 + elide: Text.ElideRight + textFormat: Text.StyledText + } + } + + Text { + id: overlayForwardedInfoText + width: parent.width + visible: typeof overlayMessage.forward_info !== "undefined" + font.pixelSize: Theme.fontSizeSmall + font.italic: true + textFormat: Text.StyledText + color: Theme.secondaryColor + wrapMode: Text.Wrap + text: visible ? qsTr("This message was forwarded. Original author: %1").arg(getOriginalAuthor(overlayMessage.forward_info, font.pixelSize)) : "" + } + + Text { + id: overlayMessageText + width: parent.width + text: Emoji.emojify(Functions.getMessageText(overlayMessage, false, messageOverlayFlickable.isOwnMessage), font.pixelSize) + font.pixelSize: Theme.fontSizeMedium + color: Theme.primaryColor + wrapMode: Text.Wrap + textFormat: Text.StyledText + onLinkActivated: { + Functions.handleLink(link); + } + linkColor: Theme.highlightColor + visible: (text !== "") + } + + Loader { + id: overlayWebPagePreviewLoader + active: false + asynchronous: true + width: parent.width + + sourceComponent: Component { + id: webPagePreviewComponent + WebPagePreview { + id: webPagePreview + + onImplicitHeightChanged: { + webPagePreviewLoader.height = webPagePreview.implicitHeight; + } + + webPageData: overlayMessage.content.web_page + largerFontSize: true + width: parent.width + } + } + } + + Loader { + id: overlayExtraContentLoader + width: parent.width + asynchronous: true + } + + Timer { + id: messageDateUpdater + interval: 60000 + running: true + repeat: true + onTriggered: { + overlayMessageDateText.text = ( overlayMessageDateText.useElapsed ? Functions.getDateTimeElapsed(overlayMessage.date) : Functions.getDateTimeTranslated(overlayMessage.date) ); + } + } + + Text { + width: parent.width + + property bool useElapsed: true + + id: overlayMessageDateText + font.pixelSize: Theme.fontSizeExtraSmall + color: Theme.secondaryColor + text: ( useElapsed ? Functions.getDateTimeElapsed(overlayMessage.date) : Functions.getDateTimeTranslated(overlayMessage.date) ) + MouseArea { + anchors.fill: parent + onClicked: { + overlayMessageDateText.useElapsed = !overlayMessageDateText.useElapsed; + overlayMessageDateText.text = ( useElapsed ? Functions.getDateTimeElapsed(overlayMessage.date) : Functions.getDateTimeTranslated(overlayMessage.date) ); + } + } + } + + Label { + id: separatorLabel + width: parent.width + font.pixelSize: Theme.fontSizeSmall + } + + } + + VerticalScrollDecorator {} +} diff --git a/qml/components/PinnedMessageItem.qml b/qml/components/PinnedMessageItem.qml new file mode 100644 index 0000000..9040a8a --- /dev/null +++ b/qml/components/PinnedMessageItem.qml @@ -0,0 +1,143 @@ +/* + 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 . +*/ +import QtQuick 2.6 +import Sailfish.Silica 1.0 +import "../components" +import "../js/functions.js" as Functions +import "../js/twemoji.js" as Emoji + +Item { + id: pinnedMessageItem + + property var pinnedMessage; + signal requestShowMessage; + signal requestCloseMessage; + + onPinnedMessageChanged: { + if (pinnedMessage) { + console.log("[ChatPage] Activating pinned message"); + var messageUserText = (pinnedMessage.sender_user_id !== chatPage.myUserId) ? Emoji.emojify(Functions.getUserName(tdLibWrapper.getUserInformation(pinnedMessage.sender_user_id)), pinnedMessageUserText.font.pixelSize) : qsTr("You"); + pinnedMessageUserText.text = (messageUserText === "" ? qsTr("Pinned Message") : messageUserText ); + pinnedMessageText.text = Emoji.emojify(Functions.getMessageText(pinnedMessage, true, pinnedMessage.sender_user_id === chatPage.myUserId), pinnedMessageText.font.pixelSize); + pinnedMessageItem.visible = true; + } else { + pinnedMessageItem.visible = false; + } + } + + visible: false + anchors.left: parent.left + anchors.right: parent.right + height: visible ? pinnedMessageRow.height : 0 + + Rectangle { + id: pinnedMessageBackground + anchors.fill: parent + opacity: 0.1 + color: Theme.secondaryColor + } + + Row { + id: pinnedMessageRow + + width: parent.width + + IconButton { + id: pinnedMessageButton + width: Theme.itemSizeLarge + height: Theme.itemSizeLarge + icon.source: "image://theme/icon-m-mark-unread" + onClicked: { + pinnedMessageItem.requestShowMessage(); + } + } + + Item { + width: parent.width - pinnedMessageButton.width - unpinMessageIconLoader.width - removePinnedMessageIconButton.width + height: pinnedMessageColumn.height + anchors.verticalCenter: parent.verticalCenter + Column { + id: pinnedMessageColumn + spacing: Theme.paddingSmall + width: parent.width + + Text { + id: pinnedMessageUserText + + width: parent.width + font.pixelSize: Theme.fontSizeExtraSmall + font.weight: Font.ExtraBold + color: Theme.primaryColor + maximumLineCount: 1 + elide: Text.ElideRight + textFormat: Text.StyledText + horizontalAlignment: Text.AlignLeft + } + + Text { + id: pinnedMessageText + + font.pixelSize: Theme.fontSizeExtraSmall + color: Theme.primaryColor + width: parent.width + elide: Text.ElideRight + maximumLineCount: 1 + textFormat: Text.StyledText + } + } + MouseArea { + anchors.fill: parent + onClicked: { + pinnedMessageItem.requestShowMessage(); + } + } + } + + Loader { + id: unpinMessageIconLoader + asynchronous: true + active: canPinMessages() + Behavior on opacity { FadeAnimation {} } + width: active ? item.width : 0 + height: active ? item.height : 0 + anchors.verticalCenter: parent.verticalCenter + sourceComponent: Component { + IconButton { + id: unpinMessageIconButton + icon.source: "image://theme/icon-m-remove" + onClicked: { + Remorse.itemAction(pinnedMessageRow, qsTr("Message unpinned"), function() { tdLibWrapper.unpinMessage(chatPage.chatInformation.id); + pinnedMessageItem.requestCloseMessage(); }); + + } + } + } + } + + IconButton { + id: removePinnedMessageIconButton + icon.source: "image://theme/icon-m-clear" + anchors.verticalCenter: parent.verticalCenter + onClicked: { + pinnedMessageItem.requestCloseMessage(); + pinnedMessage = undefined; + } + } + } +} diff --git a/qml/components/PollPreview.qml b/qml/components/PollPreview.qml index 0d4610a..74a330d 100644 --- a/qml/components/PollPreview.qml +++ b/qml/components/PollPreview.qml @@ -28,10 +28,11 @@ Item { property ListItem messageListItem - property var rawMessage: messageListItem.myMessage - property string chatId: messageListItem.page.chatInformation.id + property MessageOverlayFlickable overlayFlickable + property var rawMessage: messageListItem ? messageListItem.myMessage : overlayFlickable.overlayMessage + property string chatId: rawMessage.chat_id - property bool isOwnMessage: messageListItem.isOwnMessage + property bool isOwnMessage: messageListItem ? messageListItem.isOwnMessage : overlayFlickable.isOwnMessage property string messageId: rawMessage.id property bool canEdit: rawMessage.can_be_edited diff --git a/qml/components/StickerPreview.qml b/qml/components/StickerPreview.qml index f1c5335..1535943 100644 --- a/qml/components/StickerPreview.qml +++ b/qml/components/StickerPreview.qml @@ -21,12 +21,15 @@ import Sailfish.Silica 1.0 import WerkWolf.Fernschreiber 1.0 Item { - property ListItem messageListItem - readonly property var stickerData: messageListItem.myMessage.content.sticker; + property ListItem messageListItem + property MessageOverlayFlickable overlayFlickable + + readonly property var stickerData: messageListItem ? messageListItem.myMessage.content.sticker : overlayFlickable.overlayMessage.content.sticker; readonly property bool animated: stickerData.is_animated && appSettings.animateStickers readonly property bool stickerVisible: staticStickerLoader.item ? staticStickerLoader.item.visible : animatedStickerLoader.item ? animatedStickerLoader.item.visible : false + readonly property bool isOwnSticker : messageListItem ? messageListItem.isOwnMessage : overlayFlickable.isOwnMessage property real aspectRatio: stickerData.width / stickerData.height implicitWidth: stickerData.width @@ -43,8 +46,8 @@ Item { width: Math.min( stickerData.width, parent.width ) height: width * aspectRatio // (centered in image mode, text-like in sticker mode) - x: appSettings.showStickersAsImages ? (parent.width - width)/2 : - messageListItem.isOwnMessage ? (parent.width - width) : 0 + x: appSettings.showStickersAsImages ? (parent.width - width) / 2 : + isOwnSticker ? (parent.width - width) : 0 anchors.verticalCenter: parent.verticalCenter Loader { diff --git a/qml/components/VideoPreview.qml b/qml/components/VideoPreview.qml index c97c17c..a224742 100644 --- a/qml/components/VideoPreview.qml +++ b/qml/components/VideoPreview.qml @@ -25,19 +25,21 @@ Item { id: videoMessageComponent property ListItem messageListItem - property var rawMessage: messageListItem.myMessage + property MessageOverlayFlickable overlayFlickable + property var rawMessage: messageListItem ? messageListItem.myMessage : overlayFlickable.overlayMessage property var videoData: ( rawMessage.content['@type'] === "messageVideo" ) ? rawMessage.content.video : ( ( rawMessage.content['@type'] === "messageAnimation" ) ? rawMessage.content.animation : rawMessage.content.video_note ) property string videoUrl; property int previewFileId; property int videoFileId; + property bool isVideoNote : false; property bool fullscreen : false; - property bool onScreen: messageListItem.page.status === PageStatus.Active; + property bool onScreen: messageListItem ? messageListItem.page.status === PageStatus.Active : true; property string videoType : "video"; property bool playRequested: false; width: parent.width - height: ( rawMessage.content['@type'] === "messageVideoNote" ) ? width : Functions.getVideoHeight(width, videoData) + height: videoMessageComponent.isVideoNote ? width : Functions.getVideoHeight(width, videoData) Timer { id: screensaverTimer @@ -78,11 +80,11 @@ Item { function updateVideoThumbnail() { if (videoData) { - if (rawMessage.content['@type'] === "messageVideoNote") { - videoType = "video"; - } else { - videoType = videoData['@type']; + if (typeof rawMessage !== "undefined") { + videoMessageComponent.isVideoNote = rawMessage.content['@type'] === "messageVideoNote"; } + + videoMessageComponent.videoType = videoMessageComponent.isVideoNote ? "video" : videoData['@type']; videoFileId = videoData[videoType].id; if (typeof videoData.thumbnail !== "undefined") { previewFileId = videoData.thumbnail.photo.id; @@ -264,7 +266,7 @@ Item { id: videoComponentLoader active: false width: parent.width - height: ( rawMessage.content['@type'] === "messageVideoNote" ) ? width : Functions.getVideoHeight(parent.width, videoData) + height: videoMessageComponent.isVideoNote ? width : Functions.getVideoHeight(parent.width, videoData) sourceComponent: videoComponent } diff --git a/qml/components/WebPagePreview.qml b/qml/components/WebPagePreview.qml index a31c1c0..3437110 100644 --- a/qml/components/WebPagePreview.qml +++ b/qml/components/WebPagePreview.qml @@ -30,6 +30,7 @@ Column { property var webPageData; property var pictureFileInformation; property bool hasImage: false; + property bool largerFontSize: false; spacing: Theme.paddingSmall @@ -74,7 +75,7 @@ Column { width: parent.width text: webPageData.site_name ? Emoji.emojify(webPageData.site_name, font.pixelSize) : "" - font.pixelSize: Theme.fontSizeExtraSmall + font.pixelSize: webPagePreviewColumn.largerFontSize ? Theme.fontSizeSmall : Theme.fontSizeExtraSmall font.bold: true color: Theme.secondaryHighlightColor elide: Text.ElideRight @@ -88,7 +89,7 @@ Column { width: parent.width text: webPageData.title ? Emoji.emojify(webPageData.title, font.pixelSize) : "" - font.pixelSize: Theme.fontSizeExtraSmall + font.pixelSize: webPagePreviewColumn.largerFontSize ? Theme.fontSizeSmall : Theme.fontSizeExtraSmall font.bold: true color: Theme.primaryColor elide: Text.ElideRight @@ -103,7 +104,7 @@ Column { width: parent.width text: webPageData.description ? Emoji.emojify(webPageData.description, font.pixelSize) : "" - font.pixelSize: Theme.fontSizeExtraSmall + font.pixelSize: webPagePreviewColumn.largerFontSize ? Theme.fontSizeSmall : Theme.fontSizeExtraSmall color: Theme.primaryColor elide: Text.ElideRight wrapMode: Text.Wrap @@ -150,7 +151,7 @@ Column { width: parent.width text: qsTr("Preview not supported for this link...") - font.pixelSize: Theme.fontSizeTiny + font.pixelSize: webPagePreviewColumn.largerFontSize ? Theme.fontSizeExtraSmall : Theme.fontSizeTiny font.italic: true color: Theme.secondaryColor elide: Text.ElideRight diff --git a/qml/pages/ChatPage.qml b/qml/pages/ChatPage.qml index 3232227..1e85c51 100644 --- a/qml/pages/ChatPage.qml +++ b/qml/pages/ChatPage.qml @@ -17,6 +17,7 @@ along with Fernschreiber. If not, see . */ import QtQuick 2.6 +import QtGraphicalEffects 1.0 import Sailfish.Silica 1.0 import Sailfish.Pickers 1.0 import Nemo.Thumbnailer 1.0 @@ -145,11 +146,15 @@ Page { updateGroupStatusText(); } if (stickerManager.needsReload()) { - console.log("Stickers will be reloaded!"); + console.log("[ChatPage] Stickers will be reloaded!"); tdLibWrapper.getRecentStickers(); tdLibWrapper.getInstalledStickerSets(); stickerManager.setNeedsReload(false); } + if (chatInformation.pinned_message_id.toString() !== "0") { + console.log("[ChatPage] Loading pinned message " + chatInformation.pinned_message_id); + tdLibWrapper.getMessage(chatInformation.id, chatInformation.pinned_message_id); + } } function getMessageStatusText(message, listItemIndex, lastReadSentIndex, useElapsed) { @@ -278,6 +283,31 @@ Page { || groupStatusType === "chatMemberStatusCreator" || (groupStatusType === "chatMemberStatusRestricted" && groupStatus.permissions[privilege]) } + function canPinMessages() { + console.log("Can we pin messages?"); + if (chatPage.isPrivateChat) { + console.log("Private Chat: No!"); + return false; + } + if (chatPage.chatGroupInformation.status["@type"] === "chatMemberStatusCreator") { + console.log("Creator of this chat: Yes!"); + return true; + } + if (chatPage.chatInformation.permissions.can_pin_messages) { + console.log("All people can pin: Yes!"); + return true; + } + if (chatPage.chatGroupInformation.status["@type"] === "chatMemberStatusAdministrator") { + console.log("Admin with privileges? " + chatPage.chatGroupInformation.status.can_pin_messages); + return chatPage.chatGroupInformation.status.can_pin_messages; + } + if (chatPage.chatGroupInformation.status["@type"] === "chatMemberStatusRestricted") { + console.log("Restricted, but can pin messages? " + chatPage.chatGroupInformation.status.permissions.can_pin_messages); + return chatPage.chatGroupInformation.status.permissions.can_pin_messages; + } + console.log("Something else: No!"); + return false; + } Timer { id: forwardMessagesTimer @@ -359,6 +389,12 @@ Page { onErrorReceived: { Functions.handleErrorMessage(code, message); } + onReceivedMessage: { + if (messageId === chatInformation.pinned_message_id.toString()) { + console.log("[ChatPage] Received pinned message"); + pinnedMessageItem.pinnedMessage = message; + } + } } Connections { @@ -415,6 +451,15 @@ Page { chatInformation = chatModel.getChatInformation(); muteChatMenuItem.text = chatInformation.notification_settings.mute_for > 0 ? qsTr("Unmute Chat") : qsTr("Mute Chat"); } + onPinnedMessageChanged: { + chatInformation = chatModel.getChatInformation(); + if (chatInformation.pinned_message_id.toString() !== "0") { + console.log("[ChatPage] Loading pinned message " + chatInformation.pinned_message_id); + tdLibWrapper.getMessage(chatInformation.id, chatInformation.pinned_message_id); + } else { + pinnedMessageItem.pinnedMessage = undefined; + } + } } Connections { @@ -482,7 +527,7 @@ Page { contentWidth: width PullDownMenu { - visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active + visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active && !messageOverlayLoader.active MenuItem { id: joinLeaveChatMenuItem visible: (chatPage.isSuperGroup || chatPage.isBasicGroup) && chatGroupInformation && chatGroupInformation.status["@type"] !== "chatMemberStatusBanned" @@ -535,6 +580,7 @@ Page { } } } + Column { id: chatColumn width: parent.width @@ -589,10 +635,22 @@ Page { } } + PinnedMessageItem { + id: pinnedMessageItem + onRequestShowMessage: { + messageOverlayLoader.overlayMessage = pinnedMessageItem.pinnedMessage; + messageOverlayLoader.active = true; + } + onRequestCloseMessage: { + messageOverlayLoader.overlayMessage = undefined; + messageOverlayLoader.active = false; + } + } + Item { id: chatViewItem width: parent.width - height: parent.height - headerRow.height - newMessageColumn.height - selectedMessagesActions.height + height: parent.height - headerRow.height - pinnedMessageItem.height - newMessageColumn.height - selectedMessagesActions.height property int previousHeight; @@ -617,10 +675,24 @@ Page { } } + Loader { + asynchronous: true + active: chatView.blurred + anchors.fill: chatView + sourceComponent: Component { + FastBlur { + source: chatView + radius: Theme.paddingLarge + } + } + } SilicaListView { id: chatView + visible: !blurred + property bool blurred: messageOverlayLoader.item + anchors.fill: parent opacity: chatPage.loading ? 0 : 1 Behavior on opacity { FadeAnimation {} } @@ -829,6 +901,26 @@ Page { source: "../components/StickerPicker.qml" } + Loader { + id: messageOverlayLoader + + property var overlayMessage; + + active: false + asynchronous: true + width: parent.width + height: active ? parent.height : 0 + sourceComponent: Component { + MessageOverlayFlickable { + overlayMessage: messageOverlayLoader.overlayMessage + showHeader: !chatPage.isChannel + onRequestClose: { + messageOverlayLoader.active = false; + } + } + } + } + } Column { @@ -1160,6 +1252,7 @@ Page { } } } + Loader { id: selectedMessagesActions asynchronous: true @@ -1256,7 +1349,6 @@ Page { } } } - } } diff --git a/src/chatlistmodel.cpp b/src/chatlistmodel.cpp index 6de9094..5ce7145 100644 --- a/src/chatlistmodel.cpp +++ b/src/chatlistmodel.cpp @@ -44,6 +44,7 @@ namespace { const QString LAST_READ_OUTBOX_MESSAGE_ID("last_read_outbox_message_id"); const QString SENDING_STATE("sending_state"); const QString IS_CHANNEL("is_channel"); + const QString PINNED_MESSAGE_ID("pinned_message_id"); const QString _TYPE("@type"); } @@ -296,6 +297,7 @@ ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper) : showHiddenChats(false connect(tdLibWrapper, SIGNAL(chatReadInboxUpdated(QString, QString, int)), this, SLOT(handleChatReadInboxUpdated(QString, QString, int))); connect(tdLibWrapper, SIGNAL(chatReadOutboxUpdated(QString, QString)), this, SLOT(handleChatReadOutboxUpdated(QString, QString))); connect(tdLibWrapper, SIGNAL(chatPhotoUpdated(qlonglong, QVariantMap)), this, SLOT(handleChatPhotoUpdated(qlonglong, QVariantMap))); + connect(tdLibWrapper, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)), this, SLOT(handleChatPinnedMessageUpdated(qlonglong, qlonglong))); connect(tdLibWrapper, SIGNAL(messageSendSucceeded(QString, QString, QVariantMap)), this, SLOT(handleMessageSendSucceeded(QString, QString, QVariantMap))); connect(tdLibWrapper, SIGNAL(chatNotificationSettingsUpdated(QString, QVariantMap)), this, SLOT(handleChatNotificationSettingsUpdated(QString, QVariantMap))); connect(tdLibWrapper, SIGNAL(superGroupUpdated(qlonglong)), this, SLOT(handleGroupUpdated(qlonglong))); @@ -635,6 +637,22 @@ void ChatListModel::handleChatPhotoUpdated(qlonglong chatId, const QVariantMap & } } +void ChatListModel::handleChatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId) +{ + if (chatIndexMap.contains(chatId)) { + LOG("Updating pinned message for" << chatId); + const int chatIndex = chatIndexMap.value(chatId); + ChatData *chat = chatList.at(chatIndex); + chat->chatData.insert(PINNED_MESSAGE_ID, pinnedMessageId); + } else { + ChatData *chat = hiddenChats.value(chatId); + if (chat) { + LOG("Updating pinned message for hidden chat" << chatId); + chat->chatData.insert(PINNED_MESSAGE_ID, pinnedMessageId); + } + } +} + void ChatListModel::handleMessageSendSucceeded(const QString &messageId, const QString &oldMessageId, const QVariantMap &message) { bool ok; diff --git a/src/chatlistmodel.h b/src/chatlistmodel.h index e43ee57..1185b37 100644 --- a/src/chatlistmodel.h +++ b/src/chatlistmodel.h @@ -49,6 +49,7 @@ private slots: void handleChatReadInboxUpdated(const QString &chatId, const QString &lastReadInboxMessageId, int unreadCount); void handleChatReadOutboxUpdated(const QString &chatId, const QString &lastReadOutboxMessageId); void handleChatPhotoUpdated(qlonglong chatId, const QVariantMap &photo); + void handleChatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId); void handleMessageSendSucceeded(const QString &messageId, const QString &oldMessageId, const QVariantMap &message); void handleChatNotificationSettingsUpdated(const QString &chatId, const QVariantMap &chatNotificationSettings); void handleGroupUpdated(qlonglong groupId); diff --git a/src/chatmodel.cpp b/src/chatmodel.cpp index 72d6886..ab84c14 100644 --- a/src/chatmodel.cpp +++ b/src/chatmodel.cpp @@ -34,6 +34,7 @@ namespace { const QString UNREAD_COUNT("unread_count"); const QString LAST_READ_INBOX_MESSAGE_ID("last_read_inbox_message_id"); const QString SENDER_USER_ID("sender_user_id"); + const QString PINNED_MESSAGE_ID("pinned_message_id"); } ChatModel::ChatModel(TDLibWrapper *tdLibWrapper) : @@ -49,6 +50,7 @@ ChatModel::ChatModel(TDLibWrapper *tdLibWrapper) : connect(this->tdLibWrapper, SIGNAL(messageSendSucceeded(QString, QString, QVariantMap)), this, SLOT(handleMessageSendSucceeded(QString, QString, QVariantMap))); connect(this->tdLibWrapper, SIGNAL(chatNotificationSettingsUpdated(QString, QVariantMap)), this, SLOT(handleChatNotificationSettingsUpdated(QString, QVariantMap))); connect(this->tdLibWrapper, SIGNAL(chatPhotoUpdated(qlonglong, QVariantMap)), this, SLOT(handleChatPhotoUpdated(qlonglong, QVariantMap))); + connect(this->tdLibWrapper, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)), this, SLOT(handleChatPinnedMessageUpdated(qlonglong, qlonglong))); connect(this->tdLibWrapper, SIGNAL(messageContentUpdated(QString, QString, QVariantMap)), this, SLOT(handleMessageContentUpdated(QString, QString, QVariantMap))); connect(this->tdLibWrapper, SIGNAL(messagesDeleted(QString, QVariantList)), this, SLOT(handleMessagesDeleted(QString, QVariantList))); } @@ -292,6 +294,15 @@ void ChatModel::handleChatPhotoUpdated(qlonglong id, const QVariantMap &photo) } } +void ChatModel::handleChatPinnedMessageUpdated(qlonglong id, qlonglong pinnedMessageId) +{ + if (id == chatId) { + LOG("Pinned message updated" << chatId); + chatInformation.insert(PINNED_MESSAGE_ID, pinnedMessageId); + emit pinnedMessageChanged(); + } +} + void ChatModel::handleMessageContentUpdated(const QString &id, const QString &messageId, const QVariantMap &newContent) { LOG("Message content updated" << id << messageId); diff --git a/src/chatmodel.h b/src/chatmodel.h index a10e80d..7c54f5e 100644 --- a/src/chatmodel.h +++ b/src/chatmodel.h @@ -55,6 +55,7 @@ signals: void messageUpdated(int modelIndex); void messagesDeleted(); void smallPhotoChanged(); + void pinnedMessageChanged(); public slots: void handleMessagesReceived(const QVariantList &messages, int totalCount); @@ -64,6 +65,7 @@ public slots: void handleMessageSendSucceeded(const QString &messageId, const QString &oldMessageId, const QVariantMap &message); void handleChatNotificationSettingsUpdated(const QString &chatId, const QVariantMap &chatNotificationSettings); void handleChatPhotoUpdated(qlonglong chatId, const QVariantMap &photo); + void handleChatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId); void handleMessageContentUpdated(const QString &chatId, const QString &messageId, const QVariantMap &newContent); void handleMessagesDeleted(const QString &chatId, const QVariantList &messageIds); diff --git a/src/tdlibreceiver.cpp b/src/tdlibreceiver.cpp index b96c0dc..a37f1a9 100644 --- a/src/tdlibreceiver.cpp +++ b/src/tdlibreceiver.cpp @@ -127,6 +127,7 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren handlers.insert("updateChatPermissions", &TDLibReceiver::processUpdateChatPermissions); handlers.insert("updateChatPhoto", &TDLibReceiver::processUpdateChatPhoto); handlers.insert("updateChatTitle", &TDLibReceiver::processUpdateChatTitle); + handlers.insert("updateChatPinnedMessage", &TDLibReceiver::processUpdateChatPinnedMessage); handlers.insert("users", &TDLibReceiver::processUsers); handlers.insert("error", &TDLibReceiver::processError); handlers.insert("ok", &TDLibReceiver::nop); @@ -506,6 +507,12 @@ void TDLibReceiver::processUpdateChatTitle(const QVariantMap &receivedInformatio emit chatTitleUpdated(receivedInformation.value(CHAT_ID).toString(), receivedInformation.value(TITLE).toString()); } +void TDLibReceiver::processUpdateChatPinnedMessage(const QVariantMap &receivedInformation) +{ + LOG("Received UpdateChatPinnedMessage"); + emit chatPinnedMessageUpdated(receivedInformation.value(CHAT_ID).toLongLong(), receivedInformation.value("pinned_message_id").toLongLong()); +} + void TDLibReceiver::processUsers(const QVariantMap &receivedInformation) { LOG("Received Users"); diff --git a/src/tdlibreceiver.h b/src/tdlibreceiver.h index a06bed8..3d041c7 100644 --- a/src/tdlibreceiver.h +++ b/src/tdlibreceiver.h @@ -80,7 +80,8 @@ signals: void userProfilePhotos(const QString &extra, const QVariantList &photos, int totalPhotos); void chatPermissionsUpdated(const QString &chatId, const QVariantMap &chatPermissions); void chatPhotoUpdated(qlonglong chatId, const QVariantMap &photo); - void chatTitleUpdated(const QString &chatId, const QString &title); + void chatTitleUpdated(const QString &chatId, const QString &title); + void chatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId); void usersReceived(const QString &extra, const QVariantList &userIds, int totalUsers); void errorReceived(const int code, const QString &message); @@ -139,6 +140,7 @@ private: void processUpdateChatPermissions(const QVariantMap &receivedInformation); void processUpdateChatPhoto(const QVariantMap &receivedInformation); void processUpdateChatTitle(const QVariantMap &receivedInformation); + void processUpdateChatPinnedMessage(const QVariantMap &receivedInformation); void processUsers(const QVariantMap &receivedInformation); void processError(const QVariantMap &receivedInformation); void nop(const QVariantMap &receivedInformation); diff --git a/src/tdlibwrapper.cpp b/src/tdlibwrapper.cpp index 4ab4ea7..293dbfa 100644 --- a/src/tdlibwrapper.cpp +++ b/src/tdlibwrapper.cpp @@ -112,6 +112,7 @@ TDLibWrapper::TDLibWrapper(AppSettings *appSettings, QObject *parent) : QObject( connect(this->tdLibReceiver, SIGNAL(chatPermissionsUpdated(QString, QVariantMap)), this, SIGNAL(chatPermissionsUpdated(QString, QVariantMap))); connect(this->tdLibReceiver, SIGNAL(chatPhotoUpdated(qlonglong, QVariantMap)), this, SIGNAL(chatPhotoUpdated(qlonglong, QVariantMap))); connect(this->tdLibReceiver, SIGNAL(chatTitleUpdated(QString, QString)), this, SIGNAL(chatTitleUpdated(QString, QString))); + connect(this->tdLibReceiver, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)), this, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong))); connect(this->tdLibReceiver, SIGNAL(usersReceived(QString, QVariantList, int)), this, SIGNAL(usersReceived(QString, QVariantList, int))); connect(this->tdLibReceiver, SIGNAL(errorReceived(int, QString)), this, SIGNAL(errorReceived(int, QString))); @@ -291,6 +292,26 @@ void TDLibWrapper::viewMessage(const QString &chatId, const QString &messageId, this->sendRequest(requestObject); } +void TDLibWrapper::pinMessage(const QString &chatId, const QString &messageId, bool disableNotification) +{ + LOG("Pin message to chat" << chatId << messageId << disableNotification); + QVariantMap requestObject; + requestObject.insert(_TYPE, "pinChatMessage"); + requestObject.insert("chat_id", chatId); + requestObject.insert("message_id", messageId); + requestObject.insert("disable_notification", disableNotification); + this->sendRequest(requestObject); +} + +void TDLibWrapper::unpinMessage(const QString &chatId) +{ + LOG("Unpin message from chat" << chatId); + QVariantMap requestObject; + requestObject.insert(_TYPE, "unpinChatMessage"); + requestObject.insert("chat_id", chatId); + this->sendRequest(requestObject); +} + void TDLibWrapper::sendTextMessage(const QString &chatId, const QString &message, const QString &replyToMessageId) { LOG("Sending text message" << chatId << message << replyToMessageId); diff --git a/src/tdlibwrapper.h b/src/tdlibwrapper.h index c88b3c7..0f02595 100644 --- a/src/tdlibwrapper.h +++ b/src/tdlibwrapper.h @@ -122,6 +122,8 @@ public: Q_INVOKABLE void leaveChat(const QString &chatId); Q_INVOKABLE void getChatHistory(qlonglong chatId, const qlonglong &fromMessageId = 0, int offset = 0, int limit = 50, bool onlyLocal = false); Q_INVOKABLE void viewMessage(const QString &chatId, const QString &messageId, bool force); + Q_INVOKABLE void pinMessage(const QString &chatId, const QString &messageId, bool disableNotification = false); + Q_INVOKABLE void unpinMessage(const QString &chatId); Q_INVOKABLE void sendTextMessage(const QString &chatId, const QString &message, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendPhotoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendVideoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0"); @@ -218,6 +220,7 @@ signals: void chatPermissionsUpdated(const QString &chatId, const QVariantMap &permissions); void chatPhotoUpdated(qlonglong chatId, const QVariantMap &photo); void chatTitleUpdated(const QString &chatId, const QString &title); + void chatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId); void usersReceived(const QString &extra, const QVariantList &userIds, int totalUsers); void errorReceived(const int code, const QString &message); diff --git a/translations/harbour-fernschreiber-de.ts b/translations/harbour-fernschreiber-de.ts index 413c482..df94a10 100644 --- a/translations/harbour-fernschreiber-de.ts +++ b/translations/harbour-fernschreiber-de.ts @@ -839,6 +839,10 @@ Select Message Nachricht auswählen + + Pin Message + Nachricht anheften + MessageListViewItemSimple @@ -847,6 +851,17 @@ Sie + + MessageOverlayFlickable + + You + Sie + + + This message was forwarded. Original author: %1 + Diese Nachricht wurde weitergeleitet. Ursprünglicher Autor: %1 + + NotificationManager @@ -897,6 +912,21 @@ Sie haben noch keine Chats. + + PinnedMessageItem + + You + Sie + + + Pinned Message + Angeheftete Nachricht + + + Message unpinned + Nachricht losgeheftet + + PollCreationPage diff --git a/translations/harbour-fernschreiber-en.ts b/translations/harbour-fernschreiber-en.ts index f99c38d..c8b04f9 100644 --- a/translations/harbour-fernschreiber-en.ts +++ b/translations/harbour-fernschreiber-en.ts @@ -839,6 +839,10 @@ Select Message Select Message + + Pin Message + + MessageListViewItemSimple @@ -847,6 +851,17 @@ You + + MessageOverlayFlickable + + You + You + + + This message was forwarded. Original author: %1 + + + NotificationManager @@ -897,6 +912,21 @@ You don't have any chats yet. + + PinnedMessageItem + + You + You + + + Pinned Message + + + + Message unpinned + + + PollCreationPage diff --git a/translations/harbour-fernschreiber-es.ts b/translations/harbour-fernschreiber-es.ts index b8e0b40..bf46684 100644 --- a/translations/harbour-fernschreiber-es.ts +++ b/translations/harbour-fernschreiber-es.ts @@ -835,6 +835,10 @@ Select Message Seleccionar mensaje + + Pin Message + + MessageListViewItemSimple @@ -843,6 +847,17 @@ Usted + + MessageOverlayFlickable + + You + Usted + + + This message was forwarded. Original author: %1 + + + NotificationManager @@ -893,6 +908,21 @@ No hay todavía ninguna charla. + + PinnedMessageItem + + You + Usted + + + Pinned Message + + + + Message unpinned + + + PollCreationPage diff --git a/translations/harbour-fernschreiber-fi.ts b/translations/harbour-fernschreiber-fi.ts index fa067be..4aea0ae 100644 --- a/translations/harbour-fernschreiber-fi.ts +++ b/translations/harbour-fernschreiber-fi.ts @@ -840,6 +840,10 @@ Select Message + + Pin Message + + MessageListViewItemSimple @@ -848,6 +852,17 @@ Sinä + + MessageOverlayFlickable + + You + Sinä + + + This message was forwarded. Original author: %1 + + + NotificationManager @@ -898,6 +913,21 @@ Sinulla ei ole vielä keskusteluja. + + PinnedMessageItem + + You + Sinä + + + Pinned Message + + + + Message unpinned + + + PollCreationPage diff --git a/translations/harbour-fernschreiber-hu.ts b/translations/harbour-fernschreiber-hu.ts index be6c998..82da38a 100644 --- a/translations/harbour-fernschreiber-hu.ts +++ b/translations/harbour-fernschreiber-hu.ts @@ -835,6 +835,10 @@ Select Message + + Pin Message + + MessageListViewItemSimple @@ -843,6 +847,17 @@ Te + + MessageOverlayFlickable + + You + Te + + + This message was forwarded. Original author: %1 + + + NotificationManager @@ -893,6 +908,21 @@ + + PinnedMessageItem + + You + Te + + + Pinned Message + + + + Message unpinned + + + PollCreationPage diff --git a/translations/harbour-fernschreiber-it.ts b/translations/harbour-fernschreiber-it.ts index 64e6d4f..5b72241 100644 --- a/translations/harbour-fernschreiber-it.ts +++ b/translations/harbour-fernschreiber-it.ts @@ -839,6 +839,10 @@ Select Message + + Pin Message + + MessageListViewItemSimple @@ -847,6 +851,17 @@ Tu + + MessageOverlayFlickable + + You + Tu + + + This message was forwarded. Original author: %1 + + + NotificationManager @@ -897,6 +912,21 @@ Carica lista chat... + + PinnedMessageItem + + You + Tu + + + Pinned Message + + + + Message unpinned + + + PollCreationPage diff --git a/translations/harbour-fernschreiber-pl.ts b/translations/harbour-fernschreiber-pl.ts index 61e474e..03223e9 100644 --- a/translations/harbour-fernschreiber-pl.ts +++ b/translations/harbour-fernschreiber-pl.ts @@ -843,6 +843,10 @@ Select Message Wybierz wiadomość + + Pin Message + + MessageListViewItemSimple @@ -851,6 +855,17 @@ Ty + + MessageOverlayFlickable + + You + Ty + + + This message was forwarded. Original author: %1 + + + NotificationManager @@ -901,6 +916,21 @@ Nie masz jeszcze żadnych czatów. + + PinnedMessageItem + + You + Ty + + + Pinned Message + + + + Message unpinned + + + PollCreationPage diff --git a/translations/harbour-fernschreiber-ru.ts b/translations/harbour-fernschreiber-ru.ts index 2872909..427270e 100644 --- a/translations/harbour-fernschreiber-ru.ts +++ b/translations/harbour-fernschreiber-ru.ts @@ -843,6 +843,10 @@ Select Message + + Pin Message + + MessageListViewItemSimple @@ -851,6 +855,17 @@ Вы + + MessageOverlayFlickable + + You + Вы + + + This message was forwarded. Original author: %1 + + + NotificationManager @@ -901,6 +916,21 @@ Тут пока ничего нет + + PinnedMessageItem + + You + Вы + + + Pinned Message + + + + Message unpinned + + + PollCreationPage diff --git a/translations/harbour-fernschreiber-sv.ts b/translations/harbour-fernschreiber-sv.ts index 8379e51..2d1c91b 100644 --- a/translations/harbour-fernschreiber-sv.ts +++ b/translations/harbour-fernschreiber-sv.ts @@ -839,6 +839,10 @@ Select Message Välj meddelande + + Pin Message + + MessageListViewItemSimple @@ -847,6 +851,17 @@ Du + + MessageOverlayFlickable + + You + Du + + + This message was forwarded. Original author: %1 + + + NotificationManager @@ -897,6 +912,21 @@ Du har inga chattar än. + + PinnedMessageItem + + You + Du + + + Pinned Message + + + + Message unpinned + + + PollCreationPage diff --git a/translations/harbour-fernschreiber-zh_CN.ts b/translations/harbour-fernschreiber-zh_CN.ts index 05c9911..39899b4 100644 --- a/translations/harbour-fernschreiber-zh_CN.ts +++ b/translations/harbour-fernschreiber-zh_CN.ts @@ -835,6 +835,10 @@ Select Message 选择消息 + + Pin Message + + MessageListViewItemSimple @@ -843,6 +847,17 @@ + + MessageOverlayFlickable + + You + + + + This message was forwarded. Original author: %1 + + + NotificationManager @@ -893,6 +908,21 @@ 你还没有任何对话。 + + PinnedMessageItem + + You + + + + Pinned Message + + + + Message unpinned + + + PollCreationPage diff --git a/translations/harbour-fernschreiber.ts b/translations/harbour-fernschreiber.ts index 4996468..8414ccd 100644 --- a/translations/harbour-fernschreiber.ts +++ b/translations/harbour-fernschreiber.ts @@ -835,6 +835,10 @@ Select Message + + Pin Message + + MessageListViewItemSimple @@ -843,6 +847,17 @@ + + MessageOverlayFlickable + + You + + + + This message was forwarded. Original author: %1 + + + NotificationManager @@ -893,6 +908,21 @@ + + PinnedMessageItem + + You + + + + Pinned Message + + + + Message unpinned + + + PollCreationPage