diff --git a/harbour-fernschreiber.pro b/harbour-fernschreiber.pro
index a99ffc0..7dfd861 100644
--- a/harbour-fernschreiber.pro
+++ b/harbour-fernschreiber.pro
@@ -45,12 +45,15 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/components/BackgroundImage.qml \
qml/components/ChatListViewItem.qml \
qml/components/DocumentPreview.qml \
+ qml/components/GamePreview.qml \
qml/components/ImagePreview.qml \
qml/components/InReplyToRow.qml \
+ qml/components/InlineQuery.qml \
qml/components/LocationPreview.qml \
qml/components/MessageListViewItem.qml \
qml/components/MessageListViewItemSimple.qml \
qml/components/MessageOverlayFlickable.qml \
+ qml/components/MessageViaLabel.qml \
qml/components/MultilineEmojiLabel.qml \
qml/components/PinnedMessageItem.qml \
qml/components/PollPreview.qml \
@@ -72,6 +75,20 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/components/chatInformationPage/ChatInformationTextItem.qml \
qml/components/chatInformationPage/EditGroupChatPermissionsColumn.qml \
qml/components/chatInformationPage/EditSuperGroupSlowModeColumn.qml \
+ qml/components/inlineQueryResults/InlineQueryResult.qml \
+ qml/components/inlineQueryResults/InlineQueryResultAnimation.qml \
+ qml/components/inlineQueryResults/InlineQueryResultArticle.qml \
+ qml/components/inlineQueryResults/InlineQueryResultAudio.qml \
+ qml/components/inlineQueryResults/InlineQueryResultContact.qml \
+ qml/components/inlineQueryResults/InlineQueryResultDefaultBase.qml \
+ qml/components/inlineQueryResults/InlineQueryResultDocument.qml \
+ qml/components/inlineQueryResults/InlineQueryResultGame.qml \
+ qml/components/inlineQueryResults/InlineQueryResultLocation.qml \
+ qml/components/inlineQueryResults/InlineQueryResultPhoto.qml \
+ qml/components/inlineQueryResults/InlineQueryResultSticker.qml \
+ qml/components/inlineQueryResults/InlineQueryResultVenue.qml \
+ qml/components/inlineQueryResults/InlineQueryResultVideo.qml \
+ qml/components/inlineQueryResults/InlineQueryResultVoiceNote.qml \
qml/js/debug.js \
qml/js/functions.js \
qml/pages/ChatInformationPage.qml \
diff --git a/qml/components/GamePreview.qml b/qml/components/GamePreview.qml
new file mode 100644
index 0000000..12551b1
--- /dev/null
+++ b/qml/components/GamePreview.qml
@@ -0,0 +1,125 @@
+/*
+ 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 WerkWolf.Fernschreiber 1.0
+import "../js/functions.js" as Functions
+import "../js/twemoji.js" as Emoji
+
+Column {
+ id: gamePreviewItem
+
+ property ListItem messageListItem
+ property MessageOverlayFlickable overlayFlickable
+ property var rawMessage: messageListItem ? messageListItem.myMessage : overlayFlickable.overlayMessage
+ property bool highlighted
+
+ width: parent.width
+ height: childrenRect.height
+
+
+ Label {
+ width: parent.width
+ font.bold: true
+ font.pixelSize: Theme.fontSizeSmall
+ text: Emoji.emojify(rawMessage.content.game.title || "", font.pixelSize)
+ truncationMode: TruncationMode.Fade
+ textFormat: Text.StyledText
+ wrapMode: Text.Wrap
+ }
+ Label {
+ width: parent.width
+ font.pixelSize: Theme.fontSizeExtraSmall
+ text: Emoji.emojify(rawMessage.content.game.description || "", font.pixelSize)
+ truncationMode: TruncationMode.Fade
+ textFormat: Text.StyledText
+ wrapMode: Text.Wrap
+ }
+ Label {
+ width: parent.width
+ font.pixelSize: Theme.fontSizeExtraSmall
+ text: Emoji.emojify(Functions.enhanceMessageText(rawMessage.content.game.text) || "", font.pixelSize)
+ truncationMode: TruncationMode.Fade
+ wrapMode: Text.Wrap
+ textFormat: Text.StyledText
+ onLinkActivated: {
+ var chatCommand = Functions.handleLink(link);
+ if(chatCommand) {
+ tdLibWrapper.sendTextMessage(chatInformation.id, chatCommand);
+ }
+ }
+ }
+ Item {
+ width: parent.width
+ height: Theme.paddingLarge
+ }
+
+ Image {
+ id: thumbnail
+ source: thumbnailFile.isDownloadingCompleted ? thumbnailFile.path : ""
+ fillMode: Image.PreserveAspectCrop
+ asynchronous: true
+ visible: opacity > 0
+ opacity: status === Image.Ready ? 1.0 : 0.0
+
+ Behavior on opacity { FadeAnimation {} }
+ layer.enabled: queryResultItem.pressed
+ layer.effect: PressEffect { source: thumbnail }
+
+ TDLibFile {
+ id: thumbnailFile
+ tdlib: tdLibWrapper
+ autoLoad: true
+ }
+ Rectangle {
+ width: Theme.iconSizeMedium
+ height: width
+ anchors {
+ top: parent.top
+ topMargin: Theme.paddingSmall
+ left: parent.left
+ leftMargin: Theme.paddingSmall
+ }
+
+ color: Theme.rgba(Theme.overlayBackgroundColor, 0.2)
+ radius: Theme.paddingSmall
+ Icon {
+ id: icon
+ source: "image://theme/icon-m-game-controller"
+ asynchronous: true
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ if (rawMessage.content.game.photo) {
+ // Check first which size fits best...
+ var photo
+ for (var i = 0; i < rawMessage.content.game.photo.sizes.length; i++) {
+ photo = rawMessage.content.game.photo.sizes[i].photo
+ if (rawMessage.content.game.photo.sizes[i].width >= gamePreviewItem.width) {
+ break
+ }
+ }
+ if (photo) {
+ thumbnailFile.fileInformation = photo
+ }
+ }
+ }
+}
diff --git a/qml/components/InlineQuery.qml b/qml/components/InlineQuery.qml
new file mode 100644
index 0000000..1ad7cb3
--- /dev/null
+++ b/qml/components/InlineQuery.qml
@@ -0,0 +1,399 @@
+/*
+ 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 "../js/debug.js" as Debug
+import "../js/twemoji.js" as Emoji
+import "../js/functions.js" as Functions
+
+Loader {
+ id: inlineQueryLoader
+ active: userName.length > 1
+ asynchronous: true
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ bottom: active ? parent.bottom : parent.top
+ }
+ property bool hasOverlay: active && userNameIsValid && status === Loader.Ready && item.overlay && item.overlay.status === Loader.Ready
+ property bool hasButton: active && userNameIsValid && status === Loader.Ready && item.button && item.button.status === Loader.Ready
+
+ property int buttonPadding: hasButton ? item.button.height + Theme.paddingSmall : 0
+ Behavior on buttonPadding { NumberAnimation { duration: 200} }
+
+ property string chatId
+ property string userName
+ property bool userNameIsValid: userName !== "" && inlineBotInformation && userName.toLowerCase() === inlineBotInformation.username.toLowerCase()
+ property string query
+ property int currentOffset: 0
+ property string responseExtra: chatId+"|"+userName+"|"+query+"|"+currentOffset
+
+ property bool queued: false
+ property TextArea textField
+ property bool isLoading
+ property var inlineBotInformation: null
+ onIsLoadingChanged: {
+ requestTimeout.start();
+ }
+
+ onStatusChanged: {
+ inlineBotInformation = null;
+ if(status === Loader.Ready && userName !== "") {
+ isLoading = true; inlineQueryLoader.chatId
+ tdLibWrapper.searchPublicChat(userName, false);
+ }
+ }
+
+ onUserNameChanged: {
+ inlineBotInformation = null;
+
+ if(status === Loader.Ready && userName !== "") {
+ isLoading = true;
+ tdLibWrapper.searchPublicChat(userName, false);
+ }
+ }
+
+ onQueryChanged: {
+ if(userName.length > 0) {
+ isLoading = true;
+ requestTimer.start();
+ }
+ }
+
+ function handleQuery(name, query, offset) {
+ if(!name) {
+ inlineQueryLoader.userName = "";
+ inlineQueryLoader.query = "";
+ return;
+ }
+ if(inlineQueryLoader.userName !== name) {
+ inlineQueryLoader.userName = name
+ }
+ if(inlineQueryLoader.query !== query) {
+ inlineQueryLoader.query = query
+ }
+ inlineQueryLoader.currentOffset = offset || 0
+ }
+
+ function request() {
+ if(!inlineBotInformation || !userNameIsValid) {
+ queued = true;
+ } else {
+ queued = false;
+ var location = null;
+ if(inlineBotInformation.type.need_location && fernschreiberUtils.supportsGeoLocation()) {
+ fernschreiberUtils.startGeoLocationUpdates();
+ if(!attachmentPreviewRow.locationData.latitude) {
+ queued = true;
+ return;
+ }
+ }
+ tdLibWrapper.getInlineQueryResults(inlineBotInformation.id, chatId, location, query, inlineQueryLoader.currentOffset, inlineQueryLoader.responseExtra);
+ isLoading = true;
+ }
+ }
+
+ Timer {
+ id: requestTimeout
+ interval: 5000
+ onTriggered: {
+ inlineQueryLoader.isLoading = false;
+ }
+ }
+
+ Timer {
+ id: requestTimer
+ interval: 1000
+ onTriggered: {
+ request();
+ }
+ }
+
+ Connections {
+ target: fernschreiberUtils
+ onNewPositionInformation: {
+ attachmentPreviewRow.locationData = positionInformation;
+ if (inlineQueryLoader.queued) {
+ inlineQueryLoader.queued = false;
+ inlineQueryLoader.request()
+ }
+ }
+ }
+
+ Connections {
+ target: textField
+ onTextChanged: {
+ if(textField.text.charAt(0) === '@') {
+ var queryMatch = textField.text.match(/^@([a-zA-Z0-9_]+)\s(.*)/);
+ if(queryMatch) {
+ inlineQueryLoader.handleQuery(queryMatch[1], queryMatch[2]);
+ } else {
+ inlineQueryLoader.handleQuery();
+ }
+ } else {
+ inlineQueryLoader.handleQuery();
+ }
+ }
+ }
+
+ sourceComponent: Component {
+ Item {
+ id: inlineQueryComponent
+ anchors.fill: parent
+ property alias overlay: resultsOverlay
+ property alias button: switchToPmLoader
+ property string nextOffset
+ property string inlineQueryId
+ property string switchPmText
+ property string switchPmParameter
+ property ListModel resultModel: ListModel {
+ dynamicRoles: true
+ }
+ property string inlineQueryPlaceholder: inlineBotInformation ? inlineBotInformation.type.inline_query_placeholder : ""
+ property bool showInlineQueryPlaceholder: !!inlineQueryPlaceholder && query === ""
+ property string useDelegateSize: "default"
+ property var dimensions: ({
+ "default": [[Screen.width, Screen.height / 2], [Theme.itemSizeLarge, Theme.itemSizeLarge]], // whole line (portrait half)
+ "inlineQueryResultAnimation": [[Screen.width / 3, Screen.height / 6], [Screen.width / 3, Screen.height / 6]],
+ "inlineQueryResultVideo": [[Screen.width / 2, Screen.height / 4], [Theme.itemSizeLarge, Theme.itemSizeLarge]],
+ "inlineQueryResultSticker": [[Screen.width / 3, Screen.height / 6], [Screen.width / 3, Screen.height / 6]],
+ "inlineQueryResultPhoto": [[Screen.width/2, Screen.height / 3], [Theme.itemSizeExtraLarge, Theme.itemSizeExtraLarge]],
+ })
+ property int delegateWidth: chatPage.isPortrait ? dimensions[useDelegateSize][0][0] : dimensions[useDelegateSize][0][1]
+ property int delegateHeight: chatPage.isPortrait ? dimensions[useDelegateSize][1][0] : dimensions[useDelegateSize][1][1]
+
+ function setDelegateSizes() {
+ var sizeKey = "default";
+ var modelCount = resultModel.count;
+ if(modelCount > 0) {
+ var firstType = resultModel.get(0)["@type"];
+ if(firstType && dimensions[firstType]) {
+ var startIndex = inlineQueryLoader.currentOffset === 0 ? 1 : inlineQueryLoader.currentOffset;
+ var same = true;
+ for(var i = startIndex; i < modelCount; i += 1) {
+ if(resultModel.get(i)["@type"] !== firstType) {
+ same = false;
+ continue;
+ }
+ }
+ if(same) {
+ sizeKey = firstType;
+ }
+ }
+ }
+ useDelegateSize = sizeKey;
+ }
+
+ function loadMore() {
+ if(nextOffset && inlineQueryLoader.userNameIsValid) {
+ inlineQueryLoader.currentOffset = nextOffset;
+ inlineQueryLoader.request();
+ }
+ }
+
+ Connections {
+ target: tdLibWrapper
+
+ onChatReceived: {
+ if(chat["@extra"] === "searchPublicChat:"+inlineQueryLoader.userName) {
+ requestTimeout.stop();
+ inlineQueryLoader.isLoading = false;
+ var inlineBotInformation = tdLibWrapper.getUserInformation(chat.type.user_id);
+ if(inlineBotInformation && inlineBotInformation.type["@type"] === "userTypeBot" && inlineBotInformation.type.is_inline) {
+ inlineQueryLoader.inlineBotInformation = inlineBotInformation;
+ requestTimer.start();
+ }
+ }
+ }
+ onInlineQueryResults: {
+ if(extra === inlineQueryLoader.responseExtra) {
+ requestTimeout.stop();
+ inlineQueryLoader.isLoading = false;
+ inlineQueryComponent.inlineQueryId = inlineQueryId
+ inlineQueryComponent.nextOffset = nextOffset
+ inlineQueryComponent.switchPmText = switchPmText
+ inlineQueryComponent.switchPmParameter = switchPmParameter
+
+ if(inlineQueryLoader.currentOffset === 0) {
+ inlineQueryComponent.resultModel.clear()
+ }
+ for(var i = 0; i < results.length; i++) {
+ inlineQueryComponent.resultModel.append(results[i]);
+ }
+
+ if(inlineQueryLoader.currentOffset === 0 || inlineQueryLoader.useDelegateSize !== "default") {
+ inlineQueryComponent.setDelegateSizes()
+ }
+ }
+ }
+ }
+ // switch to pm Button
+ Loader {
+ id: switchToPmLoader
+ asynchronous: true
+ active: inlineQueryComponent.switchPmText.length > 0
+ opacity: status === Loader.Ready ? 1.0 : 0.0
+ Behavior on opacity { FadeAnimation {} }
+ height: Theme.itemSizeSmall
+ anchors {
+ top: parent.bottom
+ topMargin: Theme.paddingSmall
+ left: parent.left
+ leftMargin: Theme.horizontalPageMargin
+ right: parent.right
+ rightMargin: Theme.horizontalPageMargin
+ }
+ sourceComponent: Component {
+ MouseArea {
+ id: customButton
+ onClicked: {
+ tdLibWrapper.createPrivateChat(inlineQueryLoader.inlineBotInformation.id, "openAndSendStartToBot:"+(inlineQueryComponent.switchPmParameter.length > 0 ? " "+inlineQueryComponent.switchPmParameter:""));
+ }
+ Rectangle {
+ anchors.fill: parent
+ radius: Theme.paddingSmall
+ color: parent.pressed ? Theme.highlightBackgroundColor : Theme.rgba(Theme.DarkOnLight ? Qt.lighter(Theme.primaryColor) : Qt.darker(Theme.primaryColor), Theme.opacityFaint)
+ Label {
+ anchors {
+ fill: parent
+ leftMargin: Theme.paddingLarge
+ rightMargin: Theme.paddingLarge
+ }
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+
+ fontSizeMode: Text.Fit;
+ minimumPixelSize: Theme.fontSizeTiny;
+ font.pixelSize: Theme.fontSizeSmall
+
+ color: customButton.pressed ? Theme.highlightColor : Theme.primaryColor
+ text: Emoji.emojify(inlineQueryComponent.switchPmText, font.pixelSize)// + "we are gonna make this a bit longer"
+ }
+ }
+ }
+ }
+ }
+
+ // results grid overlay
+ Loader {
+ id: resultsOverlay
+ asynchronous: true
+ active: inlineQueryComponent.resultModel.count > 0
+ anchors.fill: parent
+ opacity: !!item ? 1.0 : 0.0
+ Behavior on opacity { FadeAnimation {} }
+ property var supportedResultTypes: [
+ "inlineQueryResultAnimation",
+ "inlineQueryResultArticle",
+ "inlineQueryResultAudio",
+ "inlineQueryResultContact",
+ "inlineQueryResultDocument",
+ "inlineQueryResultGame",
+ "inlineQueryResultLocation",
+ "inlineQueryResultPhoto",
+ "inlineQueryResultSticker",
+ "inlineQueryResultVenue",
+ "inlineQueryResultVideo",
+ "inlineQueryResultVoiceNote",
+ ]
+ sourceComponent: Component {
+ Item {
+ Rectangle {
+ id: messageContentBackground
+ color: Theme.overlayBackgroundColor
+ opacity: 0.7
+ anchors.fill: parent
+ }
+ Timer {
+ id: autoLoadMoreTimer
+ interval: 400
+ onTriggered: {
+ if (inlineQueryComponent.nextOffset && resultView.height > resultView.contentHeight - Theme.itemSizeHuge) {
+ inlineQueryComponent.loadMore();
+ }
+ }
+ }
+ SilicaGridView {
+ id: resultView
+ anchors.fill: parent
+ cellWidth: inlineQueryComponent.delegateWidth
+ cellHeight: inlineQueryComponent.delegateHeight
+
+ signal requestPlayback(url playbackSource)
+ clip: true
+ model: inlineQueryComponent.resultModel
+ delegate: Loader {
+ id: queryResultDelegate
+ height: resultView.cellHeight
+ width: resultView.cellWidth
+ source: "inlineQueryResults/" + (resultsOverlay.supportedResultTypes.indexOf(model["@type"]) > -1 ? (model["@type"].charAt(0).toUpperCase() + model["@type"].substring(1)) : "InlineQueryResultDefaultBase") +".qml"
+ }
+ footer: Component {
+ Item {
+ width: resultView.width
+ visible: height > 0
+ height: inlineQueryComponent.nextOffset ? Theme.itemSizeLarge : 0
+ Behavior on height { NumberAnimation { duration: 500 } }
+ }
+ }
+
+ onContentYChanged: {
+ if(!inlineQueryLoader.isLoading && inlineQueryComponent.nextOffset && contentHeight - contentY - height < Theme.itemSizeHuge) {
+ inlineQueryComponent.loadMore();
+ }
+ }
+
+ ScrollDecorator { flickable: resultView }
+ }
+ }
+ }
+ }
+
+
+ // textarea placeholder
+ Loader {
+ asynchronous: true
+ active: inlineQueryComponent.showInlineQueryPlaceholder
+ sourceComponent: Component {
+ Label {
+ text: Emoji.emojify(inlineQueryComponent.inlineQueryPlaceholder, font.pixelSize);
+ parent: textField
+ anchors.fill: parent
+ anchors.leftMargin: textMetrics.boundingRect.width + Theme.paddingSmall
+ font: textField.font
+ color: Theme.secondaryColor
+
+ truncationMode: TruncationMode.Fade
+ TextMetrics {
+ id: textMetrics
+ font: textField.font
+ text: textField.text
+ }
+ }
+ }
+ }
+
+
+ }
+ }
+
+
+
+}
diff --git a/qml/components/LocationPreview.qml b/qml/components/LocationPreview.qml
index e50120e..fb4ec49 100644
--- a/qml/components/LocationPreview.qml
+++ b/qml/components/LocationPreview.qml
@@ -35,6 +35,7 @@ Item {
property var pictureFileInformation;
width: parent.width
height: width / 2
+ property string fileExtra
Component.onCompleted: {
updatePicture();
@@ -47,14 +48,18 @@ Item {
function updatePicture() {
imagePreviewItem.pictureFileInformation = null;
if (locationData) {
- tdLibWrapper.getMapThumbnailFile(chatId, locationData.latitude, locationData.longitude, Math.round(imagePreviewItem.width), Math.round(imagePreviewItem.height));
+ fileExtra = "location:" + locationData.latitude + ":" + locationData.longitude + ":" + Math.round(imagePreviewItem.width) + ":" + Math.round(imagePreviewItem.height);
+ tdLibWrapper.getMapThumbnailFile(chatId, locationData.latitude, locationData.longitude, Math.round(imagePreviewItem.width), Math.round(imagePreviewItem.height), fileExtra);
}
}
Connections {
target: tdLibWrapper
onFileUpdated: {
- // we do not have a way of knowing if this is the correct file, so we have to guess the first new one should be right.
+ if(fileInformation["@extra"] !== imagePreviewItem.fileExtra) {
+ return;
+ }
+
if(!imagePreviewItem.pictureFileInformation) {
imagePreviewItem.pictureFileInformation = fileInformation;
tdLibWrapper.downloadFile(imagePreviewItem.pictureFileInformation.id);
diff --git a/qml/components/MessageListViewItem.qml b/qml/components/MessageListViewItem.qml
index bc306e7..259caa2 100644
--- a/qml/components/MessageListViewItem.qml
+++ b/qml/components/MessageListViewItem.qml
@@ -241,7 +241,7 @@ ListItem {
anchors.fill: parent
enabled: !(messageListItem.precalculatedValues.pageIsSelecting || messageListItem.isAnonymous)
onClicked: {
- tdLibWrapper.createPrivateChat(messageListItem.userInformation.id);
+ tdLibWrapper.createPrivateChat(messageListItem.userInformation.id, "openDirectly");
}
}
}
@@ -299,11 +299,15 @@ ListItem {
anchors.fill: parent
enabled: !(messageListItem.precalculatedValues.pageIsSelecting || messageListItem.isAnonymous)
onClicked: {
- tdLibWrapper.createPrivateChat(messageListItem.userInformation.id);
+ tdLibWrapper.createPrivateChat(messageListItem.userInformation.id, "openDirectly");
}
}
}
+ MessageViaLabel {
+ message: myMessage
+ }
+
Loader {
id: messageInReplyToLoader
active: myMessage.reply_to_message_id !== 0
diff --git a/qml/components/MessageListViewItemSimple.qml b/qml/components/MessageListViewItemSimple.qml
index c893488..e36416c 100644
--- a/qml/components/MessageListViewItemSimple.qml
+++ b/qml/components/MessageListViewItemSimple.qml
@@ -20,6 +20,7 @@ import QtQuick 2.6
import Sailfish.Silica 1.0
import "../js/twemoji.js" as Emoji
import "../js/functions.js" as Functions
+import "../js/debug.js" as Debug
Item {
id: messageListItem
@@ -27,6 +28,7 @@ Item {
property bool senderIsUser: myMessage.sender["@type"] === "messageSenderUser"
property var userInformation: senderIsUser ? tdLibWrapper.getUserInformation(myMessage.sender.user_id) : null
property bool isOwnMessage: senderIsUser && chatPage.myUserId === myMessage.sender.user_id
+ property var linkedMessage
height: backgroundRectangle.height + Theme.paddingMedium
Rectangle {
@@ -44,14 +46,43 @@ Item {
color: Theme.highlightColor
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Theme.fontSizeExtraSmall
+ property string messageContentText: Functions.getMessageText(messageListItem.myMessage, false, chatPage.myUserId, false)
text: (messageListItem.senderIsUser
? "" + (!messageListItem.isOwnMessage ? Emoji.emojify(Functions.getUserName(messageListItem.userInformation), font.pixelSize) : qsTr("You")) + " "
: "" + Emoji.emojify(chatPage.chatInformation.title || "") + " ")
- + Emoji.emojify(Functions.getMessageText(messageListItem.myMessage, false, chatPage.myUserId, false), font.pixelSize)
+ + Emoji.emojify(messageContentText, font.pixelSize)
textFormat: Text.RichText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
onLinkActivated: {
- Functions.handleLink(link);
+ if(link === "linkedmessage" && linkedMessage) {
+ messageOverlayLoader.overlayMessage = linkedMessage;
+ messageOverlayLoader.active = true;
+ } else {
+ Functions.handleLink(link);
+ }
+
+ }
+ }
+ Loader {
+ id: gameScoreInfoLoader
+ active: myMessage.content["@type"] === "messageGameScore"
+ asynchronous: true
+ sourceComponent: Component {
+ Connections {
+ target: tdLibWrapper
+ onReceivedMessage: {
+ if(chatId === chatPage.chatInformation.id && messageId === myMessage.content.game_message_id) {
+ messageListItem.linkedMessage = message;
+ messageText.messageContentText = messageListItem.isOwnMessage ?
+ qsTr("scored %Ln points in %2", "myself", myMessage.content.score).arg(""+message.content.game.title+"") :
+
+ qsTr("scored %Ln points in %2", "", myMessage.content.score).arg(""+message.content.game.title+"");
+ }
+ }
+ Component.onCompleted: {
+ tdLibWrapper.getMessage(chatPage.chatInformation.id, myMessage.content.game_message_id);
+ }
+ }
}
}
}
diff --git a/qml/components/MessageOverlayFlickable.qml b/qml/components/MessageOverlayFlickable.qml
index b3db83d..f26ba00 100644
--- a/qml/components/MessageOverlayFlickable.qml
+++ b/qml/components/MessageOverlayFlickable.qml
@@ -18,9 +18,9 @@
*/
import QtQuick 2.6
import Sailfish.Silica 1.0
-import "../components"
import "../js/functions.js" as Functions
import "../js/twemoji.js" as Emoji
+import "../js/debug.js" as Debug
Flickable {
id: messageOverlayFlickable
@@ -124,6 +124,10 @@ Flickable {
}
}
+ MessageViaLabel {
+ message: overlayMessage
+ }
+
Text {
id: overlayForwardedInfoText
width: parent.width
@@ -179,6 +183,16 @@ Flickable {
asynchronous: true
}
+ Loader {
+ id: replyMarkupLoader
+ property var myMessage: overlayMessage
+ width: parent.width
+ height: active ? (overlayMessage.reply_markup.rows.length * (Theme.itemSizeSmall + Theme.paddingSmall) - Theme.paddingSmall) : 0
+ asynchronous: true
+ active: !!overlayMessage.reply_markup && myMessage.reply_markup.rows
+ source: Qt.resolvedUrl("ReplyMarkupButtons.qml")
+ }
+
Timer {
id: messageDateUpdater
interval: 60000
diff --git a/qml/components/MessageViaLabel.qml b/qml/components/MessageViaLabel.qml
new file mode 100644
index 0000000..b76619e
--- /dev/null
+++ b/qml/components/MessageViaLabel.qml
@@ -0,0 +1,49 @@
+/*
+ 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 "../js/functions.js" as Functions
+import "../js/twemoji.js" as Emoji
+
+
+Loader {
+ id: botUserLoader
+ active: !!message.via_bot_user_id
+ width: parent.width
+ asynchronous: true
+ sourceComponent: Label {
+ property var botUserInformation: tdLibWrapper.getUserInformation(message.via_bot_user_id)
+ color: Theme.secondaryColor
+ font.pixelSize: Theme.fontSizeExtraSmall
+ text: qsTr("via %1", "message posted via bot user").arg("@" + Emoji.emojify(botUserInformation.username, font.pixelSize)+"")
+ textFormat: Text.RichText
+ truncationMode: TruncationMode.Fade
+ onLinkActivated: {
+ if(link === "userId://" + message.via_bot_user_id && botUserInformation.type.is_inline) {
+ newMessageTextField.text = "@"+botUserInformation.username+" "
+ newMessageTextField.cursorPosition = newMessageTextField.text.length
+ lostFocusTimer.start();
+ }
+ else {
+ Functions.handleLink(link);
+ }
+ }
+ }
+ property var message
+}
diff --git a/qml/components/ReplyMarkupButtons.qml b/qml/components/ReplyMarkupButtons.qml
index 02d9c7f..de2cd2c 100644
--- a/qml/components/ReplyMarkupButtons.qml
+++ b/qml/components/ReplyMarkupButtons.qml
@@ -23,6 +23,7 @@ import "../js/functions.js" as Functions
import "../js/debug.js" as Debug
Column {
+ id: replyMarkupButtons
width: parent.width
height: childrenRect.height
spacing: Theme.paddingSmall
@@ -36,7 +37,7 @@ Column {
Repeater {
id: buttonsRepeater
model: modelData
- property int itemWidth:precalculatedValues.textColumnWidth / count
+ property int itemWidth: replyMarkupButtons.width / count
delegate: MouseArea {
/*
Unimplemented callback types:
@@ -48,15 +49,35 @@ Column {
*/
property var callbacks: ({
inlineKeyboardButtonTypeCallback: function(){
- tdLibWrapper.getCallbackQueryAnswer(messageListItem.chatId, messageListItem.messageId, {data: modelData.type.data, "@type": "callbackQueryPayloadData"})
+ tdLibWrapper.getCallbackQueryAnswer(myMessage.chat_id, myMessage.id, {data: modelData.type.data, "@type": "callbackQueryPayloadData"})
},
+
+ inlineKeyboardButtonTypeCallbackGame: function(){
+ tdLibWrapper.getCallbackQueryAnswer(myMessage.chat_id, myMessage.id, {game_short_name: myMessage.content.game.short_name, "@type": "callbackQueryPayloadGame"})
+ },
inlineKeyboardButtonTypeUrl: function() {
Functions.handleLink(modelData.type.url);
+ },
+ inlineKeyboardButtonTypeSwitchInline: function() {
+ if(modelData.type.in_current_chat) {
+ chatPage.setMessageText("@" + userInformation.username + " "+(modelData.type.query || ""))
+ } else {
+
+ pageStack.push(Qt.resolvedUrl("../pages/ChatSelectionPage.qml"), {
+ myUserId: chatPage.myUserId,
+ payload: { neededPermissions: ["can_send_other_messages"], text:"@" + userInformation.username + " "+(modelData.type.query || "")},
+ state: "fillTextArea"
+ })
+ }
+ },
+
+ keyboardButtonTypeText: function() {
+ chatPage.setMessageText(modelData.text, true);
}
})
enabled: !!callbacks[modelData.type["@type"]]
height: Theme.itemSizeSmall
- width: (precalculatedValues.textColumnWidth + Theme.paddingSmall) / buttonsRepeater.count - (Theme.paddingSmall)
+ width: (replyMarkupButtons.width + Theme.paddingSmall) / buttonsRepeater.count - (Theme.paddingSmall)
onClicked: {
callbacks[modelData.type["@type"]]();
}
@@ -71,17 +92,18 @@ Column {
width: Math.min(parent.width - Theme.paddingSmall*2, contentWidth)
truncationMode: TruncationMode.Fade
text: Emoji.emojify(modelData.text, Theme.fontSizeSmall)
- color: parent.pressed ? Theme.highlightColor : Theme.primaryColor
+ color: parent.parent.pressed ? Theme.highlightColor : Theme.primaryColor
anchors.centerIn: parent
font.pixelSize: Theme.fontSizeSmall
}
Icon {
property var sources: ({
inlineKeyboardButtonTypeUrl: "../../images/icon-s-link.svg",
- inlineKeyboardButtonTypeSwitchInline: "image://theme/icon-s-repost",
+ inlineKeyboardButtonTypeSwitchInline: !modelData.type.in_current_chat ? "image://theme/icon-s-repost" : "image://theme/icon-s-edit",
inlineKeyboardButtonTypeCallbackWithPassword: "image://theme/icon-s-asterisk"
})
visible: !!sources[modelData.type["@type"]]
+ opacity: 0.6
source: sources[modelData.type["@type"]] || ""
sourceSize: Qt.size(Theme.iconSizeSmall, Theme.iconSizeSmall)
highlighted: parent.pressed
diff --git a/qml/components/chatInformationPage/ChatInformationPageContent.qml b/qml/components/chatInformationPage/ChatInformationPageContent.qml
index a1f29a6..021377f 100644
--- a/qml/components/chatInformationPage/ChatInformationPageContent.qml
+++ b/qml/components/chatInformationPage/ChatInformationPageContent.qml
@@ -232,7 +232,7 @@ SilicaFlickable {
MenuItem {
visible: chatInformationPage.isPrivateChat
onClicked: {
- tdLibWrapper.createNewSecretChat(chatInformationPage.chatPartnerGroupId);
+ tdLibWrapper.createNewSecretChat(chatInformationPage.chatPartnerGroupId, "openDirectly");
}
text: qsTr("New Secret Chat")
}
diff --git a/qml/components/chatInformationPage/ChatInformationTabItemMembersGroups.qml b/qml/components/chatInformationPage/ChatInformationTabItemMembersGroups.qml
index 79a72db..85e45d7 100644
--- a/qml/components/chatInformationPage/ChatInformationTabItemMembersGroups.qml
+++ b/qml/components/chatInformationPage/ChatInformationTabItemMembersGroups.qml
@@ -92,7 +92,7 @@ ChatInformationTabItemBase {
}
onClicked: {
- tdLibWrapper.createPrivateChat(user_id);
+ tdLibWrapper.createPrivateChat(user_id, "openDirectly");
}
}
footer: Component {
@@ -162,7 +162,7 @@ ChatInformationTabItemBase {
interval: 600
property int fetchLimit: 50
onTriggered: {
- if(chatInformationPage.isSuperGroup && !chatInformationPage.isChannel && (chatInformationPage.groupInformation.member_count > membersView.count)) { //
+ if(chatInformationPage.isSuperGroup && (!chatInformationPage.isChannel || chatInformationPage.canGetMembers) && (chatInformationPage.groupInformation.member_count > membersView.count)) {
tabBase.loading = true
tdLibWrapper.getSupergroupMembers(chatInformationPage.chatPartnerGroupId, fetchLimit, pageContent.membersList.count);
fetchLimit = 200
diff --git a/qml/components/inlineQueryResults/InlineQueryResult.qml b/qml/components/inlineQueryResults/InlineQueryResult.qml
new file mode 100644
index 0000000..784811d
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResult.qml
@@ -0,0 +1,33 @@
+/*
+ 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
+
+BackgroundItem {
+ id: queryResultItem
+
+ function sendInlineQueryResultMessage() {
+ tdLibWrapper.sendInlineQueryResultMessage(inlineQueryLoader.chatId, 0, 0, inlineQueryComponent.inlineQueryId, model.id);
+ inlineQueryLoader.textField.text = "";
+ }
+ onClicked: {
+ sendInlineQueryResultMessage()
+ }
+
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultAnimation.qml b/qml/components/inlineQueryResults/InlineQueryResultAnimation.qml
new file mode 100644
index 0000000..85bbeb1
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultAnimation.qml
@@ -0,0 +1,291 @@
+/*
+ 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 QtMultimedia 5.6
+import WerkWolf.Fernschreiber 1.0
+import QtGraphicalEffects 1.0
+import Nemo.Thumbnailer 1.0
+import "../"
+import "../../js/twemoji.js" as Emoji
+import "../../js/debug.js" as Debug
+
+InlineQueryResult {
+ id: queryResultItem
+ property bool isAnimation: true
+ property bool loopPreview: isAnimation
+ property bool mutePreview: isAnimation
+ enabled: false // don't send on click
+ layer.enabled: mouseArea.pressed
+ layer.effect: PressEffect { source: queryResultItem }
+
+ property string animationKey: "animation"
+ property bool hasThumbnail: !!model[queryResultItem.animationKey].thumbnail
+
+ property string videoMimeType: "video/mp4"
+
+ TDLibFile {
+ id: file
+ tdlib: tdLibWrapper
+ autoLoad: true
+ fileInformation: hasThumbnail ? model[queryResultItem.animationKey].thumbnail.file : (queryResultItem.isAnimation ? model[queryResultItem.animationKey].animation : model[queryResultItem.animationKey].video)
+ }
+
+ Image {
+ id: miniThumbnail
+ asynchronous: true
+ source: model[queryResultItem.animationKey].minithumbnail ? "data:image/jpg;base64,"+model[queryResultItem.animationKey].minithumbnail.data : ""
+ anchors.fill: parent
+ fillMode: Image.PreserveAspectCrop
+ layer.enabled: queryResultItem.pressed
+ layer.effect: PressEffect { source: miniThumbnail }
+ }
+ Component {
+ id: videoThumbnail
+ Thumbnail {
+ id: thumbnail
+ source: file.path
+ sourceSize.width: width
+ sourceSize.height: height
+ mimeType: queryResultItem.videoMimeType
+ layer.enabled: queryResultItem.pressed
+ layer.effect: PressEffect { source: thumbnail }
+ opacity: status === Thumbnail.Ready ? 1.0 : 0.0
+ Behavior on opacity { FadeAnimation {} }
+ }
+ }
+ Component {
+ id: imageThumbnail
+ Image {
+ id: thumbnail
+ source: file.path
+ sourceSize.width: width
+ sourceSize.height: height
+ layer.enabled: queryResultItem.pressed
+ layer.effect: PressEffect { source: thumbnail }
+ fillMode: Image.PreserveAspectCrop
+ opacity: status === Image.Ready ? 1.0 : 0.0
+ Behavior on opacity { FadeAnimation {} }
+ onStatusChanged: {
+ // we don't get many hints what may be wrong, so we guess it may be a webp image ;)
+ if(status === Image.Error) {
+ Debug.log("Inline Query Video: Thumbnail invalid. Blindly trying webp, which might work.")
+ queryResultItem.videoMimeType = "image/webp";
+ thumbnailLoader.sourceComponent = videoThumbnail;
+ }
+ }
+ }
+ }
+ Loader {
+ id: thumbnailLoader
+ asynchronous: true
+ active: file.isDownloadingCompleted
+ anchors.fill: parent
+ sourceComponent: queryResultItem.hasThumbnail ? (model[queryResultItem.animationKey].thumbnail.format["@type"] === "thumbnailFormatMpeg4" ? videoThumbnail : imageThumbnail) : model[queryResultItem.animationKey].mime_type === "video/mp4" ? videoThumbnail : imageThumbnail
+ }
+ Column {
+ id: texts
+ anchors {
+ left: parent.left
+ margins: Theme.paddingSmall
+ right: parent.right
+ bottom: parent.bottom
+ }
+
+ Label {
+ id: titleLabel
+ width: parent.width
+ font.pixelSize: Theme.fontSizeTiny
+ horizontalAlignment: Text.AlignHCenter
+ wrapMode: Text.Wrap
+ visible: text.length > 0
+ text: Emoji.emojify(model.title || "", font.pixelSize);
+ }
+ Label {
+ id: descriptionLabel
+ width: parent.width
+ font.pixelSize: Theme.fontSizeTiny
+ horizontalAlignment: Text.AlignHCenter
+ wrapMode: Text.Wrap
+ visible: text.length > 0
+ text: Emoji.emojify(model.description || "", font.pixelSize);
+ }
+ }
+
+ Loader {
+ anchors.fill: texts
+ asynchronous: true
+ active: titleLabel.visible || descriptionLabel.visible
+ sourceComponent: Component {
+ DropShadow {
+ horizontalOffset: 0
+ verticalOffset: 0
+ radius: Theme.paddingSmall
+ spread: 0.5
+ samples: 17
+ color: Theme.overlayBackgroundColor
+ source: texts
+ }
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ onClicked: {
+ // dialog
+
+ var dialog = pageStack.push(dialogComponent,{})
+ dialog.accepted.connect(function() {
+ queryResultItem.sendInlineQueryResultMessage();
+ })
+ }
+ }
+ Component {
+ id: dialogComponent
+ Dialog {
+
+ TDLibFile {
+ id: previewFile
+ tdlib: tdLibWrapper
+ autoLoad: model[queryResultItem.animationKey].mime_type !== "text/html"
+ fileInformation: queryResultItem.isAnimation ? model[queryResultItem.animationKey].animation : model[queryResultItem.animationKey].video
+ }
+
+ DialogHeader { id: dialogHeader }
+
+ ProgressCircle {
+ value: previewFile.downloadedSize / previewFile.expectedSize
+ width: Theme.iconSizeMedium
+ height: Theme.iconSizeMedium
+ anchors.centerIn: parent
+ opacity: previewFile.isDownloadingActive ? 1.0 : 0.0
+ Behavior on opacity { FadeAnimation {} }
+ }
+ Column {
+ visible: !previewFile.autoLoad
+ spacing: Theme.paddingLarge
+ anchors {
+ left: parent.left
+ leftMargin: Theme.horizontalPageMargin
+ right: parent.right
+ rightMargin: Theme.horizontalPageMargin
+ verticalCenter: parent.verticalCenter
+ }
+
+ Label {
+ width: parent.width
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: Theme.secondaryHighlightColor
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ text: Emoji.emojify(model.title || "", font.pixelSize);
+ visible: text.length > 1
+ linkColor: Theme.primaryColor
+ }
+
+ Label {
+ width: parent.width
+ font.pixelSize: Theme.fontSizeLarge
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: Theme.highlightColor
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ text: ''+Emoji.emojify(previewFile.fileInformation.remote.id, font.pixelSize)+' '
+ linkColor: Theme.primaryColor
+ }
+
+ Label {
+ width: parent.width
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: Theme.secondaryHighlightColor
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ text: Emoji.emojify(model.description || "", font.pixelSize)
+ visible: text.length > 1
+ linkColor: Theme.secondaryColor
+ }
+ }
+
+
+ Loader {
+ id: videoLoader
+ anchors {
+ top: dialogHeader.bottom
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+ active: previewFile.isDownloadingCompleted
+ asynchronous: true
+ sourceComponent: Component {
+ Item {
+ Connections {
+ target: resultView
+ onRequestPlayback: {
+ if(previewVideo.playbackState === MediaPlayer.PlayingState && previewVideo.source !== playbackSource) {
+ previewVideo.pause()
+ }
+ }
+ }
+ Timer {
+ id: loopTimer
+ interval: 0
+ onTriggered: previewVideo.play()
+ }
+
+ Video {
+ id: previewVideo
+ source: previewFile.path
+ autoPlay: true
+ muted: queryResultItem.mutePreview
+ anchors.fill: parent
+
+ onStatusChanged: {
+ if (status == MediaPlayer.EndOfMedia) {
+ if(queryResultItem.loopPreview) {
+ loopTimer.start()
+ }
+ }
+ }
+ onPlaybackStateChanged: {
+ if(playbackState === MediaPlayer.PlayingState) {
+ resultView.requestPlayback(source);
+ }
+ }
+ layer.enabled: playPauseMouseArea.pressed
+ layer.effect: PressEffect { source: previewVideo }
+ }
+ MouseArea {
+ id: playPauseMouseArea
+ anchors.fill: parent
+ onClicked: {
+ if(previewVideo.playbackState === MediaPlayer.PlayingState) {
+ previewVideo.pause();
+ } else {
+ previewVideo.play();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultArticle.qml b/qml/components/inlineQueryResults/InlineQueryResultArticle.qml
new file mode 100644
index 0000000..d002362
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultArticle.qml
@@ -0,0 +1,43 @@
+/*
+ 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 WerkWolf.Fernschreiber 1.0
+import "../../js/twemoji.js" as Emoji
+
+InlineQueryResultDefaultBase {
+ id: queryResultItem
+
+ title: Emoji.emojify(model.title || "", titleLable.font.pixelSize)
+ description: Emoji.emojify(model.description || "", descriptionLabel.font.pixelSize)
+ descriptionLabel {
+ maximumLineCount: 3
+ wrapMode: extraText.length === 0 ? Text.Wrap : Text.NoWrap
+ }
+
+ extraText: model.url || ""
+ extraTextLabel.visible: !model.hide_url && extraText.length > 0
+
+ thumbnailFileInformation: model.thumbnail ? model.thumbnail.file : {}
+
+ icon.source: "image://theme/icon-m-link"
+ icon.visible: thumbnail.visible && thumbnail.opacity === 0
+
+ thumbnail.visible: model.thumbnail || !!model.url
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultAudio.qml b/qml/components/inlineQueryResults/InlineQueryResultAudio.qml
new file mode 100644
index 0000000..aced1a7
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultAudio.qml
@@ -0,0 +1,183 @@
+/*
+ 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 QtMultimedia 5.6
+import WerkWolf.Fernschreiber 1.0
+import "../"
+import "../../js/twemoji.js" as Emoji
+
+InlineQueryResult {
+ id: queryResultItem
+ property var resultData: model.audio || model.voice_note
+ property var audioData: resultData.audio || resultData.voice
+
+ enabled: false // don't send on click
+
+ Connections {
+ target: resultView
+ onRequestPlayback: {
+ if(audioPlayer.playbackState === Audio.PlayingState && audioPlayer.source !== playbackSource) {
+ audioPlayer.pause()
+ }
+ }
+ }
+
+ TDLibFile {
+ id: file
+ tdlib: tdLibWrapper
+ autoLoad: false
+ fileInformation: queryResultItem.audioData
+ }
+
+ TDLibFile {
+ id: thumbnail
+ tdlib: tdLibWrapper
+ autoLoad: true
+ fileInformation: queryResultItem.resultData.album_cover_thumbnail ? queryResultItem.resultData.album_cover_thumbnail.file : {}
+ }
+
+ Loader {
+ id: thumbnailLoader
+ asynchronous: true
+ active: thumbnail.isDownloadingCompleted
+ height: parent.height
+ width: height
+ opacity: item && item.status === Image.Ready ? 0.5 : 0.0
+ Behavior on opacity { FadeAnimation {} }
+ sourceComponent: Component {
+ Image {
+ id: thumbnailImage
+ source: thumbnail.path
+ sourceSize.width: width
+ sourceSize.height: height
+
+ layer.enabled: playPauseButton.pressed
+ layer.effect: PressEffect { source: thumbnailImage }
+ }
+ }
+ }
+
+ IconButton {
+ id: playPauseButton
+ anchors.centerIn: thumbnailLoader
+ icon {
+ asynchronous: true
+ source: audioPlayer.playbackState === Audio.PlayingState || (file.isDownloadingActive && audioPlayer.autoPlay) ? "image://theme/icon-m-pause": "image://theme/icon-m-play"
+ }
+ onClicked: {
+ if(!file.isDownloadingCompleted && !file.isDownloadingActive) {
+ file.load();
+ audioPlayer.autoPlay = true
+ } else if(file.isDownloadingActive) {
+ // cancel playback intent?
+ audioPlayer.autoPlay = false
+ } else if(file.isDownloadingCompleted) {
+ //playPause
+ if(audioPlayer.playbackState === Audio.PlayingState) {
+ audioPlayer.pause();
+ } else {
+ audioPlayer.play();
+ }
+ }
+ }
+ }
+ ProgressCircle {
+ value: file.downloadedSize / file.expectedSize
+ width: Theme.iconSizeMedium
+ height: Theme.iconSizeMedium
+ anchors.centerIn: playPauseButton
+ opacity: file.isDownloadingActive ? 1.0 : 0.0
+ Behavior on opacity { FadeAnimation {} }
+ }
+
+ Audio {
+ id: audioPlayer
+ source: file.isDownloadingCompleted ? file.path : ""
+ autoPlay: false
+ onPlaybackStateChanged: {
+ if(playbackState === Audio.PlayingState) {
+ resultView.requestPlayback(source);
+ }
+ }
+ }
+
+ Column {
+ anchors {
+ left: thumbnailLoader.right
+ leftMargin: Theme.paddingSmall
+ right: sendButton.left
+ verticalCenter: parent.verticalCenter
+ }
+
+ Label {
+ width: parent.width
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.highlightColor
+ text: Emoji.emojify(queryResultItem.resultData.performer || "", font.pixelSize)
+ visible: text.length > 0
+ truncationMode: TruncationMode.Fade
+ }
+ Label {
+ width: parent.width
+ font.pixelSize: Theme.fontSizeTiny
+ color: Theme.secondaryHighlightColor
+ text: Emoji.emojify(queryResultItem.resultData.title || model.title || "", font.pixelSize)
+ visible: text.length > 0
+ truncationMode: TruncationMode.Fade
+ }
+ Item {
+ height: sizeLabel.height
+ width: parent.width
+ Label {
+ id: durationLabel
+ font.pixelSize: Theme.fontSizeTiny
+ color: Theme.secondaryColor
+ text: (audioPlayer.position > 0 || audioPlayer.playbackState === Audio.PlayingState ? (Format.formatDuration(audioPlayer.position/1000, Formatter.DurationShort)+" / ") : "") + Format.formatDuration(queryResultItem.audioData.duration || (audioPlayer.duration/1000), Formatter.DurationShort)
+ visible: (queryResultItem.audioData.duration || (audioPlayer.duration/1000)) > 0
+ truncationMode: TruncationMode.Fade
+ }
+ Label {
+ id: sizeLabel
+ anchors.right: parent.right
+ font.pixelSize: Theme.fontSizeTiny
+ color: Theme.secondaryColor
+ text: Format.formatFileSize(file.expectedSize)
+ visible: file.expectedSize > 0
+ truncationMode: TruncationMode.Fade
+ }
+ }
+ }
+
+ IconButton {
+ id: sendButton
+ anchors {
+ right: parent.right
+ rightMargin: Theme.horizontalPageMargin
+ verticalCenter: parent.verticalCenter
+ }
+ icon {
+ asynchronous: true
+ source: "image://theme/icon-m-send"
+ }
+ onClicked: {
+ queryResultItem.sendInlineQueryResultMessage();
+ }
+ }
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultContact.qml b/qml/components/inlineQueryResults/InlineQueryResultContact.qml
new file mode 100644
index 0000000..dc05dfa
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultContact.qml
@@ -0,0 +1,39 @@
+/*
+ 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 WerkWolf.Fernschreiber 1.0
+import "../../js/twemoji.js" as Emoji
+
+InlineQueryResultDefaultBase {
+ id: queryResultItem
+ property string namesSeparator: model.contact.first_name && model.contact.last_name ? " " : ""
+
+ title: Emoji.emojify(model.contact.first_name + namesSeparator + model.contact.last_name || "", titleLable.font.pixelSize)
+ description: Emoji.emojify(model.contact.phone_number || "", descriptionLabel.font.pixelSize)
+
+ extraText: model.url || ""
+ extraTextLabel.visible: !model.hide_url && extraText.length > 0
+
+ thumbnailFileInformation: model.thumbnail ? model.thumbnail.file : {}
+
+ icon.source: "image://theme/icon-m-contact"
+ icon.visible: thumbnail.visible && thumbnail.opacity === 0
+
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultDefaultBase.qml b/qml/components/inlineQueryResults/InlineQueryResultDefaultBase.qml
new file mode 100644
index 0000000..4c389bc
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultDefaultBase.qml
@@ -0,0 +1,103 @@
+/*
+ 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 WerkWolf.Fernschreiber 1.0
+import "../"
+
+InlineQueryResult {
+ id: queryResultItem
+
+ property alias title: titleLabel.text
+ property alias titleLable: titleLabel
+
+ property alias description: descriptionLabel.text
+ property alias descriptionLabel: descriptionLabel
+
+ property alias extraText: extraTextLabel.text
+ property alias extraTextLabel: extraTextLabel
+
+ property alias thumbnailFileInformation: thumbnailFile.fileInformation
+ property alias thumbnail: thumbnail
+
+ property alias icon: icon
+
+
+ Image {
+ id: thumbnail
+ source: thumbnailFile.isDownloadingCompleted ? thumbnailFile.path : ""
+ fillMode: Image.PreserveAspectCrop
+ asynchronous: true
+ width: visible ? Theme.itemSizeLarge : 0
+ height: width
+ opacity: status === Image.Ready ? 1.0 : 0.0
+
+ Behavior on opacity { FadeAnimation {} }
+ layer.enabled: queryResultItem.pressed
+ layer.effect: PressEffect { source: thumbnail }
+
+ TDLibFile {
+ id: thumbnailFile
+ tdlib: tdLibWrapper
+ autoLoad: true
+ }
+ }
+ Icon {
+ id: icon
+ asynchronous: true
+ anchors.centerIn: thumbnail
+ Behavior on opacity { FadeAnimation {} }
+ }
+
+ Column {
+ anchors {
+ left: thumbnail.right
+ leftMargin: thumbnail.visible ? Theme.paddingLarge : Theme.horizontalPageMargin
+ right: parent.right
+ rightMargin: Theme.horizontalPageMargin
+ verticalCenter: parent.verticalCenter
+ }
+
+ Label {
+ id: titleLabel
+ width: parent.width
+ font.pixelSize: Theme.fontSizeSmall
+ color: highlighted || !queryResultItem.enabled ? Theme.highlightColor : Theme.primaryColor
+ visible: text.length > 0
+ truncationMode: TruncationMode.Fade
+ }
+ Label {
+ id: descriptionLabel
+ width: parent.width
+ font.pixelSize: Theme.fontSizeTiny
+ color: highlighted || !queryResultItem.enabled ? Theme.secondaryColor : Theme.secondaryHighlightColor
+ visible: text.length > 0
+ truncationMode: TruncationMode.Fade
+ }
+
+ Label {
+ id: extraTextLabel
+ width: parent.width
+ font.pixelSize: Theme.fontSizeTiny
+ color: highlighted || !queryResultItem.enabled ? Theme.secondaryHighlightColor : Theme.secondaryColor
+ visible: text.length > 0
+ truncationMode: TruncationMode.Fade
+ }
+ }
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultDocument.qml b/qml/components/inlineQueryResults/InlineQueryResultDocument.qml
new file mode 100644
index 0000000..13e1238
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultDocument.qml
@@ -0,0 +1,49 @@
+/*
+ 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 WerkWolf.Fernschreiber 1.0
+import "../"
+import "../../js/twemoji.js" as Emoji
+
+Loader {
+ Component {
+ id: documentComponent
+ InlineQueryResultDefaultBase {
+ id: queryResultItem
+
+ title: Emoji.emojify(model.title || model.document.file_name || "", titleLable.font.pixelSize)
+ description: Emoji.emojify(model.description || model.document.file_name || "", descriptionLabel.font.pixelSize)
+ extraText: Format.formatFileSize(model.document.document.expected_size)
+
+ thumbnailFileInformation: model.thumbnail ? model.thumbnail.file : {}
+
+ icon.source: Theme.iconForMimeType(model.document.mime_type)
+ icon.visible: thumbnail.visible && thumbnail.opacity === 0
+ }
+ }
+ Component {
+ id: voiceNoteDocumentComponent
+ InlineQueryResultVoiceNote {
+ resultData: model.document
+ audioData: model.document.document
+ }
+ }
+ sourceComponent: model.document.mime_type === "audio/ogg" ? voiceNoteDocumentComponent : documentComponent
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultGame.qml b/qml/components/inlineQueryResults/InlineQueryResultGame.qml
new file mode 100644
index 0000000..75de557
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultGame.qml
@@ -0,0 +1,53 @@
+/*
+ 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 WerkWolf.Fernschreiber 1.0
+import "../../js/twemoji.js" as Emoji
+
+InlineQueryResultDefaultBase {
+ id: queryResultItem
+
+ title: Emoji.emojify(model.game.title || "", titleLable.font.pixelSize)
+ description: Emoji.emojify(model.game.description || "", descriptionLabel.font.pixelSize)
+ descriptionLabel {
+ maximumLineCount: 3
+ wrapMode: Text.Wrap
+ }
+
+ icon.source: "image://theme/icon-m-game-controller"
+ icon.visible: thumbnail.opacity === 0
+
+
+ Component.onCompleted: {
+ if (model.game.photo) {
+ // Check first which size fits best...
+ var photo
+ for (var i = 0; i < model.game.photo.sizes.length; i++) {
+ photo = model.game.photo.sizes[i].photo
+ if (model.game.photo.sizes[i].width >= queryResultItem.width) {
+ break
+ }
+ }
+ if (photo) {
+ thumbnailFileInformation = photo
+ }
+ }
+ }
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultLocation.qml b/qml/components/inlineQueryResults/InlineQueryResultLocation.qml
new file mode 100644
index 0000000..07219c4
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultLocation.qml
@@ -0,0 +1,23 @@
+/*
+ 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
+
+InlineQueryResultVenue {
+ resultData: model
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultPhoto.qml b/qml/components/inlineQueryResults/InlineQueryResultPhoto.qml
new file mode 100644
index 0000000..f278b76
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultPhoto.qml
@@ -0,0 +1,68 @@
+/*
+ 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 QtMultimedia 5.6
+import WerkWolf.Fernschreiber 1.0
+import "../"
+
+InlineQueryResult {
+ id: queryResultItem
+
+
+ TDLibFile {
+ id: file
+ tdlib: tdLibWrapper
+ autoLoad: true
+ }
+
+ Loader {
+ asynchronous: true
+ active: file.isDownloadingCompleted
+ anchors.fill: parent
+ opacity: item && item.status === Image.Ready ? 1.0 : 0.0
+ Behavior on opacity { FadeAnimation {} }
+ sourceComponent: Component {
+ Image {
+ id: image
+ source: file.path
+ asynchronous: true
+ clip: true
+ fillMode: Image.PreserveAspectCrop
+ layer.enabled: queryResultItem.pressed
+ layer.effect: PressEffect { source: image }
+ }
+ }
+ }
+ Component.onCompleted: {
+ if (model.photo) {
+ // Check first which size fits best...
+ var photo
+ for (var i = 0; i < model.photo.sizes.length; i++) {
+ photo = model.photo.sizes[i].photo
+ if (model.photo.sizes[i].width >= queryResultItem.width) {
+ break
+ }
+ }
+ if (photo) {
+ file.fileInformation = photo
+ }
+ }
+ }
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultSticker.qml b/qml/components/inlineQueryResults/InlineQueryResultSticker.qml
new file mode 100644
index 0000000..4d2ab03
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultSticker.qml
@@ -0,0 +1,106 @@
+/*
+ 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 WerkWolf.Fernschreiber 1.0
+import Nemo.Thumbnailer 1.0
+import "../"
+
+InlineQueryResult {
+ id: queryResultItem
+
+ property bool animate
+ property bool animating: animate && model.sticker.is_animated
+ property url stickerId: "http://sticker/" + model.sticker.sticker.remote.id
+ onAnimatingChanged: {
+ if(animating) {
+ resultView.requestPlayback(stickerId);
+ }
+ }
+
+ Connections {
+ target: resultView
+ onRequestPlayback: {
+ if(queryResultItem.animating && queryResultItem.stickerId !== playbackSource) {
+ animate = false
+ }
+ }
+ }
+
+ onPressAndHold: {
+ animate = !animate
+ }
+
+ TDLibFile {
+ id: file
+ tdlib: tdLibWrapper
+ fileInformation: model.sticker.sticker
+ autoLoad: true
+ }
+
+ Loader {
+ id: animatedStickerLoader
+ anchors {
+ fill: parent
+ margins: Theme.paddingLarge
+ }
+ active: queryResultItem.animating
+ sourceComponent: Component {
+ AnimatedImage {
+ id: animatedSticker
+ anchors.fill: parent
+ source: file.path
+ asynchronous: true
+ paused: !Qt.application.active
+ cache: false
+ layer.enabled: highlighted
+ layer.effect: PressEffect { source: animatedSticker }
+ }
+ }
+ }
+
+ Image {
+ id: staticSticker
+ anchors {
+ fill: parent
+ margins: Theme.paddingLarge
+ }
+ source: file.path
+ fillMode: Image.PreserveAspectFit
+ autoTransform: true
+ asynchronous: true
+ visible: !queryResultItem.animating && opacity > 0
+ opacity: status === Image.Ready ? 1 : 0
+ Behavior on opacity { FadeAnimation {} }
+ layer.enabled: queryResultItem.highlighted
+ layer.effect: PressEffect { source: staticSticker }
+ }
+
+ Icon {
+ source: "image://theme/icon-m-video"
+ width: Theme.iconSizeExtraSmall
+ height: width
+ visible: model.sticker.is_animated
+ highlighted: queryResultItem.highlighted || queryResultItem.animating
+ anchors {
+ right: parent.right
+ bottom: parent.bottom
+ }
+ }
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultVenue.qml b/qml/components/inlineQueryResults/InlineQueryResultVenue.qml
new file mode 100644
index 0000000..297f9c4
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultVenue.qml
@@ -0,0 +1,51 @@
+/*
+ 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 WerkWolf.Fernschreiber 1.0
+import "../../js/twemoji.js" as Emoji
+
+InlineQueryResultDefaultBase {
+ id: queryResultItem
+ property string locationId
+ property var resultData: model.venue
+
+ title: Emoji.emojify(queryResultItem.resultData.title || (queryResultItem.resultData.location.latitude + ":" + queryResultItem.resultData.location.longitude), titleLable.font.pixelSize)
+ description: Emoji.emojify(queryResultItem.resultData.address || "", descriptionLabel.font.pixelSize)
+ extraText: Emoji.emojify(queryResultItem.resultData.type || "", extraTextLabel.font.pixelSize)
+
+
+ Connections {
+ target: tdLibWrapper
+ onFileUpdated: {
+ if(fileInformation["@extra"] === queryResultItem.locationId) {
+ thumbnailFileInformation = fileInformation
+ }
+
+ }
+ }
+
+ Component.onCompleted: {
+ var dimensions = [ Math.round(thumbnail.width), Math.round(thumbnail.height)];
+
+ locationId = "location:" + resultData.location.latitude + ":" + resultData.location.longitude + ":" + dimensions[0] + ":" + dimensions[1];
+
+ tdLibWrapper.getMapThumbnailFile(chatId, resultData.location.latitude, resultData.location.longitude, dimensions[0], dimensions[1], locationId);
+ }
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultVideo.qml b/qml/components/inlineQueryResults/InlineQueryResultVideo.qml
new file mode 100644
index 0000000..7b71f2b
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultVideo.qml
@@ -0,0 +1,25 @@
+/*
+ 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
+
+InlineQueryResultAnimation {
+ isAnimation: false
+ animationKey: "video"
+}
diff --git a/qml/components/inlineQueryResults/InlineQueryResultVoiceNote.qml b/qml/components/inlineQueryResults/InlineQueryResultVoiceNote.qml
new file mode 100644
index 0000000..b78fc29
--- /dev/null
+++ b/qml/components/inlineQueryResults/InlineQueryResultVoiceNote.qml
@@ -0,0 +1,24 @@
+/*
+ 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
+
+InlineQueryResultAudio {
+
+}
diff --git a/qml/js/functions.js b/qml/js/functions.js
index 923f068..db2705f 100644
--- a/qml/js/functions.js
+++ b/qml/js/functions.js
@@ -145,6 +145,10 @@ function getMessageText(message, simple, currentUserId, ignoreEntities) {
return myself ? qsTr("sent a self-destructing video that is expired", "myself") : qsTr("sent a self-destructing video that is expired");
case 'messageScreenshotTaken':
return myself ? qsTr("created a screenshot in this chat", "myself") : qsTr("created a screenshot in this chat");
+ case 'messageGame':
+ return simple ? (myself ? qsTr("sent a game", "myself") : qsTr("sent a game")) : "";
+ case 'messageGameScore':
+ return myself ? qsTr("scored %Ln points", "myself", message.content.score) : qsTr("scored %Ln points", "myself", message.content.score);
case 'messageUnsupported':
return myself ? qsTr("sent an unsupported message", "myself") : qsTr("sent an unsupported message");
default:
@@ -361,16 +365,16 @@ function handleLink(link) {
if (typeof userInformation.id === "undefined") {
appNotification.show(qsTr("Unable to find user %1").arg(userName));
} else {
- tdLibWrapper.createPrivateChat(userInformation.id);
+ tdLibWrapper.createPrivateChat(userInformation.id, "openDirectly");
}
} else if (link.indexOf("userId://") === 0) {
- tdLibWrapper.createPrivateChat(link.substring(9));
+ tdLibWrapper.createPrivateChat(link.substring(9), "openDirectly");
} else if (link.indexOf("tg://") === 0) {
Debug.log("Special TG link: ", link);
if (link.indexOf("tg://join?invite=") === 0) {
tdLibWrapper.joinChatByInviteLink(tMePrefix + "joinchat/" + link.substring(17));
} else if (link.indexOf("tg://resolve?domain=") === 0) {
- tdLibWrapper.searchPublicChat(link.substring(20));
+ tdLibWrapper.searchPublicChat(link.substring(20), true);
}
} else if (link.indexOf("botCommand://") === 0) { // this gets returned to send on ChatPage
return link.substring(13);
@@ -383,7 +387,7 @@ function handleLink(link) {
// Fail with nice error message if it doesn't work
} else {
Debug.log("Search public chat: ", link.substring(tMePrefix.length));
- tdLibWrapper.searchPublicChat(link.substring(tMePrefix.length));
+ tdLibWrapper.searchPublicChat(link.substring(tMePrefix.length), true);
// Check responses for updateBasicGroup or updateSupergroup
// Fire createBasicGroupChat or createSupergroupChat
// Do the necessary stuff to open the chat
@@ -440,8 +444,10 @@ function getMessagesArrayText(messages) {
}
function handleErrorMessage(code, message) {
- if (code === 404) {
- // Silently ignore 404 Not Found messages (occur sometimes, without clear context...)
+ if (code === 404 || (code === 400 && message === "USERNAME_INVALID")) {
+ // Silently ignore
+ // - 404 Not Found messages (occur sometimes, without clear context...)
+ // - searchPublicChat messages for "invalid" inline queries
return;
}
if (message === "USER_ALREADY_PARTICIPANT") {
diff --git a/qml/pages/AboutPage.qml b/qml/pages/AboutPage.qml
index 87af291..70fbcce 100644
--- a/qml/pages/AboutPage.qml
+++ b/qml/pages/AboutPage.qml
@@ -25,6 +25,7 @@ Page {
id: aboutPage
allowedOrientations: Orientation.All
+ property bool isLoggedIn : false
property var userInformation : tdLibWrapper.getUserInformation();
SilicaFlickable {
@@ -156,7 +157,8 @@ Page {
}
Loader {
- active: !!aboutPage.userInformation.phone_number
+ id: userInformationLoader
+ active: isLoggedIn
width: parent.width
sourceComponent: Component {
Column {
@@ -196,6 +198,33 @@ Page {
horizontalCenter: parent.horizontalCenter
}
}
+ BackgroundItem {
+ width: parent.width
+
+ BackgroundItem {
+ id: logOutItem
+ width: parent.width
+ function showRemorseItem() {
+ remorse.execute(logOutItem, qsTr("Logged out"), function() {
+ tdLibWrapper.logout();
+ pageStack.pop();
+ });
+ }
+ RemorseItem {
+ id: remorse
+ }
+ Button {
+ id: logOutButton
+ text: qsTr("Log Out")
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ }
+ onClicked: {
+ logOutItem.showRemorseItem();
+ }
+ }
+ }
+ }
}
}
}
diff --git a/qml/pages/ChatInformationPage.qml b/qml/pages/ChatInformationPage.qml
index 18f6a2a..15ac664 100644
--- a/qml/pages/ChatInformationPage.qml
+++ b/qml/pages/ChatInformationPage.qml
@@ -38,6 +38,7 @@ Page {
property bool isBasicGroup: false
property bool isSuperGroup: false
property bool isChannel: false
+ readonly property bool canGetMembers: ("can_get_members" in groupFullInformation) && groupFullInformation.can_get_members
property string chatPartnerGroupId
@@ -69,8 +70,6 @@ Page {
}
}
-
-
Loader {
id: mainContentLoader
active: false
diff --git a/qml/pages/ChatPage.qml b/qml/pages/ChatPage.qml
index 4db6a78..1747aae 100644
--- a/qml/pages/ChatPage.qml
+++ b/qml/pages/ChatPage.qml
@@ -45,6 +45,7 @@ Page {
property bool isSuperGroup: false;
property bool isChannel: false;
property var chatPartnerInformation;
+ property var botInformation;
property var chatGroupInformation;
property int chatOnlineMemberCount: 0;
property var emojiProposals;
@@ -59,6 +60,8 @@ Page {
property var selectedMessages: []
readonly property bool isSelecting: selectedMessages.length > 0
readonly property bool canSendMessages: hasSendPrivilege("can_send_messages")
+ property bool doSendBotStartMessage
+ property string sendBotStartMessageParameter
states: [
State {
@@ -154,6 +157,9 @@ Page {
if (isSecretChat) {
tdLibWrapper.getSecretChat(chatInformation.type.secret_chat_id);
}
+ if(chatPartnerInformation.type["@type"] === "userTypeBot") {
+ tdLibWrapper.getUserFullInfo(chatPartnerInformation.id)
+ }
}
else if (isBasicGroup) {
chatGroupInformation = tdLibWrapper.getBasicGroup(chatInformation.type.basic_group_id);
@@ -172,6 +178,7 @@ Page {
}
tdLibWrapper.getChatPinnedMessage(chatInformation.id);
tdLibWrapper.toggleChatIsMarkedAsUnread(chatInformation.id, false);
+
}
function getMessageStatusText(message, listItemIndex, lastReadSentIndex, useElapsed) {
@@ -207,14 +214,13 @@ Page {
}
function clearAttachmentPreviewRow() {
- attachmentPreviewRow.visible = false;
attachmentPreviewRow.isPicture = false;
attachmentPreviewRow.isVideo = false;
attachmentPreviewRow.isDocument = false;
attachmentPreviewRow.isVoiceNote = false;
attachmentPreviewRow.isLocation = false;
- attachmentPreviewRow.fileProperties = {};
- attachmentPreviewRow.locationData = {};
+ attachmentPreviewRow.fileProperties = null;
+ attachmentPreviewRow.locationData = null;
attachmentPreviewRow.attachmentDescription = "";
fernschreiberUtils.stopGeoLocationUpdates();
}
@@ -286,6 +292,10 @@ Page {
}
function handleMessageTextReplacement(text, cursorPosition) {
+ if(!newMessageTextField.focus) {
+ return;
+ }
+
var wordBoundaries = getWordBoundaries(text, cursorPosition);
var currentWord = text.substring(wordBoundaries.beginIndex, wordBoundaries.endIndex);
@@ -300,6 +310,7 @@ Page {
} else {
knownUsersRepeater.model = undefined;
}
+
}
function replaceMessageText(text, cursorPosition, newText) {
@@ -310,6 +321,19 @@ Page {
newMessageTextField.cursorPosition = newIndex;
lostFocusTimer.start();
}
+
+ function setMessageText(text, doSend) {
+ if(doSend) {
+ tdLibWrapper.sendTextMessage(chatInformation.id, text, "0");
+ }
+ else {
+ newMessageTextField.text = text
+ newMessageTextField.cursorPosition = text.length
+ lostFocusTimer.start();
+ }
+
+ }
+
function forwardMessages(fromChatId, messageIds) {
forwardMessagesTimer.fromChatId = fromChatId;
forwardMessagesTimer.messageIds = messageIds;
@@ -402,6 +426,17 @@ Page {
switch(status) {
case PageStatus.Activating:
tdLibWrapper.openChat(chatInformation.id);
+ if(!chatPage.isInitialized) {
+ if(chatInformation.draft_message) {
+ if(chatInformation.draft_message && chatInformation.draft_message.input_message_text) {
+ newMessageTextField.text = chatInformation.draft_message.input_message_text.text.text;
+ if(chatInformation.draft_message.reply_to_message_id) {
+ tdLibWrapper.getMessage(chatInformation.id, chatInformation.draft_message.reply_to_message_id);
+ }
+ }
+ }
+ }
+
break;
case PageStatus.Active:
if (!chatPage.isInitialized) {
@@ -410,13 +445,8 @@ Page {
pageStack.pushAttached(Qt.resolvedUrl("ChatInformationPage.qml"), { "chatInformation" : chatInformation, "privateChatUserInformation": chatPartnerInformation, "groupInformation": chatGroupInformation, "chatOnlineMemberCount": chatOnlineMemberCount});
chatPage.isInitialized = true;
- if(chatInformation.draft_message) {
- if(chatInformation.draft_message && chatInformation.draft_message.input_message_text) {
- newMessageTextField.text = chatInformation.draft_message.input_message_text.text.text;
- if(chatInformation.draft_message.reply_to_message_id) {
- tdLibWrapper.getMessage(chatInformation.id, chatInformation.draft_message.reply_to_message_id);
- }
- }
+ if(doSendBotStartMessage) {
+ tdLibWrapper.sendBotStartMessage(chatInformation.id, chatInformation.id, sendBotStartMessageParameter, "")
}
}
break;
@@ -494,6 +524,25 @@ Page {
chatPage.isSecretChatReady = chatPage.secretChatDetails.state["@type"] === "secretChatStateReady";
}
}
+ onCallbackQueryAnswer: {
+ if(text.length > 0) { // ignore bool "alert", just show as notification:
+ appNotification.show(Emoji.emojify(text, Theme.fontSizeSmall));
+ }
+ if(url.length > 0) {
+ Functions.handleLink(url);
+ }
+ }
+ onUserFullInfoReceived: {
+ if(userFullInfo["@extra"] === chatPartnerInformation.id.toString()) {
+ chatPage.botInformation = userFullInfo;
+ }
+ }
+ onUserFullInfoUpdated: {
+
+ if(userId === chatPartnerInformation.id) {
+ chatPage.botInformation = userFullInfo;
+ }
+ }
}
Connections {
@@ -895,7 +944,7 @@ Page {
id: chatView
visible: !blurred
- property bool blurred: messageOverlayLoader.item || stickerPickerLoader.item || voiceNoteOverlayLoader.item
+ property bool blurred: messageOverlayLoader.item || stickerPickerLoader.item || voiceNoteOverlayLoader.item || inlineQuery.hasOverlay
anchors.fill: parent
opacity: chatPage.loading ? 0 : 1
@@ -975,6 +1024,38 @@ Page {
}
model: chatModel
+ header: Component {
+ Loader {
+ active: !!chatPage.botInformation
+ && !!chatPage.botInformation.bot_info && chatPage.botInformation.bot_info.description.length > 0
+ asynchronous: true
+ width: chatView.width
+ sourceComponent: Component {
+ Label {
+ id: botInfoLabel
+ topPadding: Theme.paddingLarge
+ bottomPadding: Theme.paddingLarge
+ leftPadding: Theme.horizontalPageMargin
+ rightPadding: Theme.horizontalPageMargin
+ text: Emoji.emojify(chatPage.botInformation.bot_info.description, font.pixelSize)
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.highlightColor
+ wrapMode: Text.Wrap
+ textFormat: Text.StyledText
+ horizontalAlignment: Text.AlignHCenter
+ onLinkActivated: {
+ var chatCommand = Functions.handleLink(link);
+ if(chatCommand) {
+ tdLibWrapper.sendTextMessage(chatInformation.id, chatCommand);
+ }
+ }
+ linkColor: Theme.primaryColor
+ visible: (text !== "")
+ }
+ }
+ }
+ }
+
readonly property var contentComponentNames: ({
messageSticker: "StickerPreview",
messagePhoto: "ImagePreview",
@@ -986,7 +1067,8 @@ Page {
messageDocument: "DocumentPreview",
messageLocation: "LocationPreview",
messageVenue: "LocationPreview",
- messagePoll: "PollPreview"
+ messagePoll: "PollPreview",
+ messageGame: "GamePreview"
})
function getContentComponentHeight(componentName, content, parentWidth) {
switch(componentName) {
@@ -1002,6 +1084,8 @@ Page {
return Theme.itemSizeSmall;
case "PollPreview":
return Theme.itemSizeSmall * (4 + content.poll.options);
+ case "GamePreview":
+ return parentWidth * 0.66666666 + Theme.itemSizeLarge; // 2 / 3;
}
}
@@ -1014,6 +1098,7 @@ Page {
"messageChatJoinByLink",
"messageChatSetTtl",
"messageChatUpgradeFrom",
+ "messageGameScore",
"messageChatUpgradeTo",
"messageCustomServiceAction",
"messagePinMessage",
@@ -1168,12 +1253,17 @@ Page {
}
}
+ InlineQuery {
+ id: inlineQuery
+ textField: newMessageTextField
+ chatId: chatInformation.id
+ }
}
Column {
id: newMessageColumn
spacing: Theme.paddingSmall
- topPadding: Theme.paddingSmall
+ topPadding: Theme.paddingSmall + inlineQuery.buttonPadding
anchors.horizontalCenter: parent.horizontalCenter
visible: height > 0
width: parent.width - ( 2 * Theme.horizontalPageMargin )
@@ -1212,8 +1302,8 @@ Page {
property bool isNeeded: false
width: chatPage.width
x: -Theme.horizontalPageMargin
- height: isNeeded ? attachmentOptionsRow.height : 0
- Behavior on height { SmoothedAnimation { duration: 200 } }
+ height: isNeeded && !inlineQuery.userNameIsValid ? attachmentOptionsRow.height : 0
+ Behavior on height { SmoothedAnimation { duration: 200 } }
visible: height > 0
contentHeight: attachmentOptionsRow.height
contentWidth: Math.max(width, attachmentOptionsRow.width)
@@ -1252,7 +1342,6 @@ Page {
Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
attachmentPreviewRow.isPicture = true;
- attachmentPreviewRow.visible = true;
controlSendButton();
})
}
@@ -1269,7 +1358,6 @@ Page {
Debug.log("Selected video: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
attachmentPreviewRow.isVideo = true;
- attachmentPreviewRow.visible = true;
controlSendButton();
})
}
@@ -1299,7 +1387,6 @@ Page {
Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
attachmentPreviewRow.isDocument = true;
- attachmentPreviewRow.visible = true;
controlSendButton();
})
}
@@ -1337,7 +1424,6 @@ Page {
attachmentOptionsFlickable.isNeeded = false;
attachmentPreviewRow.isLocation = true;
attachmentPreviewRow.attachmentDescription = qsTr("Location: Obtaining position...");
- attachmentPreviewRow.visible = true;
controlSendButton();
}
}
@@ -1348,7 +1434,7 @@ Page {
Row {
id: attachmentPreviewRow
- visible: false
+ visible: (!!locationData || !!fileProperties) && !inlineQuery.userNameIsValid
spacing: Theme.paddingMedium
width: parent.width
layoutDirection: Qt.RightToLeft
@@ -1359,8 +1445,8 @@ Page {
property bool isDocument: false;
property bool isVoiceNote: false;
property bool isLocation: false;
- property var locationData: ({});
- property var fileProperties:({});
+ property var locationData: null;
+ property var fileProperties: null;
property string attachmentDescription: "";
Connections {
@@ -1390,15 +1476,15 @@ Page {
sourceSize.height: height
fillMode: Thumbnail.PreserveAspectCrop
- mimeType: typeof attachmentPreviewRow.fileProperties !== "undefined" ? attachmentPreviewRow.fileProperties.mimeType || "" : ""
- source: typeof attachmentPreviewRow.fileProperties !== "undefined" ? attachmentPreviewRow.fileProperties.url || "" : ""
+ mimeType: !!attachmentPreviewRow.fileProperties ? attachmentPreviewRow.fileProperties.mimeType || "" : ""
+ source: !!attachmentPreviewRow.fileProperties ? attachmentPreviewRow.fileProperties.url || "" : ""
visible: attachmentPreviewRow.isPicture || attachmentPreviewRow.isVideo
}
Label {
id: attachmentPreviewText
font.pixelSize: Theme.fontSizeSmall
- text: ( attachmentPreviewRow.isVoiceNote || attachmentPreviewRow.isLocation ) ? attachmentPreviewRow.attachmentDescription : ( typeof attachmentPreviewRow.fileProperties !== "undefined" ? attachmentPreviewRow.fileProperties.fileName || "" : "" );
+ text: ( attachmentPreviewRow.isVoiceNote || attachmentPreviewRow.isLocation ) ? attachmentPreviewRow.attachmentDescription : ( !!attachmentPreviewRow.fileProperties ? attachmentPreviewRow.fileProperties.fileName || "" : "" );
anchors.verticalCenter: parent.verticalCenter
maximumLineCount: 1
@@ -1491,9 +1577,11 @@ Page {
id: atMentionColumn
width: parent.width
anchors.horizontalCenter: parent.horizontalCenter
- visible: knownUsersRepeater.count > 0 ? true : false
+ visible: opacity > 0
opacity: knownUsersRepeater.count > 0 ? 1 : 0
Behavior on opacity { NumberAnimation {} }
+ height: knownUsersRepeater.count > 0 ? childrenRect.height : 0
+ Behavior on height { SmoothedAnimation { duration: 200 } }
spacing: Theme.paddingMedium
Flickable {
@@ -1598,7 +1686,7 @@ Page {
TextArea {
id: newMessageTextField
- width: parent.width - attachmentIconButton.width - (newMessageSendButton.visible ? newMessageSendButton.width : 0)
+ width: parent.width - (attachmentIconButton.visible ? attachmentIconButton.width : 0) - (newMessageSendButton.visible ? newMessageSendButton.width : 0) - (cancelInlineQueryButton.visible ? cancelInlineQueryButton.width : 0)
height: Math.min(chatContainer.height / 3, implicitHeight)
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: Theme.fontSizeSmall
@@ -1617,7 +1705,7 @@ Page {
}
}
- EnterKey.enabled: !appSettings.sendByEnter || text.length
+ EnterKey.enabled: !inlineQuery.userNameIsValid && (!appSettings.sendByEnter || text.length)
EnterKey.iconSource: appSettings.sendByEnter ? "image://theme/icon-m-chat" : "image://theme/icon-m-enter"
onTextChanged: {
@@ -1632,6 +1720,7 @@ Page {
anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.paddingSmall
enabled: !attachmentPreviewRow.visible
+ visible: !inlineQuery.userNameIsValid
onClicked: {
if (attachmentOptionsFlickable.isNeeded) {
attachmentOptionsFlickable.isNeeded = false;
@@ -1648,7 +1737,7 @@ Page {
icon.source: "image://theme/icon-m-chat"
anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.paddingSmall
- visible: !appSettings.sendByEnter || attachmentPreviewRow.visible
+ visible: !inlineQuery.userNameIsValid && (!appSettings.sendByEnter || attachmentPreviewRow.visible)
enabled: false
onClicked: {
sendMessage();
@@ -1658,6 +1747,42 @@ Page {
}
}
}
+
+ Item {
+ width: cancelInlineQueryButton.width
+ height: cancelInlineQueryButton.height
+ visible: inlineQuery.userNameIsValid
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: Theme.paddingSmall
+
+ IconButton {
+ id: cancelInlineQueryButton
+ icon.source: "image://theme/icon-m-cancel"
+ visible: parent.visible
+ opacity: inlineQuery.isLoading ? 0.2 : 1
+ Behavior on opacity { FadeAnimation {} }
+ onClicked: {
+ if(inlineQuery.query !== "") {
+ newMessageTextField.text = "@" + inlineQuery.userName + " "
+ newMessageTextField.cursorPosition = newMessageTextField.text.length
+ lostFocusTimer.start();
+ } else {
+ newMessageTextField.text = ""
+ }
+ }
+ onPressAndHold: {
+ newMessageTextField.text = ""
+ }
+ }
+
+ BusyIndicator {
+ size: BusyIndicatorSize.Small
+ anchors.centerIn: parent
+ running: inlineQuery.isLoading
+ }
+ }
+
+
}
}
}
diff --git a/qml/pages/ChatSelectionPage.qml b/qml/pages/ChatSelectionPage.qml
index f75ab64..1e6262a 100644
--- a/qml/pages/ChatSelectionPage.qml
+++ b/qml/pages/ChatSelectionPage.qml
@@ -43,6 +43,9 @@ Dialog {
case "forwardMessages":
acceptDestinationInstance.forwardMessages(payload.fromChatId, payload.messageIds)
break;
+ case "fillTextArea": // ReplyMarkupButtons: inlineKeyboardButtonTypeSwitchInline
+ acceptDestinationInstance.setMessageText(payload.text)
+ break;
// future uses of chat selection can be processed here
}
}
@@ -75,7 +78,7 @@ Dialog {
QtObject {
property bool visible: false
Component.onCompleted: {
- if(chatSelectionPage.state === "forwardMessages") {
+ if(chatSelectionPage.state === "forwardMessages" || chatSelectionPage.state === "fillTextArea" ) {
var chatType = display.type['@type']
var chatGroupInformation
if(chatType === "chatTypePrivate" || chatType === "chatTypeSecret") {
@@ -126,6 +129,7 @@ Dialog {
var chat = tdLibWrapper.getChat(display.id);
switch(chatSelectionPage.state) {
case "forwardMessages":
+ case "fillTextArea":
chatSelectionPage.acceptDestinationProperties = { "chatInformation" : chat};
chatSelectionPage.acceptDestination = Qt.resolvedUrl("../pages/ChatPage.qml");
break;
diff --git a/qml/pages/NewChatPage.qml b/qml/pages/NewChatPage.qml
index b98f5fd..917d7ea 100644
--- a/qml/pages/NewChatPage.qml
+++ b/qml/pages/NewChatPage.qml
@@ -220,7 +220,7 @@ Page {
icon.source: "image://theme/icon-m-chat"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
- tdLibWrapper.createPrivateChat(display.id);
+ tdLibWrapper.createPrivateChat(display.id, "openDirectly");
}
}
@@ -257,7 +257,7 @@ Page {
MouseArea {
anchors.fill: parent
onClicked: {
- tdLibWrapper.createPrivateChat(display.id);
+ tdLibWrapper.createPrivateChat(display.id, "openDirectly");
}
onPressed: {
privateChatHighlightBackground.visible = true;
@@ -295,7 +295,7 @@ Page {
icon.source: "image://theme/icon-m-device-lock"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
- tdLibWrapper.createNewSecretChat(display.id);
+ tdLibWrapper.createNewSecretChat(display.id, "openDirectly");
}
}
@@ -331,7 +331,7 @@ Page {
MouseArea {
anchors.fill: parent
onClicked: {
- tdLibWrapper.createNewSecretChat(display.id);
+ tdLibWrapper.createNewSecretChat(display.id, "openDirectly");
}
onPressed: {
secretChatHighlightBackground.visible = true;
diff --git a/qml/pages/OverviewPage.qml b/qml/pages/OverviewPage.qml
index f9abc67..40a9bc1 100644
--- a/qml/pages/OverviewPage.qml
+++ b/qml/pages/OverviewPage.qml
@@ -31,13 +31,14 @@ Page {
property bool initializationCompleted: false;
property bool loading: true;
+ property bool logoutLoading: false;
property int authorizationState: TelegramAPI.Closed
property int connectionState: TelegramAPI.WaitingForNetwork
property int ownUserId;
property bool chatListCreated: false;
onStatusChanged: {
- if (status === PageStatus.Active && initializationCompleted && !chatListCreated) {
+ if (status === PageStatus.Active && initializationCompleted && !chatListCreated && !logoutLoading) {
updateContent();
}
}
@@ -151,7 +152,9 @@ Page {
case TelegramAPI.WaitCode:
case TelegramAPI.WaitPassword:
case TelegramAPI.WaitRegistration:
+ case TelegramAPI.AuthorizationStateClosed:
overviewPage.loading = false;
+ overviewPage.logoutLoading = false;
if(isOnInitialization) { // pageStack isn't ready on Component.onCompleted
openInitializationPageTimer.start()
} else {
@@ -159,10 +162,25 @@ Page {
}
break;
case TelegramAPI.AuthorizationReady:
+ loadingBusyIndicator.text = qsTr("Loading chat list...");
overviewPage.loading = false;
overviewPage.initializationCompleted = true;
overviewPage.updateContent();
break;
+ case TelegramAPI.AuthorizationStateLoggingOut:
+ if (logoutLoading) {
+ Debug.log("Resources cleared already");
+ return;
+ }
+ Debug.log("Logging out")
+ overviewPage.initializationCompleted = false;
+ overviewPage.loading = false;
+ chatListCreatedTimer.stop();
+ updateSecondaryContentTimer.stop();
+ loadingBusyIndicator.text = qsTr("Logging out")
+ overviewPage.logoutLoading = true;
+ chatListModel.reset();
+ break;
default:
// Nothing ;)
}
@@ -210,10 +228,17 @@ Page {
}
}
onChatReceived: {
- if(chat["@extra"] === "openDirectly") {
+ var openAndSendStartToBot = chat["@extra"].indexOf("openAndSendStartToBot:") === 0
+ if(chat["@extra"] === "openDirectly" || openAndSendStartToBot && chat.type["@type"] === "chatTypePrivate") {
pageStack.pop(overviewPage, PageStackAction.Immediate)
// if we get a new chat (no messages?), we can not use the provided data
- pageStack.push(Qt.resolvedUrl("../pages/ChatPage.qml"), { "chatInformation" : tdLibWrapper.getChat(chat.id) });
+ var chatinfo = tdLibWrapper.getChat(chat.id);
+ var options = { "chatInformation" : chatinfo }
+ if(openAndSendStartToBot) {
+ options.doSendBotStartMessage = true;
+ options.sendBotStartMessageParameter = chat["@extra"].substring(22);
+ }
+ pageStack.push(Qt.resolvedUrl("../pages/ChatPage.qml"), options);
}
}
onErrorReceived: {
@@ -247,7 +272,7 @@ Page {
}
MenuItem {
text: qsTr("About Fernschreiber")
- onClicked: pageStack.push(Qt.resolvedUrl("../pages/AboutPage.qml"))
+ onClicked: pageStack.push(Qt.resolvedUrl("../pages/AboutPage.qml"), {isLoggedIn : (overviewPage.authorizationState == TelegramAPI.AuthorizationReady)})
}
MenuItem {
text: qsTr("Settings")
@@ -324,7 +349,7 @@ Page {
right: parent.right
}
clip: true
- opacity: overviewPage.chatListCreated ? 1 : 0
+ opacity: (overviewPage.chatListCreated && !overviewPage.logoutLoading) ? 1 : 0
Behavior on opacity { FadeAnimation {} }
model: chatListModel
delegate: ChatListViewItem {
@@ -352,20 +377,13 @@ Page {
spacing: Theme.paddingMedium
anchors.verticalCenter: chatListView.verticalCenter
- opacity: overviewPage.chatListCreated ? 0 : 1
+ opacity: overviewPage.chatListCreated && !overviewPage.logoutLoading ? 0 : 1
Behavior on opacity { FadeAnimation {} }
- visible: !overviewPage.chatListCreated
+ visible: !overviewPage.chatListCreated || overviewPage.logoutLoading
- InfoLabel {
- id: loadingLabel
- text: qsTr("Loading chat list...")
- }
-
- BusyIndicator {
- id: loadingBusyIndicator
- anchors.horizontalCenter: parent.horizontalCenter
- running: !overviewPage.chatListCreated
- size: BusyIndicatorSize.Large
+ BusyLabel {
+ id: loadingBusyIndicator
+ running: true
}
}
}
diff --git a/qml/pages/SettingsPage.qml b/qml/pages/SettingsPage.qml
index b91691e..bc8d0c8 100644
--- a/qml/pages/SettingsPage.qml
+++ b/qml/pages/SettingsPage.qml
@@ -159,6 +159,20 @@ Page {
}
}
+ SectionHeader {
+ text: qsTr("Privacy")
+ }
+
+ TextSwitch {
+ checked: appSettings.allowInlineBotLocationAccess
+ text: qsTr("Allow sending Location to inline bots")
+ description: qsTr("Some inline bots request location data when using them")
+ automaticCheck: false
+ onClicked: {
+ appSettings.allowInlineBotLocationAccess = !checked
+ }
+ }
+
SectionHeader {
text: qsTr("Storage")
}
diff --git a/src/appsettings.cpp b/src/appsettings.cpp
index d9239a9..525f078 100644
--- a/src/appsettings.cpp
+++ b/src/appsettings.cpp
@@ -29,6 +29,7 @@ namespace {
const QString KEY_NOTIFICATION_TURNS_DISPLAY_ON("notificationTurnsDisplayOn");
const QString KEY_NOTIFICATION_FEEDBACK("notificationFeedback");
const QString KEY_STORAGE_OPTIMIZER("storageOptimizer");
+ const QString KEY_INLINEBOT_LOCATION_ACCESS("allowInlineBotLocationAccess");
const QString KEY_REMAINING_INTERACTION_HINTS("remainingInteractionHints");
const QString KEY_ONLINE_ONLY_MODE("onlineOnlyMode");
}
@@ -149,6 +150,21 @@ void AppSettings::setStorageOptimizer(bool enable)
}
}
+bool AppSettings::allowInlineBotLocationAccess() const
+{
+ return settings.value(KEY_INLINEBOT_LOCATION_ACCESS, false).toBool();
+}
+
+void AppSettings::setAllowInlineBotLocationAccess(bool enable)
+{
+
+ if (allowInlineBotLocationAccess() != enable) {
+ LOG(KEY_INLINEBOT_LOCATION_ACCESS << enable);
+ settings.setValue(KEY_INLINEBOT_LOCATION_ACCESS, enable);
+ emit allowInlineBotLocationAccessChanged();
+ }
+}
+
int AppSettings::remainingInteractionHints() const
{
return settings.value(KEY_REMAINING_INTERACTION_HINTS, 3).toInt();
diff --git a/src/appsettings.h b/src/appsettings.h
index 373d3df..d6135d2 100644
--- a/src/appsettings.h
+++ b/src/appsettings.h
@@ -31,6 +31,7 @@ class AppSettings : public QObject {
Q_PROPERTY(bool notificationTurnsDisplayOn READ notificationTurnsDisplayOn WRITE setNotificationTurnsDisplayOn NOTIFY notificationTurnsDisplayOnChanged)
Q_PROPERTY(NotificationFeedback notificationFeedback READ notificationFeedback WRITE setNotificationFeedback NOTIFY notificationFeedbackChanged)
Q_PROPERTY(bool storageOptimizer READ storageOptimizer WRITE setStorageOptimizer NOTIFY storageOptimizerChanged)
+ Q_PROPERTY(bool allowInlineBotLocationAccess READ allowInlineBotLocationAccess WRITE setAllowInlineBotLocationAccess NOTIFY allowInlineBotLocationAccessChanged)
Q_PROPERTY(int remainingInteractionHints READ remainingInteractionHints WRITE setRemainingInteractionHints NOTIFY remainingInteractionHintsChanged)
Q_PROPERTY(bool onlineOnlyMode READ onlineOnlyMode WRITE setOnlineOnlyMode NOTIFY onlineOnlyModeChanged)
@@ -69,6 +70,9 @@ public:
bool storageOptimizer() const;
void setStorageOptimizer(bool enable);
+ bool allowInlineBotLocationAccess() const;
+ void setAllowInlineBotLocationAccess(bool enable);
+
int remainingInteractionHints() const;
void setRemainingInteractionHints(int remainingHints);
@@ -84,6 +88,7 @@ signals:
void notificationTurnsDisplayOnChanged();
void notificationFeedbackChanged();
void storageOptimizerChanged();
+ void allowInlineBotLocationAccessChanged();
void remainingInteractionHintsChanged();
void onlineOnlyModeChanged();
diff --git a/src/chatlistmodel.cpp b/src/chatlistmodel.cpp
index f6de2be..1a796be 100644
--- a/src/chatlistmodel.cpp
+++ b/src/chatlistmodel.cpp
@@ -390,6 +390,12 @@ ChatListModel::~ChatListModel()
qDeleteAll(hiddenChats.values());
}
+void ChatListModel::reset()
+{
+ chatList.clear();
+ hiddenChats.clear();
+}
+
QHash ChatListModel::roleNames() const
{
QHash roles;
diff --git a/src/chatlistmodel.h b/src/chatlistmodel.h
index 91fd8a0..8c17d75 100644
--- a/src/chatlistmodel.h
+++ b/src/chatlistmodel.h
@@ -64,6 +64,8 @@ public:
Q_INVOKABLE void redrawModel();
Q_INVOKABLE QVariantMap get(int row);
Q_INVOKABLE QVariantMap getById(qlonglong chatId);
+ Q_INVOKABLE void reset();
+
Q_INVOKABLE void calculateUnreadState();
bool showAllChats() const;
diff --git a/src/fernschreiberutils.cpp b/src/fernschreiberutils.cpp
index 3237982..57a07e7 100644
--- a/src/fernschreiberutils.cpp
+++ b/src/fernschreiberutils.cpp
@@ -178,6 +178,13 @@ QString FernschreiberUtils::getMessageShortText(TDLibWrapper *tdLibWrapper, cons
if (contentType == "messageScreenshotTaken") {
return myself ? tr("created a screenshot in this chat", "myself") : tr("created a screenshot in this chat");
}
+ if (contentType == "messageGameScore") {
+ qint32 score = messageContent.value("score").toInt();
+ return myself ? tr("scored %Ln points", "myself", score) : tr("scored %Ln points", "", score);
+ }
+ if (contentType == "messageGame") {
+ return myself ? tr("sent a game", "myself") : tr("sent a game");
+ }
if (contentType == "messageUnsupported") {
return myself ? tr("sent an unsupported message", "myself") : tr("sent an unsupported message");
}
diff --git a/src/tdlibreceiver.cpp b/src/tdlibreceiver.cpp
index 045e27e..6a62d28 100644
--- a/src/tdlibreceiver.cpp
+++ b/src/tdlibreceiver.cpp
@@ -137,6 +137,8 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren
handlers.insert("updateMessageEdited", &TDLibReceiver::processUpdateMessageEdited);
handlers.insert("updateChatIsMarkedAsUnread", &TDLibReceiver::processUpdateChatIsMarkedAsUnread);
handlers.insert("updateChatDraftMessage", &TDLibReceiver::processUpdateChatDraftMessage);
+ handlers.insert("inlineQueryResults", &TDLibReceiver::processInlineQueryResults);
+ handlers.insert("callbackQueryAnswer", &TDLibReceiver::processCallbackQueryAnswer);
}
void TDLibReceiver::setActive(bool active)
@@ -596,3 +598,16 @@ void TDLibReceiver::processUpdateChatDraftMessage(const QVariantMap &receivedInf
LOG("Draft message was updated");
emit chatDraftMessageUpdated(receivedInformation.value(CHAT_ID).toLongLong(), receivedInformation.value("draft_message").toMap(), findChatPositionOrder(receivedInformation.value(POSITIONS).toList()));
}
+
+void TDLibReceiver::processInlineQueryResults(const QVariantMap &receivedInformation)
+{
+ LOG("Inline Query results");
+ emit inlineQueryResults(receivedInformation.value("inline_query_id").toString(), receivedInformation.value("next_offset").toString(), receivedInformation.value("results").toList(), receivedInformation.value("switch_pm_text").toString(), receivedInformation.value("switch_pm_parameter").toString(), receivedInformation.value(EXTRA).toString());
+}
+
+void TDLibReceiver::processCallbackQueryAnswer(const QVariantMap &receivedInformation)
+{
+
+ LOG("Callback Query answer");
+ emit callbackQueryAnswer(receivedInformation.value(TEXT).toString(), receivedInformation.value("alert").toBool(), receivedInformation.value("url").toString());
+}
diff --git a/src/tdlibreceiver.h b/src/tdlibreceiver.h
index 7eb8b99..4ffbe58 100644
--- a/src/tdlibreceiver.h
+++ b/src/tdlibreceiver.h
@@ -93,6 +93,8 @@ signals:
void contactsImported(const QVariantList &importerCount, const QVariantList &userIds);
void chatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread);
void chatDraftMessageUpdated(qlonglong chatId, const QVariantMap &draftMessage, const QString &order);
+ void inlineQueryResults(const QString &inlineQueryId, const QString &nextOffset, const QVariantList &results, const QString &switchPmText, const QString &switchPmParameter, const QString &extra);
+ void callbackQueryAnswer(const QString &text, bool alert, const QString &url);
private:
typedef void (TDLibReceiver::*Handler)(const QVariantMap &);
@@ -160,6 +162,8 @@ private:
void processImportedContacts(const QVariantMap &receivedInformation);
void processUpdateChatIsMarkedAsUnread(const QVariantMap &receivedInformation);
void processUpdateChatDraftMessage(const QVariantMap &receivedInformation);
+ void processInlineQueryResults(const QVariantMap &receivedInformation);
+ void processCallbackQueryAnswer(const QVariantMap &receivedInformation);
};
#endif // TDLIBRECEIVER_H
diff --git a/src/tdlibwrapper.cpp b/src/tdlibwrapper.cpp
index 80d8520..f8cdcb8 100644
--- a/src/tdlibwrapper.cpp
+++ b/src/tdlibwrapper.cpp
@@ -60,7 +60,9 @@ TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface,
this->mceInterface = mceInterface;
this->tdLibClient = td_json_client_create();
this->authorizationState = AuthorizationState::Closed;
- this->tdLibReceiver = new TDLibReceiver(this->tdLibClient, this);
+ this->isLoggingOut = false;
+
+ initializeTDLibReciever();
QString tdLibDatabaseDirectoryPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tdlib";
QDir tdLibDatabaseDirectory(tdLibDatabaseDirectoryPath);
@@ -75,6 +77,29 @@ TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface,
this->removeOpenWith();
}
+ connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList)));
+
+ connect(this->appSettings, SIGNAL(useOpenWithChanged()), this, SLOT(handleOpenWithChanged()));
+ connect(this->appSettings, SIGNAL(storageOptimizerChanged()), this, SLOT(handleStorageOptimizerChanged()));
+
+ this->setLogVerbosityLevel();
+ this->setOptionInteger("notification_group_count_max", 5);
+}
+
+TDLibWrapper::~TDLibWrapper()
+{
+ LOG("Destroying TD Lib...");
+ this->tdLibReceiver->setActive(false);
+ while (this->tdLibReceiver->isRunning()) {
+ QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
+ }
+ qDeleteAll(basicGroups.values());
+ qDeleteAll(superGroups.values());
+ td_json_client_destroy(this->tdLibClient);
+}
+
+void TDLibWrapper::initializeTDLibReciever() {
+ this->tdLibReceiver = new TDLibReceiver(this->tdLibClient, this);
connect(this->tdLibReceiver, SIGNAL(versionDetected(QString)), this, SLOT(handleVersionDetected(QString)));
connect(this->tdLibReceiver, SIGNAL(authorizationStateChanged(QString, QVariantMap)), this, SLOT(handleAuthorizationStateChanged(QString, QVariantMap)));
connect(this->tdLibReceiver, SIGNAL(optionUpdated(QString, QVariant)), this, SLOT(handleOptionUpdated(QString, QVariant)));
@@ -131,32 +156,18 @@ TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface,
connect(this->tdLibReceiver, SIGNAL(messageEditedUpdated(qlonglong, qlonglong, QVariantMap)), this, SIGNAL(messageEditedUpdated(qlonglong, qlonglong, QVariantMap)));
connect(this->tdLibReceiver, SIGNAL(chatIsMarkedAsUnreadUpdated(qlonglong, bool)), this, SIGNAL(chatIsMarkedAsUnreadUpdated(qlonglong, bool)));
connect(this->tdLibReceiver, SIGNAL(chatDraftMessageUpdated(qlonglong, QVariantMap, QString)), this, SIGNAL(chatDraftMessageUpdated(qlonglong, QVariantMap, QString)));
-
- connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList)));
-
- connect(this->appSettings, SIGNAL(useOpenWithChanged()), this, SLOT(handleOpenWithChanged()));
- connect(this->appSettings, SIGNAL(storageOptimizerChanged()), this, SLOT(handleStorageOptimizerChanged()));
+ connect(this->tdLibReceiver, SIGNAL(inlineQueryResults(QString, QString, QVariantList, QString, QString, QString)), this, SIGNAL(inlineQueryResults(QString, QString, QVariantList, QString, QString, QString)));
+ connect(this->tdLibReceiver, SIGNAL(callbackQueryAnswer(QString, bool, QString)), this, SIGNAL(callbackQueryAnswer(QString, bool, QString)));
this->tdLibReceiver->start();
-
- this->setLogVerbosityLevel();
- this->setOptionInteger("notification_group_count_max", 5);
-}
-
-TDLibWrapper::~TDLibWrapper()
-{
- LOG("Destroying TD Lib...");
- this->tdLibReceiver->setActive(false);
- while (this->tdLibReceiver->isRunning()) {
- QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
- }
- qDeleteAll(basicGroups.values());
- qDeleteAll(superGroups.values());
- td_json_client_destroy(this->tdLibClient);
}
void TDLibWrapper::sendRequest(const QVariantMap &requestObject)
{
+ if (this->isLoggingOut) {
+ LOG("Sending request to TD Lib skipped as logging out is in progress, object type name:" << requestObject.value(_TYPE).toString());
+ return;
+ }
LOG("Sending request to TD Lib, object type name:" << requestObject.value(_TYPE).toString());
QJsonDocument requestDocument = QJsonDocument::fromVariant(requestObject);
VERBOSE(requestDocument.toJson().constData());
@@ -224,6 +235,16 @@ void TDLibWrapper::registerUser(const QString &firstName, const QString &lastNam
this->sendRequest(requestObject);
}
+void TDLibWrapper::logout()
+{
+ LOG("Logging out");
+ QVariantMap requestObject;
+ requestObject.insert("@type", "logOut");
+ this->sendRequest(requestObject);
+ this->isLoggingOut = true;
+
+}
+
void TDLibWrapper::getChats()
{
LOG("Getting chats");
@@ -685,13 +706,12 @@ void TDLibWrapper::deleteMessages(const QString &chatId, const QVariantList mess
this->sendRequest(requestObject);
}
-void TDLibWrapper::getMapThumbnailFile(const QString &chatId, double latitude, double longitude, int width, int height)
+void TDLibWrapper::getMapThumbnailFile(const QString &chatId, double latitude, double longitude, int width, int height, const QString &extra)
{
LOG("Getting Map Thumbnail File" << chatId);
QVariantMap location;
location.insert("latitude", latitude);
location.insert("longitude", longitude);
-
// ensure dimensions are in bounds (16 - 1024)
int boundsWidth = std::min(std::max(width, 16), 1024);
int boundsHeight = std::min(std::max(height, 16), 1024);
@@ -704,6 +724,7 @@ void TDLibWrapper::getMapThumbnailFile(const QString &chatId, double latitude, d
requestObject.insert("height", boundsHeight);
requestObject.insert("scale", 1); // 1-3
requestObject.insert(CHAT_ID, chatId);
+ requestObject.insert(_EXTRA, extra);
this->sendRequest(requestObject);
}
@@ -769,43 +790,43 @@ void TDLibWrapper::getUserFullInfo(const QString &userId)
this->sendRequest(requestObject);
}
-void TDLibWrapper::createPrivateChat(const QString &userId)
+void TDLibWrapper::createPrivateChat(const QString &userId, const QString &extra)
{
LOG("Creating Private Chat");
QVariantMap requestObject;
requestObject.insert(_TYPE, "createPrivateChat");
requestObject.insert("user_id", userId);
- requestObject.insert(_EXTRA, "openDirectly"); //gets matched in qml
+ requestObject.insert(_EXTRA, extra); //"openDirectly"/"openAndSendStartToBot:[optional parameter]" gets matched in qml
this->sendRequest(requestObject);
}
-void TDLibWrapper::createNewSecretChat(const QString &userId)
+void TDLibWrapper::createNewSecretChat(const QString &userId, const QString &extra)
{
LOG("Creating new secret chat");
QVariantMap requestObject;
requestObject.insert(_TYPE, "createNewSecretChat");
requestObject.insert("user_id", userId);
- requestObject.insert(_EXTRA, "openDirectly"); //gets matched in qml
+ requestObject.insert(_EXTRA, extra); //"openDirectly" gets matched in qml
this->sendRequest(requestObject);
}
-void TDLibWrapper::createSupergroupChat(const QString &supergroupId)
+void TDLibWrapper::createSupergroupChat(const QString &supergroupId, const QString &extra)
{
LOG("Creating Supergroup Chat");
QVariantMap requestObject;
requestObject.insert(_TYPE, "createSupergroupChat");
requestObject.insert("supergroup_id", supergroupId);
- requestObject.insert(_EXTRA, "openDirectly"); //gets matched in qml
+ requestObject.insert(_EXTRA, extra); //"openDirectly" gets matched in qml
this->sendRequest(requestObject);
}
-void TDLibWrapper::createBasicGroupChat(const QString &basicGroupId)
+void TDLibWrapper::createBasicGroupChat(const QString &basicGroupId, const QString &extra)
{
LOG("Creating Basic Group Chat");
QVariantMap requestObject;
requestObject.insert(_TYPE, "createBasicGroupChat");
requestObject.insert("basic_group_id", basicGroupId);
- requestObject.insert(_EXTRA, "openDirectly"); //gets matched in qml
+ requestObject.insert(_EXTRA, extra); //"openDirectly"/"openAndSend:*" gets matched in qml
this->sendRequest(requestObject);
}
@@ -929,12 +950,15 @@ void TDLibWrapper::getPollVoters(const QString &chatId, qlonglong messageId, int
this->sendRequest(requestObject);
}
-void TDLibWrapper::searchPublicChat(const QString &userName)
+void TDLibWrapper::searchPublicChat(const QString &userName, bool doOpenOnFound)
{
LOG("Search public chat" << userName);
- this->activeChatSearchName = userName;
+ if(doOpenOnFound) {
+ this->activeChatSearchName = userName;
+ }
QVariantMap requestObject;
requestObject.insert(_TYPE, "searchPublicChat");
+ requestObject.insert(_EXTRA, "searchPublicChat:"+userName);
requestObject.insert(USERNAME, userName);
this->sendRequest(requestObject);
}
@@ -1075,6 +1099,53 @@ void TDLibWrapper::setChatDraftMessage(qlonglong chatId, qlonglong threadId, qlo
this->sendRequest(requestObject);
}
+void TDLibWrapper::getInlineQueryResults(qlonglong botUserId, qlonglong chatId, const QVariantMap &userLocation, const QString &query, const QString &offset, const QString &extra)
+{
+
+ LOG("Get Inline Query Results" << chatId << query);
+ QVariantMap requestObject;
+ requestObject.insert(_TYPE, "getInlineQueryResults");
+ requestObject.insert(CHAT_ID, chatId);
+ requestObject.insert("bot_user_id", botUserId);
+ if(!userLocation.isEmpty()) {
+ requestObject.insert("user_location", userLocation);
+ }
+ requestObject.insert("query", query);
+ requestObject.insert("offset", offset);
+ requestObject.insert(_EXTRA, extra);
+
+ this->sendRequest(requestObject);
+}
+
+void TDLibWrapper::sendInlineQueryResultMessage(qlonglong chatId, qlonglong threadId, qlonglong replyToMessageId, const QString &queryId, const QString &resultId)
+{
+
+ LOG("Send Inline Query Result Message" << chatId);
+ QVariantMap requestObject;
+ requestObject.insert(_TYPE, "sendInlineQueryResultMessage");
+ requestObject.insert(CHAT_ID, chatId);
+ requestObject.insert("message_thread_id", threadId);
+ requestObject.insert("reply_to_message_id", replyToMessageId);
+ requestObject.insert("query_id", queryId);
+ requestObject.insert("result_id", resultId);
+
+ this->sendRequest(requestObject);
+}
+
+void TDLibWrapper::sendBotStartMessage(qlonglong botUserId, qlonglong chatId, const QString ¶meter, const QString &extra)
+{
+
+ LOG("Send Bot Start Message" << botUserId << chatId << parameter << extra);
+ QVariantMap requestObject;
+ requestObject.insert(_TYPE, "sendBotStartMessage");
+ requestObject.insert("bot_user_id", botUserId);
+ requestObject.insert(CHAT_ID, chatId);
+ requestObject.insert("parameter", parameter);
+ requestObject.insert(_EXTRA, extra);
+
+ this->sendRequest(requestObject);
+}
+
void TDLibWrapper::searchEmoji(const QString &queryString)
{
LOG("Searching emoji" << queryString);
@@ -1266,6 +1337,28 @@ void TDLibWrapper::handleAuthorizationStateChanged(const QString &authorizationS
this->setInitialParameters();
this->authorizationState = AuthorizationState::WaitTdlibParameters;
}
+ if (authorizationState == "authorizationStateLoggingOut") {
+ this->authorizationState = AuthorizationState::AuthorizationStateLoggingOut;
+ }
+ if (authorizationState == "authorizationStateClosed") {
+ this->authorizationState = AuthorizationState::AuthorizationStateClosed;
+ LOG("Reloading TD Lib...");
+ this->basicGroups.clear();
+ this->superGroups.clear();
+ this->allUsers.clear();
+ this->allUserNames.clear();
+ this->tdLibReceiver->setActive(false);
+ while (this->tdLibReceiver->isRunning()) {
+ QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
+ }
+ td_json_client_destroy(this->tdLibClient);
+ this->tdLibReceiver->terminate();
+ QDir appPath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ appPath.removeRecursively();
+ this->tdLibClient = td_json_client_create();
+ initializeTDLibReciever();
+ this->isLoggingOut = false;
+ }
this->authorizationStateData = authorizationStateData;
emit authorizationStateChanged(this->authorizationState, this->authorizationStateData);
@@ -1349,12 +1442,12 @@ void TDLibWrapper::handleChatReceived(const QVariantMap &chatInformation)
if (receivedChatType == ChatTypeBasicGroup) {
LOG("Found basic group for active search" << this->activeChatSearchName);
this->activeChatSearchName.clear();
- this->createBasicGroupChat(chatType.value("basic_group_id").toString());
+ this->createBasicGroupChat(chatType.value("basic_group_id").toString(), "openDirectly");
}
if (receivedChatType == ChatTypeSupergroup) {
LOG("Found supergroup for active search" << this->activeChatSearchName);
this->activeChatSearchName.clear();
- this->createSupergroupChat(chatType.value("supergroup_id").toString());
+ this->createSupergroupChat(chatType.value("supergroup_id").toString(), "openDirectly");
}
}
}
@@ -1381,7 +1474,7 @@ void TDLibWrapper::handleBasicGroupUpdated(qlonglong groupId, const QVariantMap
if (!this->activeChatSearchName.isEmpty() && this->activeChatSearchName == groupInformation.value(USERNAME).toString()) {
LOG("Found basic group for active search" << this->activeChatSearchName);
this->activeChatSearchName.clear();
- this->createBasicGroupChat(groupInformation.value(ID).toString());
+ this->createBasicGroupChat(groupInformation.value(ID).toString(), "openDirectly");
}
}
@@ -1391,7 +1484,7 @@ void TDLibWrapper::handleSuperGroupUpdated(qlonglong groupId, const QVariantMap
if (!this->activeChatSearchName.isEmpty() && this->activeChatSearchName == groupInformation.value(USERNAME).toString()) {
LOG("Found supergroup for active search" << this->activeChatSearchName);
this->activeChatSearchName.clear();
- this->createSupergroupChat(groupInformation.value(ID).toString());
+ this->createSupergroupChat(groupInformation.value(ID).toString(), "openDirectly");
}
}
diff --git a/src/tdlibwrapper.h b/src/tdlibwrapper.h
index c07c761..914ccf6 100644
--- a/src/tdlibwrapper.h
+++ b/src/tdlibwrapper.h
@@ -46,7 +46,9 @@ public:
WaitPassword,
WaitPhoneNumber,
WaitRegistration,
- WaitTdlibParameters
+ WaitTdlibParameters,
+ AuthorizationStateClosed,
+ AuthorizationStateLoggingOut
};
Q_ENUM(AuthorizationState)
@@ -125,6 +127,7 @@ public:
Q_INVOKABLE void setAuthenticationCode(const QString &authenticationCode);
Q_INVOKABLE void setAuthenticationPassword(const QString &authenticationPassword);
Q_INVOKABLE void registerUser(const QString &firstName, const QString &lastName);
+ Q_INVOKABLE void logout();
Q_INVOKABLE void getChats();
Q_INVOKABLE void downloadFile(int fileId);
Q_INVOKABLE void openChat(const QString &chatId);
@@ -152,17 +155,17 @@ public:
Q_INVOKABLE void setChatNotificationSettings(const QString &chatId, const QVariantMap ¬ificationSettings);
Q_INVOKABLE void editMessageText(const QString &chatId, const QString &messageId, const QString &message);
Q_INVOKABLE void deleteMessages(const QString &chatId, const QVariantList messageIds);
- Q_INVOKABLE void getMapThumbnailFile(const QString &chatId, double latitude, double longitude, int width, int height);
+ Q_INVOKABLE void getMapThumbnailFile(const QString &chatId, double latitude, double longitude, int width, int height, const QString &extra);
Q_INVOKABLE void getRecentStickers();
Q_INVOKABLE void getInstalledStickerSets();
Q_INVOKABLE void getStickerSet(const QString &setId);
Q_INVOKABLE void getSupergroupMembers(const QString &groupId, int limit, int offset);
Q_INVOKABLE void getGroupFullInfo(const QString &groupId, bool isSuperGroup);
Q_INVOKABLE void getUserFullInfo(const QString &userId);
- Q_INVOKABLE void createPrivateChat(const QString &userId);
- Q_INVOKABLE void createNewSecretChat(const QString &userId);
- Q_INVOKABLE void createSupergroupChat(const QString &supergroupId);
- Q_INVOKABLE void createBasicGroupChat(const QString &basicGroupId);
+ Q_INVOKABLE void createPrivateChat(const QString &userId, const QString &extra);
+ Q_INVOKABLE void createNewSecretChat(const QString &userId, const QString &extra);
+ Q_INVOKABLE void createSupergroupChat(const QString &supergroupId, const QString &extra);
+ Q_INVOKABLE void createBasicGroupChat(const QString &basicGroupId, const QString &extra);
Q_INVOKABLE void getGroupsInCommon(const QString &userId, int limit, int offset);
Q_INVOKABLE void getUserProfilePhotos(const QString &userId, int limit, int offset);
Q_INVOKABLE void setChatPermissions(const QString &chatId, const QVariantMap &chatPermissions);
@@ -174,7 +177,7 @@ public:
Q_INVOKABLE void setPollAnswer(const QString &chatId, qlonglong messageId, QVariantList optionIds);
Q_INVOKABLE void stopPoll(const QString &chatId, qlonglong messageId);
Q_INVOKABLE void getPollVoters(const QString &chatId, qlonglong messageId, int optionId, int limit, int offset, const QString &extra);
- Q_INVOKABLE void searchPublicChat(const QString &userName);
+ Q_INVOKABLE void searchPublicChat(const QString &userName, bool doOpenOnFound);
Q_INVOKABLE void joinChatByInviteLink(const QString &inviteLink);
Q_INVOKABLE void getDeepLinkInfo(const QString &link);
Q_INVOKABLE void getContacts();
@@ -187,6 +190,9 @@ public:
Q_INVOKABLE void toggleChatIsMarkedAsUnread(qlonglong chatId, bool isMarkedAsUnread);
Q_INVOKABLE void toggleChatIsPinned(qlonglong chatId, bool isPinned);
Q_INVOKABLE void setChatDraftMessage(qlonglong chatId, qlonglong threadId, qlonglong replyToMessageId, const QString &draft);
+ Q_INVOKABLE void getInlineQueryResults(qlonglong botUserId, qlonglong chatId, const QVariantMap &userLocation, const QString &query, const QString &offset, const QString &extra);
+ Q_INVOKABLE void sendInlineQueryResultMessage(qlonglong chatId, qlonglong threadId, qlonglong replyToMessageId, const QString &queryId, const QString &resultId);
+ Q_INVOKABLE void sendBotStartMessage(qlonglong botUserId, qlonglong chatId, const QString ¶meter, const QString &extra);
// Others (candidates for extraction ;))
Q_INVOKABLE void searchEmoji(const QString &queryString);
@@ -259,6 +265,8 @@ signals:
void messageNotFound(qlonglong chatId, qlonglong messageId);
void chatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread);
void chatDraftMessageUpdated(qlonglong chatId, const QVariantMap &draftMessage, const QString &order);
+ void inlineQueryResults(const QString &inlineQueryId, const QString &nextOffset, const QVariantList &results, const QString &switchPmText, const QString &switchPmParameter, const QString &extra);
+ void callbackQueryAnswer(const QString &text, bool alert, const QString &url);
public slots:
void handleVersionDetected(const QString &version);
@@ -290,6 +298,7 @@ private:
void setEncryptionKey();
void setLogVerbosityLevel();
const Group *updateGroup(qlonglong groupId, const QVariantMap &groupInfo, QHash *groups);
+ void initializeTDLibReciever();
private:
void *tdLibClient;
@@ -315,6 +324,7 @@ private:
QString activeChatSearchName;
bool joinChatRequested;
+ bool isLoggingOut;
};
diff --git a/translations/harbour-fernschreiber-de.ts b/translations/harbour-fernschreiber-de.ts
index ced219c..2d6978c 100644
--- a/translations/harbour-fernschreiber-de.ts
+++ b/translations/harbour-fernschreiber-de.ts
@@ -83,6 +83,14 @@
rlottie auf GitHub öffnen
+
+
+
+
+
+
+
+
BackgroundProgressIndicator
@@ -823,6 +831,30 @@
myself
haben %1 vom Chat entfernt
+
+
+ myself
+
+ haben %Ln Punkt erziehlt
+ haben %Ln Punkte erziehlt
+
+
+
+
+
+ hat %Ln Punkt erziehlt
+ hat %Ln Punkte erziehlt
+
+
+
+
+ myself
+ haben ein Spiel gesendet
+
+
+
+ hat ein Spiel gesendet
+
ImagePage
@@ -973,6 +1005,21 @@
Sie
+
+
+ myself
+
+ haben %Ln Punkt bei %2 erziehlt
+ haben %Ln Punkte bei %2 erziehlt
+
+
+
+
+
+ hat %Ln Punkt bei %2 erziehlt
+ hat %Ln Punkte bei %2 erziehlt
+
+
MessageOverlayFlickable
@@ -985,6 +1032,14 @@
Diese Nachricht wurde weitergeleitet. Ursprünglicher Autor: %1
+
+ MessageViaLabel
+
+
+ message posted via bot user
+ via %1
+
+
NewChatPage
@@ -1112,6 +1167,10 @@
Sie können über das Pull-Down-Menü öffentliche Chats finden oder einen Neuen erstellen.
+
+
+
+
PinnedMessageItem
@@ -1445,6 +1504,18 @@
Schaltet das Offline-Caching aus. Bestimmte Features können in diesem Modus eingeschränkt sein oder fehlen. Änderungen erfordern einen Neustart von Fernschreiber, um in Kraft zu treten.
+
+
+ Privatsphäre
+
+
+
+ Erlaubt Standortsendung an Inline-Bots
+
+
+
+ Einige Inline-Bots fragen bei Nutzung Standortdaten an
+
StickerPicker
@@ -1912,5 +1983,22 @@
myself
haben %1 vom Chat entfernt
+
+
+ myself
+
+ haben %Ln Punkt erziehlt
+ haben %Ln Punkte erziehlt
+
+
+
+
+ myself
+ haben ein Spiel gesendet
+
+
+
+ hat ein Spiel gesendet
+
diff --git a/translations/harbour-fernschreiber-en.ts b/translations/harbour-fernschreiber-en.ts
index a4bdfce..1db9d16 100644
--- a/translations/harbour-fernschreiber-en.ts
+++ b/translations/harbour-fernschreiber-en.ts
@@ -83,6 +83,14 @@
Open rlottie on GitHub
+
+
+
+
+
+
+
+
BackgroundProgressIndicator
@@ -823,6 +831,30 @@
myself
have removed %1 from the chat
+
+
+ myself
+
+ scored %Ln point
+ scored %Ln points
+
+
+
+
+
+ scored %Ln point
+ scored %Ln points
+
+
+
+
+ myself
+ sent a game
+
+
+
+ sent a game
+
ImagePage
@@ -973,6 +1005,21 @@
You
+
+
+ myself
+
+ scored %Ln point in %2
+ scored %Ln points in %2
+
+
+
+
+
+ scored %Ln point in %2
+ scored %Ln points in %2
+
+
MessageOverlayFlickable
@@ -985,6 +1032,14 @@
This message was forwarded. Original author: %1
+
+ MessageViaLabel
+
+
+ message posted via bot user
+ via %1
+
+
NewChatPage
@@ -1112,6 +1167,10 @@
You can search public chats or create a new chat via the pull-down menu.
+
+
+ Logging out
+
PinnedMessageItem
@@ -1445,6 +1504,18 @@
Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.
+
+
+ Privacy
+
+
+
+ Allow sending Location to inline bots
+
+
+
+ Some inline bots request location data when using them
+
StickerPicker
@@ -1912,5 +1983,22 @@
myself
have removed %1 from the chat
+
+
+ myself
+
+ scored %Ln point
+ scored %Ln points
+
+
+
+
+ myself
+ sent a game
+
+
+
+ sent a game
+
diff --git a/translations/harbour-fernschreiber-es.ts b/translations/harbour-fernschreiber-es.ts
index 09cbb49..7dee1a2 100644
--- a/translations/harbour-fernschreiber-es.ts
+++ b/translations/harbour-fernschreiber-es.ts
@@ -83,6 +83,14 @@
Librería rlottie
+
+
+
+
+
+
+
+
BackgroundProgressIndicator
@@ -813,6 +821,28 @@
myself
ha quitado %1 de charla
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
ImagePage
@@ -963,6 +993,19 @@
Usted
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
MessageOverlayFlickable
@@ -975,6 +1018,14 @@
Este mensaje fue reenviado. Autor original: %1
+
+ MessageViaLabel
+
+
+ message posted via bot user
+
+
+
NewChatPage
@@ -1101,6 +1152,10 @@
Puede buscar charlas públicas o crear un nueva charla a través de la polea de opciones.
+
+
+
+
PinnedMessageItem
@@ -1424,6 +1479,18 @@
Deshabilita el almacenamiento en caché sin conexión. Algunas funciones pueden estar limitadas o ausentes en este modo. Se requiere reiniciar Fernschreiber para su efecto.
+
+
+
+
+
+
+
+
+
+
+
+
StickerPicker
@@ -1891,5 +1958,21 @@
myself
ha añadido %1 de la charla
+
+
+ myself
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
diff --git a/translations/harbour-fernschreiber-fi.ts b/translations/harbour-fernschreiber-fi.ts
index 1be7379..99e7130 100644
--- a/translations/harbour-fernschreiber-fi.ts
+++ b/translations/harbour-fernschreiber-fi.ts
@@ -83,6 +83,14 @@
Avaa rlottie GitHubissa
+
+
+
+
+
+
+
+
BackgroundProgressIndicator
@@ -824,6 +832,30 @@
myself
poistit käyttäjän %1 keskustelusta
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
ImagePage
@@ -974,6 +1006,21 @@
Sinä
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
MessageOverlayFlickable
@@ -986,6 +1033,14 @@
Välitetty viesti. Alkuperäinen lähettäjä: %1
+
+ MessageViaLabel
+
+
+ message posted via bot user
+
+
+
NewChatPage
@@ -1113,6 +1168,10 @@
Voit etsiä julkisia keskusteluja tai luoda uuden keskustelun alasvetovalikosta.
+
+
+
+
PinnedMessageItem
@@ -1444,6 +1503,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1913,5 +1984,22 @@
myself
poistit käyttäjän %1 keskustelusta
+
+
+ myself
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
diff --git a/translations/harbour-fernschreiber-hu.ts b/translations/harbour-fernschreiber-hu.ts
index ea3bdcf..b03007c 100644
--- a/translations/harbour-fernschreiber-hu.ts
+++ b/translations/harbour-fernschreiber-hu.ts
@@ -83,6 +83,14 @@
+
+
+
+
+
+
+
+
BackgroundProgressIndicator
@@ -813,6 +821,28 @@
myself
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
ImagePage
@@ -963,6 +993,19 @@
Te
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
MessageOverlayFlickable
@@ -975,6 +1018,14 @@
+
+ MessageViaLabel
+
+
+ message posted via bot user
+
+
+
NewChatPage
@@ -1101,6 +1152,10 @@
+
+
+
+
PinnedMessageItem
@@ -1422,6 +1477,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1891,5 +1958,21 @@
myself
+
+
+ myself
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
diff --git a/translations/harbour-fernschreiber-it.ts b/translations/harbour-fernschreiber-it.ts
index 4585d13..1a7c553 100644
--- a/translations/harbour-fernschreiber-it.ts
+++ b/translations/harbour-fernschreiber-it.ts
@@ -83,6 +83,14 @@
Apri rlottie su GitHub
+
+
+
+
+
+
+
+
BackgroundProgressIndicator
@@ -823,6 +831,30 @@
myself
hai rimosso %1 dalla chat
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
ImagePage
@@ -973,6 +1005,21 @@
Tu
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
MessageOverlayFlickable
@@ -985,6 +1032,14 @@
Questo è un messaggio inoltrato. Autore originale: %1
+
+ MessageViaLabel
+
+
+ message posted via bot user
+
+
+
NewChatPage
@@ -1112,6 +1167,10 @@
Puoi creare una nuova chat o cercare chat pubbliche dal menu a trascinamento.
+
+
+
+
PinnedMessageItem
@@ -1445,6 +1504,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
StickerPicker
@@ -1912,5 +1983,22 @@
myself
hai rimosso %1 dalla chat
+
+
+ myself
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
diff --git a/translations/harbour-fernschreiber-pl.ts b/translations/harbour-fernschreiber-pl.ts
index 17d3b8e..51d5db4 100644
--- a/translations/harbour-fernschreiber-pl.ts
+++ b/translations/harbour-fernschreiber-pl.ts
@@ -83,6 +83,14 @@
Otwórz rlottie na GitHub
+
+
+
+
+
+
+
+
BackgroundProgressIndicator
@@ -833,6 +841,32 @@
myself
usunąłem %1 z czatu
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
ImagePage
@@ -983,6 +1017,23 @@
Ty
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
+
+
MessageOverlayFlickable
@@ -995,6 +1046,14 @@
Ta wiadomość została przekazana. Oryginalny autor: %1
+
+ MessageViaLabel
+
+
+ message posted via bot user
+
+
+
NewChatPage
@@ -1123,6 +1182,10 @@
Możesz przeszukiwać czaty publiczne lub utworzyć nowy czat za pomocą menu rozwijanego z góry.
+
+
+
+
PinnedMessageItem
@@ -1466,6 +1529,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
StickerPicker
@@ -1933,5 +2008,23 @@
myself
usunąłem %1 z czatu
+
+
+ myself
+
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
diff --git a/translations/harbour-fernschreiber-ru.ts b/translations/harbour-fernschreiber-ru.ts
index 7efed5f..08f3600 100644
--- a/translations/harbour-fernschreiber-ru.ts
+++ b/translations/harbour-fernschreiber-ru.ts
@@ -83,6 +83,14 @@
Открыть rlottie на GitHub
+
+
+
+
+
+
+
+
BackgroundProgressIndicator
@@ -833,6 +841,32 @@
myself
%1 удалены из чата
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
ImagePage
@@ -983,6 +1017,23 @@
Вы
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
+
+
MessageOverlayFlickable
@@ -995,6 +1046,14 @@
Это сообщение было переадресовано. Первоначальный автор: %1
+
+ MessageViaLabel
+
+
+ message posted via bot user
+
+
+
NewChatPage
@@ -1123,6 +1182,10 @@
Вы можете искать публичные чаты или создать новый чат с помощью выпадающего меню
+
+
+
+
PinnedMessageItem
@@ -1466,6 +1529,18 @@
При нём не будет использоваться автономное кэширование и некоторые функции могут быть ограничены или отсутствовать. Изменения вступят в силу после перезапуска Fernschreiber.
+
+
+
+
+
+
+
+
+
+
+
+
StickerPicker
@@ -1933,5 +2008,23 @@
myself
%1 удалены из чата
+
+
+ myself
+
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
diff --git a/translations/harbour-fernschreiber-sv.ts b/translations/harbour-fernschreiber-sv.ts
index ad35d9e..6bd2e8e 100644
--- a/translations/harbour-fernschreiber-sv.ts
+++ b/translations/harbour-fernschreiber-sv.ts
@@ -83,6 +83,14 @@
Öppna rlottie på GitHub
+
+
+
+
+
+
+
+
BackgroundProgressIndicator
@@ -823,6 +831,30 @@
myself
har tagit bort %1 från chatten
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
ImagePage
@@ -973,6 +1005,21 @@
Du
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
MessageOverlayFlickable
@@ -985,6 +1032,14 @@
Detta meddelande är vidarebefordrat. Ursprunglig avsändare: %1
+
+ MessageViaLabel
+
+
+ message posted via bot user
+
+
+
NewChatPage
@@ -1112,6 +1167,10 @@
Du kan söka efter allmänna chattar eller skapa en ny chatt via toppmenyn.
+
+
+
+
PinnedMessageItem
@@ -1443,6 +1502,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1912,5 +1983,22 @@
myself
har tagit bort %1 från chatten
+
+
+ myself
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
diff --git a/translations/harbour-fernschreiber-zh_CN.ts b/translations/harbour-fernschreiber-zh_CN.ts
index 01554e9..39f507f 100644
--- a/translations/harbour-fernschreiber-zh_CN.ts
+++ b/translations/harbour-fernschreiber-zh_CN.ts
@@ -83,6 +83,14 @@
在 Github 打开 rlottie
+
+
+
+
+
+
+
+
BackgroundProgressIndicator
@@ -813,6 +821,28 @@
myself
已从此对话移除 %1
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
ImagePage
@@ -963,6 +993,19 @@
你
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
MessageOverlayFlickable
@@ -975,6 +1018,14 @@
此消息为转发消息,原作者: %1
+
+ MessageViaLabel
+
+
+ message posted via bot user
+
+
+
NewChatPage
@@ -1101,6 +1152,10 @@
你可以搜索公共对话或通过下拉菜单创建新对话。
+
+
+
+
PinnedMessageItem
@@ -1424,6 +1479,18 @@
禁用离线缓存。某些特定功能会在此模式中受限或消失。切换模式需要重启 fernschreiber 才能生效。
+
+
+
+
+
+
+
+
+
+
+
+
StickerPicker
@@ -1891,5 +1958,21 @@
myself
已从此对话移除 %1
+
+
+ myself
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
diff --git a/translations/harbour-fernschreiber.ts b/translations/harbour-fernschreiber.ts
index acf72ba..9dd1765 100644
--- a/translations/harbour-fernschreiber.ts
+++ b/translations/harbour-fernschreiber.ts
@@ -83,6 +83,14 @@
Open rlottie on GitHub
+
+
+
+
+
+
+
+
BackgroundProgressIndicator
@@ -823,6 +831,30 @@
myself
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+
ImagePage
@@ -973,6 +1005,21 @@
You
+
+
+ myself
+
+
+
+
+
+
+
+
+
+
+
+
MessageOverlayFlickable
@@ -985,6 +1032,14 @@
+
+ MessageViaLabel
+
+
+ message posted via bot user
+
+
+
NewChatPage
@@ -1112,6 +1167,10 @@
+
+
+
+
PinnedMessageItem
@@ -1443,6 +1502,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1912,5 +1983,22 @@
myself
+
+
+ myself
+
+
+
+
+
+
+
+ myself
+
+
+
+
+
+