Merge pull request #307 from jgibbon/feature/inline_queries

Implement inline queries (among others)
This commit is contained in:
Sebastian Wolf 2021-01-15 21:49:26 +01:00 committed by GitHub
commit 907a4580fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 2926 additions and 78 deletions

View file

@ -45,12 +45,15 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/components/BackgroundImage.qml \ qml/components/BackgroundImage.qml \
qml/components/ChatListViewItem.qml \ qml/components/ChatListViewItem.qml \
qml/components/DocumentPreview.qml \ qml/components/DocumentPreview.qml \
qml/components/GamePreview.qml \
qml/components/ImagePreview.qml \ qml/components/ImagePreview.qml \
qml/components/InReplyToRow.qml \ qml/components/InReplyToRow.qml \
qml/components/InlineQuery.qml \
qml/components/LocationPreview.qml \ qml/components/LocationPreview.qml \
qml/components/MessageListViewItem.qml \ qml/components/MessageListViewItem.qml \
qml/components/MessageListViewItemSimple.qml \ qml/components/MessageListViewItemSimple.qml \
qml/components/MessageOverlayFlickable.qml \ qml/components/MessageOverlayFlickable.qml \
qml/components/MessageViaLabel.qml \
qml/components/MultilineEmojiLabel.qml \ qml/components/MultilineEmojiLabel.qml \
qml/components/PinnedMessageItem.qml \ qml/components/PinnedMessageItem.qml \
qml/components/PollPreview.qml \ qml/components/PollPreview.qml \
@ -72,6 +75,20 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/components/chatInformationPage/ChatInformationTextItem.qml \ qml/components/chatInformationPage/ChatInformationTextItem.qml \
qml/components/chatInformationPage/EditGroupChatPermissionsColumn.qml \ qml/components/chatInformationPage/EditGroupChatPermissionsColumn.qml \
qml/components/chatInformationPage/EditSuperGroupSlowModeColumn.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/debug.js \
qml/js/functions.js \ qml/js/functions.js \
qml/pages/ChatInformationPage.qml \ qml/pages/ChatInformationPage.qml \

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}
}
}
}
}
}
}

View file

@ -35,6 +35,7 @@ Item {
property var pictureFileInformation; property var pictureFileInformation;
width: parent.width width: parent.width
height: width / 2 height: width / 2
property string fileExtra
Component.onCompleted: { Component.onCompleted: {
updatePicture(); updatePicture();
@ -47,14 +48,18 @@ Item {
function updatePicture() { function updatePicture() {
imagePreviewItem.pictureFileInformation = null; imagePreviewItem.pictureFileInformation = null;
if (locationData) { 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 { Connections {
target: tdLibWrapper target: tdLibWrapper
onFileUpdated: { 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) { if(!imagePreviewItem.pictureFileInformation) {
imagePreviewItem.pictureFileInformation = fileInformation; imagePreviewItem.pictureFileInformation = fileInformation;
tdLibWrapper.downloadFile(imagePreviewItem.pictureFileInformation.id); tdLibWrapper.downloadFile(imagePreviewItem.pictureFileInformation.id);

View file

@ -241,7 +241,7 @@ ListItem {
anchors.fill: parent anchors.fill: parent
enabled: !(messageListItem.precalculatedValues.pageIsSelecting || messageListItem.isAnonymous) enabled: !(messageListItem.precalculatedValues.pageIsSelecting || messageListItem.isAnonymous)
onClicked: { onClicked: {
tdLibWrapper.createPrivateChat(messageListItem.userInformation.id); tdLibWrapper.createPrivateChat(messageListItem.userInformation.id, "openDirectly");
} }
} }
} }
@ -299,11 +299,15 @@ ListItem {
anchors.fill: parent anchors.fill: parent
enabled: !(messageListItem.precalculatedValues.pageIsSelecting || messageListItem.isAnonymous) enabled: !(messageListItem.precalculatedValues.pageIsSelecting || messageListItem.isAnonymous)
onClicked: { onClicked: {
tdLibWrapper.createPrivateChat(messageListItem.userInformation.id); tdLibWrapper.createPrivateChat(messageListItem.userInformation.id, "openDirectly");
} }
} }
} }
MessageViaLabel {
message: myMessage
}
Loader { Loader {
id: messageInReplyToLoader id: messageInReplyToLoader
active: myMessage.reply_to_message_id !== 0 active: myMessage.reply_to_message_id !== 0

View file

@ -20,12 +20,14 @@ import QtQuick 2.6
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import "../js/twemoji.js" as Emoji import "../js/twemoji.js" as Emoji
import "../js/functions.js" as Functions import "../js/functions.js" as Functions
import "../js/debug.js" as Debug
Item { Item {
id: messageListItem id: messageListItem
property var myMessage: display property var myMessage: display
property var userInformation: tdLibWrapper.getUserInformation(myMessage.sender.user_id) property var userInformation: tdLibWrapper.getUserInformation(myMessage.sender.user_id)
property bool isOwnMessage: chatPage.myUserId === myMessage.sender.user_id property bool isOwnMessage: chatPage.myUserId === myMessage.sender.user_id
property var linkedMessage
height: backgroundRectangle.height + Theme.paddingMedium height: backgroundRectangle.height + Theme.paddingMedium
Rectangle { Rectangle {
@ -43,11 +45,40 @@ Item {
color: Theme.highlightColor color: Theme.highlightColor
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
font.pixelSize: Theme.fontSizeExtraSmall font.pixelSize: Theme.fontSizeExtraSmall
text: "<a style=\"text-decoration: none; font-weight: bold; color:"+Theme.primaryColor+"\" href=\"userId://" + messageListItem.userInformation.id + "\">" + (!messageListItem.isOwnMessage ? Emoji.emojify(Functions.getUserName(messageListItem.userInformation), font.pixelSize) : qsTr("You")) + "</a> " + Emoji.emojify(Functions.getMessageText(messageListItem.myMessage, false, chatPage.myUserId, false), font.pixelSize) property string messageContentText: Functions.getMessageText(messageListItem.myMessage, false, chatPage.myUserId, false)
text: "<a style=\"text-decoration: none; font-weight: bold; color:"+Theme.primaryColor+"\" href=\"userId://" + messageListItem.userInformation.id + "\">" + (!messageListItem.isOwnMessage ? Emoji.emojify(Functions.getUserName(messageListItem.userInformation), font.pixelSize) : qsTr("You")) + "</a> " + Emoji.emojify(messageContentText, font.pixelSize)
textFormat: Text.RichText textFormat: Text.RichText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere wrapMode: Text.WrapAtWordBoundaryOrAnywhere
onLinkActivated: { 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("<a href=\"linkedmessage\" style=\"text-decoration: none; color:"+Theme.primaryColor+"\">"+message.content.game.title+"</a>") :
qsTr("scored %Ln points in %2", "", myMessage.content.score).arg("<a href=\"linkedmessage\" style=\"text-decoration: none; color:"+Theme.primaryColor+"\" >"+message.content.game.title+"</a>");
}
}
Component.onCompleted: {
tdLibWrapper.getMessage(chatPage.chatInformation.id, myMessage.content.game_message_id);
}
}
} }
} }
} }

View file

@ -18,9 +18,9 @@
*/ */
import QtQuick 2.6 import QtQuick 2.6
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import "../components"
import "../js/functions.js" as Functions import "../js/functions.js" as Functions
import "../js/twemoji.js" as Emoji import "../js/twemoji.js" as Emoji
import "../js/debug.js" as Debug
Flickable { Flickable {
id: messageOverlayFlickable id: messageOverlayFlickable
@ -124,6 +124,10 @@ Flickable {
} }
} }
MessageViaLabel {
message: overlayMessage
}
Text { Text {
id: overlayForwardedInfoText id: overlayForwardedInfoText
width: parent.width width: parent.width
@ -179,6 +183,16 @@ Flickable {
asynchronous: true 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 { Timer {
id: messageDateUpdater id: messageDateUpdater
interval: 60000 interval: 60000

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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("<a style=\"text-decoration: none; font-weight: bold; color:"+Theme.primaryColor+"\" href=\"userId://" + message.via_bot_user_id + "\">@" + Emoji.emojify(botUserInformation.username, font.pixelSize)+"</a>")
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
}

View file

@ -23,6 +23,7 @@ import "../js/functions.js" as Functions
import "../js/debug.js" as Debug import "../js/debug.js" as Debug
Column { Column {
id: replyMarkupButtons
width: parent.width width: parent.width
height: childrenRect.height height: childrenRect.height
spacing: Theme.paddingSmall spacing: Theme.paddingSmall
@ -36,7 +37,7 @@ Column {
Repeater { Repeater {
id: buttonsRepeater id: buttonsRepeater
model: modelData model: modelData
property int itemWidth:precalculatedValues.textColumnWidth / count property int itemWidth: replyMarkupButtons.width / count
delegate: MouseArea { delegate: MouseArea {
/* /*
Unimplemented callback types: Unimplemented callback types:
@ -48,15 +49,35 @@ Column {
*/ */
property var callbacks: ({ property var callbacks: ({
inlineKeyboardButtonTypeCallback: function(){ 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() { inlineKeyboardButtonTypeUrl: function() {
Functions.handleLink(modelData.type.url); 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"]] enabled: !!callbacks[modelData.type["@type"]]
height: Theme.itemSizeSmall height: Theme.itemSizeSmall
width: (precalculatedValues.textColumnWidth + Theme.paddingSmall) / buttonsRepeater.count - (Theme.paddingSmall) width: (replyMarkupButtons.width + Theme.paddingSmall) / buttonsRepeater.count - (Theme.paddingSmall)
onClicked: { onClicked: {
callbacks[modelData.type["@type"]](); callbacks[modelData.type["@type"]]();
} }
@ -71,17 +92,18 @@ Column {
width: Math.min(parent.width - Theme.paddingSmall*2, contentWidth) width: Math.min(parent.width - Theme.paddingSmall*2, contentWidth)
truncationMode: TruncationMode.Fade truncationMode: TruncationMode.Fade
text: Emoji.emojify(modelData.text, Theme.fontSizeSmall) 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 anchors.centerIn: parent
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
} }
Icon { Icon {
property var sources: ({ property var sources: ({
inlineKeyboardButtonTypeUrl: "../../images/icon-s-link.svg", 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" inlineKeyboardButtonTypeCallbackWithPassword: "image://theme/icon-s-asterisk"
}) })
visible: !!sources[modelData.type["@type"]] visible: !!sources[modelData.type["@type"]]
opacity: 0.6
source: sources[modelData.type["@type"]] || "" source: sources[modelData.type["@type"]] || ""
sourceSize: Qt.size(Theme.iconSizeSmall, Theme.iconSizeSmall) sourceSize: Qt.size(Theme.iconSizeSmall, Theme.iconSizeSmall)
highlighted: parent.pressed highlighted: parent.pressed

View file

@ -232,7 +232,7 @@ SilicaFlickable {
MenuItem { MenuItem {
visible: chatInformationPage.isPrivateChat visible: chatInformationPage.isPrivateChat
onClicked: { onClicked: {
tdLibWrapper.createNewSecretChat(chatInformationPage.chatPartnerGroupId); tdLibWrapper.createNewSecretChat(chatInformationPage.chatPartnerGroupId, "openDirectly");
} }
text: qsTr("New Secret Chat") text: qsTr("New Secret Chat")
} }

View file

@ -92,7 +92,7 @@ ChatInformationTabItemBase {
} }
onClicked: { onClicked: {
tdLibWrapper.createPrivateChat(user_id); tdLibWrapper.createPrivateChat(user_id, "openDirectly");
} }
} }
footer: Component { footer: Component {

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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()
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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: '<a href="'+Emoji.emojify(previewFile.fileInformation.remote.id, font.pixelSize)+'">'+Emoji.emojify(previewFile.fileInformation.remote.id, font.pixelSize)+'</a> '
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();
}
}
}
}
}
}
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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();
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.6
InlineQueryResultVenue {
resultData: model
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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);
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.6
InlineQueryResultAnimation {
isAnimation: false
animationKey: "video"
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.6
InlineQueryResultAudio {
}

View file

@ -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"); return myself ? qsTr("sent a self-destructing video that is expired", "myself") : qsTr("sent a self-destructing video that is expired");
case 'messageScreenshotTaken': case 'messageScreenshotTaken':
return myself ? qsTr("created a screenshot in this chat", "myself") : qsTr("created a screenshot in this chat"); 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': case 'messageUnsupported':
return myself ? qsTr("sent an unsupported message", "myself") : qsTr("sent an unsupported message"); return myself ? qsTr("sent an unsupported message", "myself") : qsTr("sent an unsupported message");
default: default:
@ -361,16 +365,16 @@ function handleLink(link) {
if (typeof userInformation.id === "undefined") { if (typeof userInformation.id === "undefined") {
appNotification.show(qsTr("Unable to find user %1").arg(userName)); appNotification.show(qsTr("Unable to find user %1").arg(userName));
} else { } else {
tdLibWrapper.createPrivateChat(userInformation.id); tdLibWrapper.createPrivateChat(userInformation.id, "openDirectly");
} }
} else if (link.indexOf("userId://") === 0) { } else if (link.indexOf("userId://") === 0) {
tdLibWrapper.createPrivateChat(link.substring(9)); tdLibWrapper.createPrivateChat(link.substring(9), "openDirectly");
} else if (link.indexOf("tg://") === 0) { } else if (link.indexOf("tg://") === 0) {
Debug.log("Special TG link: ", link); Debug.log("Special TG link: ", link);
if (link.indexOf("tg://join?invite=") === 0) { if (link.indexOf("tg://join?invite=") === 0) {
tdLibWrapper.joinChatByInviteLink(tMePrefix + "joinchat/" + link.substring(17)); tdLibWrapper.joinChatByInviteLink(tMePrefix + "joinchat/" + link.substring(17));
} else if (link.indexOf("tg://resolve?domain=") === 0) { } 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 } else if (link.indexOf("botCommand://") === 0) { // this gets returned to send on ChatPage
return link.substring(13); return link.substring(13);
@ -383,7 +387,7 @@ function handleLink(link) {
// Fail with nice error message if it doesn't work // Fail with nice error message if it doesn't work
} else { } else {
Debug.log("Search public chat: ", link.substring(tMePrefix.length)); 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 // Check responses for updateBasicGroup or updateSupergroup
// Fire createBasicGroupChat or createSupergroupChat // Fire createBasicGroupChat or createSupergroupChat
// Do the necessary stuff to open the chat // Do the necessary stuff to open the chat
@ -440,8 +444,10 @@ function getMessagesArrayText(messages) {
} }
function handleErrorMessage(code, message) { function handleErrorMessage(code, message) {
if (code === 404) { if (code === 404 || (code === 400 && message === "USERNAME_INVALID")) {
// Silently ignore 404 Not Found messages (occur sometimes, without clear context...) // Silently ignore
// - 404 Not Found messages (occur sometimes, without clear context...)
// - searchPublicChat messages for "invalid" inline queries
return; return;
} }
if (message === "USER_ALREADY_PARTICIPANT") { if (message === "USER_ALREADY_PARTICIPANT") {

View file

@ -45,6 +45,7 @@ Page {
property bool isSuperGroup: false; property bool isSuperGroup: false;
property bool isChannel: false; property bool isChannel: false;
property var chatPartnerInformation; property var chatPartnerInformation;
property var botInformation;
property var chatGroupInformation; property var chatGroupInformation;
property int chatOnlineMemberCount: 0; property int chatOnlineMemberCount: 0;
property var emojiProposals; property var emojiProposals;
@ -59,6 +60,8 @@ Page {
property var selectedMessages: [] property var selectedMessages: []
readonly property bool isSelecting: selectedMessages.length > 0 readonly property bool isSelecting: selectedMessages.length > 0
readonly property bool canSendMessages: hasSendPrivilege("can_send_messages") readonly property bool canSendMessages: hasSendPrivilege("can_send_messages")
property bool doSendBotStartMessage
property string sendBotStartMessageParameter
states: [ states: [
State { State {
@ -154,6 +157,9 @@ Page {
if (isSecretChat) { if (isSecretChat) {
tdLibWrapper.getSecretChat(chatInformation.type.secret_chat_id); tdLibWrapper.getSecretChat(chatInformation.type.secret_chat_id);
} }
if(chatPartnerInformation.type["@type"] === "userTypeBot") {
tdLibWrapper.getUserFullInfo(chatPartnerInformation.id)
}
} }
else if (isBasicGroup) { else if (isBasicGroup) {
chatGroupInformation = tdLibWrapper.getBasicGroup(chatInformation.type.basic_group_id); chatGroupInformation = tdLibWrapper.getBasicGroup(chatInformation.type.basic_group_id);
@ -172,6 +178,7 @@ Page {
} }
tdLibWrapper.getChatPinnedMessage(chatInformation.id); tdLibWrapper.getChatPinnedMessage(chatInformation.id);
tdLibWrapper.toggleChatIsMarkedAsUnread(chatInformation.id, false); tdLibWrapper.toggleChatIsMarkedAsUnread(chatInformation.id, false);
} }
function getMessageStatusText(message, listItemIndex, lastReadSentIndex, useElapsed) { function getMessageStatusText(message, listItemIndex, lastReadSentIndex, useElapsed) {
@ -207,14 +214,13 @@ Page {
} }
function clearAttachmentPreviewRow() { function clearAttachmentPreviewRow() {
attachmentPreviewRow.visible = false;
attachmentPreviewRow.isPicture = false; attachmentPreviewRow.isPicture = false;
attachmentPreviewRow.isVideo = false; attachmentPreviewRow.isVideo = false;
attachmentPreviewRow.isDocument = false; attachmentPreviewRow.isDocument = false;
attachmentPreviewRow.isVoiceNote = false; attachmentPreviewRow.isVoiceNote = false;
attachmentPreviewRow.isLocation = false; attachmentPreviewRow.isLocation = false;
attachmentPreviewRow.fileProperties = {}; attachmentPreviewRow.fileProperties = null;
attachmentPreviewRow.locationData = {}; attachmentPreviewRow.locationData = null;
attachmentPreviewRow.attachmentDescription = ""; attachmentPreviewRow.attachmentDescription = "";
fernschreiberUtils.stopGeoLocationUpdates(); fernschreiberUtils.stopGeoLocationUpdates();
} }
@ -286,6 +292,10 @@ Page {
} }
function handleMessageTextReplacement(text, cursorPosition) { function handleMessageTextReplacement(text, cursorPosition) {
if(!newMessageTextField.focus) {
return;
}
var wordBoundaries = getWordBoundaries(text, cursorPosition); var wordBoundaries = getWordBoundaries(text, cursorPosition);
var currentWord = text.substring(wordBoundaries.beginIndex, wordBoundaries.endIndex); var currentWord = text.substring(wordBoundaries.beginIndex, wordBoundaries.endIndex);
@ -300,6 +310,7 @@ Page {
} else { } else {
knownUsersRepeater.model = undefined; knownUsersRepeater.model = undefined;
} }
} }
function replaceMessageText(text, cursorPosition, newText) { function replaceMessageText(text, cursorPosition, newText) {
@ -310,6 +321,19 @@ Page {
newMessageTextField.cursorPosition = newIndex; newMessageTextField.cursorPosition = newIndex;
lostFocusTimer.start(); 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) { function forwardMessages(fromChatId, messageIds) {
forwardMessagesTimer.fromChatId = fromChatId; forwardMessagesTimer.fromChatId = fromChatId;
forwardMessagesTimer.messageIds = messageIds; forwardMessagesTimer.messageIds = messageIds;
@ -402,6 +426,17 @@ Page {
switch(status) { switch(status) {
case PageStatus.Activating: case PageStatus.Activating:
tdLibWrapper.openChat(chatInformation.id); 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; break;
case PageStatus.Active: case PageStatus.Active:
if (!chatPage.isInitialized) { if (!chatPage.isInitialized) {
@ -410,13 +445,8 @@ Page {
pageStack.pushAttached(Qt.resolvedUrl("ChatInformationPage.qml"), { "chatInformation" : chatInformation, "privateChatUserInformation": chatPartnerInformation, "groupInformation": chatGroupInformation, "chatOnlineMemberCount": chatOnlineMemberCount}); pageStack.pushAttached(Qt.resolvedUrl("ChatInformationPage.qml"), { "chatInformation" : chatInformation, "privateChatUserInformation": chatPartnerInformation, "groupInformation": chatGroupInformation, "chatOnlineMemberCount": chatOnlineMemberCount});
chatPage.isInitialized = true; chatPage.isInitialized = true;
if(chatInformation.draft_message) { if(doSendBotStartMessage) {
if(chatInformation.draft_message && chatInformation.draft_message.input_message_text) { tdLibWrapper.sendBotStartMessage(chatInformation.id, chatInformation.id, sendBotStartMessageParameter, "")
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; break;
@ -494,6 +524,25 @@ Page {
chatPage.isSecretChatReady = chatPage.secretChatDetails.state["@type"] === "secretChatStateReady"; 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 { Connections {
@ -895,7 +944,7 @@ Page {
id: chatView id: chatView
visible: !blurred 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 anchors.fill: parent
opacity: chatPage.loading ? 0 : 1 opacity: chatPage.loading ? 0 : 1
@ -975,6 +1024,38 @@ Page {
} }
model: chatModel 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: ({ readonly property var contentComponentNames: ({
messageSticker: "StickerPreview", messageSticker: "StickerPreview",
messagePhoto: "ImagePreview", messagePhoto: "ImagePreview",
@ -986,7 +1067,8 @@ Page {
messageDocument: "DocumentPreview", messageDocument: "DocumentPreview",
messageLocation: "LocationPreview", messageLocation: "LocationPreview",
messageVenue: "LocationPreview", messageVenue: "LocationPreview",
messagePoll: "PollPreview" messagePoll: "PollPreview",
messageGame: "GamePreview"
}) })
function getContentComponentHeight(componentName, content, parentWidth) { function getContentComponentHeight(componentName, content, parentWidth) {
switch(componentName) { switch(componentName) {
@ -1002,6 +1084,8 @@ Page {
return Theme.itemSizeSmall; return Theme.itemSizeSmall;
case "PollPreview": case "PollPreview":
return Theme.itemSizeSmall * (4 + content.poll.options); return Theme.itemSizeSmall * (4 + content.poll.options);
case "GamePreview":
return parentWidth * 0.66666666 + Theme.itemSizeLarge; // 2 / 3;
} }
} }
@ -1014,6 +1098,7 @@ Page {
"messageChatJoinByLink", "messageChatJoinByLink",
"messageChatSetTtl", "messageChatSetTtl",
"messageChatUpgradeFrom", "messageChatUpgradeFrom",
"messageGameScore",
"messageChatUpgradeTo", "messageChatUpgradeTo",
"messageCustomServiceAction", "messageCustomServiceAction",
"messagePinMessage", "messagePinMessage",
@ -1168,12 +1253,17 @@ Page {
} }
} }
InlineQuery {
id: inlineQuery
textField: newMessageTextField
chatId: chatInformation.id
}
} }
Column { Column {
id: newMessageColumn id: newMessageColumn
spacing: Theme.paddingSmall spacing: Theme.paddingSmall
topPadding: Theme.paddingSmall topPadding: Theme.paddingSmall + inlineQuery.buttonPadding
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: height > 0 visible: height > 0
width: parent.width - ( 2 * Theme.horizontalPageMargin ) width: parent.width - ( 2 * Theme.horizontalPageMargin )
@ -1212,8 +1302,8 @@ Page {
property bool isNeeded: false property bool isNeeded: false
width: chatPage.width width: chatPage.width
x: -Theme.horizontalPageMargin x: -Theme.horizontalPageMargin
height: isNeeded ? attachmentOptionsRow.height : 0 height: isNeeded && !inlineQuery.userNameIsValid ? attachmentOptionsRow.height : 0
Behavior on height { SmoothedAnimation { duration: 200 } } Behavior on height { SmoothedAnimation { duration: 200 } }
visible: height > 0 visible: height > 0
contentHeight: attachmentOptionsRow.height contentHeight: attachmentOptionsRow.height
contentWidth: Math.max(width, attachmentOptionsRow.width) contentWidth: Math.max(width, attachmentOptionsRow.width)
@ -1252,7 +1342,6 @@ Page {
Debug.log("Selected document: ", picker.selectedContentProperties.filePath ); Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties; attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
attachmentPreviewRow.isPicture = true; attachmentPreviewRow.isPicture = true;
attachmentPreviewRow.visible = true;
controlSendButton(); controlSendButton();
}) })
} }
@ -1269,7 +1358,6 @@ Page {
Debug.log("Selected video: ", picker.selectedContentProperties.filePath ); Debug.log("Selected video: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties; attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
attachmentPreviewRow.isVideo = true; attachmentPreviewRow.isVideo = true;
attachmentPreviewRow.visible = true;
controlSendButton(); controlSendButton();
}) })
} }
@ -1299,7 +1387,6 @@ Page {
Debug.log("Selected document: ", picker.selectedContentProperties.filePath ); Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
attachmentPreviewRow.fileProperties = picker.selectedContentProperties; attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
attachmentPreviewRow.isDocument = true; attachmentPreviewRow.isDocument = true;
attachmentPreviewRow.visible = true;
controlSendButton(); controlSendButton();
}) })
} }
@ -1337,7 +1424,6 @@ Page {
attachmentOptionsFlickable.isNeeded = false; attachmentOptionsFlickable.isNeeded = false;
attachmentPreviewRow.isLocation = true; attachmentPreviewRow.isLocation = true;
attachmentPreviewRow.attachmentDescription = qsTr("Location: Obtaining position..."); attachmentPreviewRow.attachmentDescription = qsTr("Location: Obtaining position...");
attachmentPreviewRow.visible = true;
controlSendButton(); controlSendButton();
} }
} }
@ -1348,7 +1434,7 @@ Page {
Row { Row {
id: attachmentPreviewRow id: attachmentPreviewRow
visible: false visible: (!!locationData || !!fileProperties) && !inlineQuery.userNameIsValid
spacing: Theme.paddingMedium spacing: Theme.paddingMedium
width: parent.width width: parent.width
layoutDirection: Qt.RightToLeft layoutDirection: Qt.RightToLeft
@ -1359,8 +1445,8 @@ Page {
property bool isDocument: false; property bool isDocument: false;
property bool isVoiceNote: false; property bool isVoiceNote: false;
property bool isLocation: false; property bool isLocation: false;
property var locationData: ({}); property var locationData: null;
property var fileProperties:({}); property var fileProperties: null;
property string attachmentDescription: ""; property string attachmentDescription: "";
Connections { Connections {
@ -1390,15 +1476,15 @@ Page {
sourceSize.height: height sourceSize.height: height
fillMode: Thumbnail.PreserveAspectCrop fillMode: Thumbnail.PreserveAspectCrop
mimeType: typeof attachmentPreviewRow.fileProperties !== "undefined" ? attachmentPreviewRow.fileProperties.mimeType || "" : "" mimeType: !!attachmentPreviewRow.fileProperties ? attachmentPreviewRow.fileProperties.mimeType || "" : ""
source: typeof attachmentPreviewRow.fileProperties !== "undefined" ? attachmentPreviewRow.fileProperties.url || "" : "" source: !!attachmentPreviewRow.fileProperties ? attachmentPreviewRow.fileProperties.url || "" : ""
visible: attachmentPreviewRow.isPicture || attachmentPreviewRow.isVideo visible: attachmentPreviewRow.isPicture || attachmentPreviewRow.isVideo
} }
Label { Label {
id: attachmentPreviewText id: attachmentPreviewText
font.pixelSize: Theme.fontSizeSmall 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 anchors.verticalCenter: parent.verticalCenter
maximumLineCount: 1 maximumLineCount: 1
@ -1491,9 +1577,11 @@ Page {
id: atMentionColumn id: atMentionColumn
width: parent.width width: parent.width
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: knownUsersRepeater.count > 0 ? true : false visible: opacity > 0
opacity: knownUsersRepeater.count > 0 ? 1 : 0 opacity: knownUsersRepeater.count > 0 ? 1 : 0
Behavior on opacity { NumberAnimation {} } Behavior on opacity { NumberAnimation {} }
height: knownUsersRepeater.count > 0 ? childrenRect.height : 0
Behavior on height { SmoothedAnimation { duration: 200 } }
spacing: Theme.paddingMedium spacing: Theme.paddingMedium
Flickable { Flickable {
@ -1598,7 +1686,7 @@ Page {
TextArea { TextArea {
id: newMessageTextField 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) height: Math.min(chatContainer.height / 3, implicitHeight)
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
font.pixelSize: Theme.fontSizeSmall 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" EnterKey.iconSource: appSettings.sendByEnter ? "image://theme/icon-m-chat" : "image://theme/icon-m-enter"
onTextChanged: { onTextChanged: {
@ -1632,6 +1720,7 @@ Page {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.paddingSmall anchors.bottomMargin: Theme.paddingSmall
enabled: !attachmentPreviewRow.visible enabled: !attachmentPreviewRow.visible
visible: !inlineQuery.userNameIsValid
onClicked: { onClicked: {
if (attachmentOptionsFlickable.isNeeded) { if (attachmentOptionsFlickable.isNeeded) {
attachmentOptionsFlickable.isNeeded = false; attachmentOptionsFlickable.isNeeded = false;
@ -1648,7 +1737,7 @@ Page {
icon.source: "image://theme/icon-m-chat" icon.source: "image://theme/icon-m-chat"
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.paddingSmall anchors.bottomMargin: Theme.paddingSmall
visible: !appSettings.sendByEnter || attachmentPreviewRow.visible visible: !inlineQuery.userNameIsValid && (!appSettings.sendByEnter || attachmentPreviewRow.visible)
enabled: false enabled: false
onClicked: { onClicked: {
sendMessage(); 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
}
}
} }
} }
} }

View file

@ -43,6 +43,9 @@ Dialog {
case "forwardMessages": case "forwardMessages":
acceptDestinationInstance.forwardMessages(payload.fromChatId, payload.messageIds) acceptDestinationInstance.forwardMessages(payload.fromChatId, payload.messageIds)
break; break;
case "fillTextArea": // ReplyMarkupButtons: inlineKeyboardButtonTypeSwitchInline
acceptDestinationInstance.setMessageText(payload.text)
break;
// future uses of chat selection can be processed here // future uses of chat selection can be processed here
} }
} }
@ -75,7 +78,7 @@ Dialog {
QtObject { QtObject {
property bool visible: false property bool visible: false
Component.onCompleted: { Component.onCompleted: {
if(chatSelectionPage.state === "forwardMessages") { if(chatSelectionPage.state === "forwardMessages" || chatSelectionPage.state === "fillTextArea" ) {
var chatType = display.type['@type'] var chatType = display.type['@type']
var chatGroupInformation var chatGroupInformation
if(chatType === "chatTypePrivate" || chatType === "chatTypeSecret") { if(chatType === "chatTypePrivate" || chatType === "chatTypeSecret") {
@ -126,6 +129,7 @@ Dialog {
var chat = tdLibWrapper.getChat(display.id); var chat = tdLibWrapper.getChat(display.id);
switch(chatSelectionPage.state) { switch(chatSelectionPage.state) {
case "forwardMessages": case "forwardMessages":
case "fillTextArea":
chatSelectionPage.acceptDestinationProperties = { "chatInformation" : chat}; chatSelectionPage.acceptDestinationProperties = { "chatInformation" : chat};
chatSelectionPage.acceptDestination = Qt.resolvedUrl("../pages/ChatPage.qml"); chatSelectionPage.acceptDestination = Qt.resolvedUrl("../pages/ChatPage.qml");
break; break;

View file

@ -220,7 +220,7 @@ Page {
icon.source: "image://theme/icon-m-chat" icon.source: "image://theme/icon-m-chat"
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onClicked: { onClicked: {
tdLibWrapper.createPrivateChat(display.id); tdLibWrapper.createPrivateChat(display.id, "openDirectly");
} }
} }
@ -257,7 +257,7 @@ Page {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
tdLibWrapper.createPrivateChat(display.id); tdLibWrapper.createPrivateChat(display.id, "openDirectly");
} }
onPressed: { onPressed: {
privateChatHighlightBackground.visible = true; privateChatHighlightBackground.visible = true;
@ -295,7 +295,7 @@ Page {
icon.source: "image://theme/icon-m-device-lock" icon.source: "image://theme/icon-m-device-lock"
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onClicked: { onClicked: {
tdLibWrapper.createNewSecretChat(display.id); tdLibWrapper.createNewSecretChat(display.id, "openDirectly");
} }
} }
@ -331,7 +331,7 @@ Page {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
tdLibWrapper.createNewSecretChat(display.id); tdLibWrapper.createNewSecretChat(display.id, "openDirectly");
} }
onPressed: { onPressed: {
secretChatHighlightBackground.visible = true; secretChatHighlightBackground.visible = true;

View file

@ -210,10 +210,17 @@ Page {
} }
} }
onChatReceived: { 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) pageStack.pop(overviewPage, PageStackAction.Immediate)
// if we get a new chat (no messages?), we can not use the provided data // 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: { onErrorReceived: {

View file

@ -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 { SectionHeader {
text: qsTr("Storage") text: qsTr("Storage")
} }

View file

@ -29,6 +29,7 @@ namespace {
const QString KEY_NOTIFICATION_TURNS_DISPLAY_ON("notificationTurnsDisplayOn"); const QString KEY_NOTIFICATION_TURNS_DISPLAY_ON("notificationTurnsDisplayOn");
const QString KEY_NOTIFICATION_FEEDBACK("notificationFeedback"); const QString KEY_NOTIFICATION_FEEDBACK("notificationFeedback");
const QString KEY_STORAGE_OPTIMIZER("storageOptimizer"); const QString KEY_STORAGE_OPTIMIZER("storageOptimizer");
const QString KEY_INLINEBOT_LOCATION_ACCESS("allowInlineBotLocationAccess");
const QString KEY_REMAINING_INTERACTION_HINTS("remainingInteractionHints"); const QString KEY_REMAINING_INTERACTION_HINTS("remainingInteractionHints");
const QString KEY_ONLINE_ONLY_MODE("onlineOnlyMode"); 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 int AppSettings::remainingInteractionHints() const
{ {
return settings.value(KEY_REMAINING_INTERACTION_HINTS, 3).toInt(); return settings.value(KEY_REMAINING_INTERACTION_HINTS, 3).toInt();

View file

@ -31,6 +31,7 @@ class AppSettings : public QObject {
Q_PROPERTY(bool notificationTurnsDisplayOn READ notificationTurnsDisplayOn WRITE setNotificationTurnsDisplayOn NOTIFY notificationTurnsDisplayOnChanged) Q_PROPERTY(bool notificationTurnsDisplayOn READ notificationTurnsDisplayOn WRITE setNotificationTurnsDisplayOn NOTIFY notificationTurnsDisplayOnChanged)
Q_PROPERTY(NotificationFeedback notificationFeedback READ notificationFeedback WRITE setNotificationFeedback NOTIFY notificationFeedbackChanged) Q_PROPERTY(NotificationFeedback notificationFeedback READ notificationFeedback WRITE setNotificationFeedback NOTIFY notificationFeedbackChanged)
Q_PROPERTY(bool storageOptimizer READ storageOptimizer WRITE setStorageOptimizer NOTIFY storageOptimizerChanged) 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(int remainingInteractionHints READ remainingInteractionHints WRITE setRemainingInteractionHints NOTIFY remainingInteractionHintsChanged)
Q_PROPERTY(bool onlineOnlyMode READ onlineOnlyMode WRITE setOnlineOnlyMode NOTIFY onlineOnlyModeChanged) Q_PROPERTY(bool onlineOnlyMode READ onlineOnlyMode WRITE setOnlineOnlyMode NOTIFY onlineOnlyModeChanged)
@ -69,6 +70,9 @@ public:
bool storageOptimizer() const; bool storageOptimizer() const;
void setStorageOptimizer(bool enable); void setStorageOptimizer(bool enable);
bool allowInlineBotLocationAccess() const;
void setAllowInlineBotLocationAccess(bool enable);
int remainingInteractionHints() const; int remainingInteractionHints() const;
void setRemainingInteractionHints(int remainingHints); void setRemainingInteractionHints(int remainingHints);
@ -84,6 +88,7 @@ signals:
void notificationTurnsDisplayOnChanged(); void notificationTurnsDisplayOnChanged();
void notificationFeedbackChanged(); void notificationFeedbackChanged();
void storageOptimizerChanged(); void storageOptimizerChanged();
void allowInlineBotLocationAccessChanged();
void remainingInteractionHintsChanged(); void remainingInteractionHintsChanged();
void onlineOnlyModeChanged(); void onlineOnlyModeChanged();

View file

@ -178,6 +178,13 @@ QString FernschreiberUtils::getMessageShortText(TDLibWrapper *tdLibWrapper, cons
if (contentType == "messageScreenshotTaken") { if (contentType == "messageScreenshotTaken") {
return myself ? tr("created a screenshot in this chat", "myself") : tr("created a screenshot in this chat"); 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") { if (contentType == "messageUnsupported") {
return myself ? tr("sent an unsupported message", "myself") : tr("sent an unsupported message"); return myself ? tr("sent an unsupported message", "myself") : tr("sent an unsupported message");
} }

View file

@ -137,6 +137,8 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren
handlers.insert("updateMessageEdited", &TDLibReceiver::processUpdateMessageEdited); handlers.insert("updateMessageEdited", &TDLibReceiver::processUpdateMessageEdited);
handlers.insert("updateChatIsMarkedAsUnread", &TDLibReceiver::processUpdateChatIsMarkedAsUnread); handlers.insert("updateChatIsMarkedAsUnread", &TDLibReceiver::processUpdateChatIsMarkedAsUnread);
handlers.insert("updateChatDraftMessage", &TDLibReceiver::processUpdateChatDraftMessage); handlers.insert("updateChatDraftMessage", &TDLibReceiver::processUpdateChatDraftMessage);
handlers.insert("inlineQueryResults", &TDLibReceiver::processInlineQueryResults);
handlers.insert("callbackQueryAnswer", &TDLibReceiver::processCallbackQueryAnswer);
} }
void TDLibReceiver::setActive(bool active) void TDLibReceiver::setActive(bool active)
@ -596,3 +598,16 @@ void TDLibReceiver::processUpdateChatDraftMessage(const QVariantMap &receivedInf
LOG("Draft message was updated"); LOG("Draft message was updated");
emit chatDraftMessageUpdated(receivedInformation.value(CHAT_ID).toLongLong(), receivedInformation.value("draft_message").toMap(), findChatPositionOrder(receivedInformation.value(POSITIONS).toList())); 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());
}

View file

@ -93,6 +93,8 @@ signals:
void contactsImported(const QVariantList &importerCount, const QVariantList &userIds); void contactsImported(const QVariantList &importerCount, const QVariantList &userIds);
void chatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread); void chatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread);
void chatDraftMessageUpdated(qlonglong chatId, const QVariantMap &draftMessage, const QString &order); 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: private:
typedef void (TDLibReceiver::*Handler)(const QVariantMap &); typedef void (TDLibReceiver::*Handler)(const QVariantMap &);
@ -160,6 +162,8 @@ private:
void processImportedContacts(const QVariantMap &receivedInformation); void processImportedContacts(const QVariantMap &receivedInformation);
void processUpdateChatIsMarkedAsUnread(const QVariantMap &receivedInformation); void processUpdateChatIsMarkedAsUnread(const QVariantMap &receivedInformation);
void processUpdateChatDraftMessage(const QVariantMap &receivedInformation); void processUpdateChatDraftMessage(const QVariantMap &receivedInformation);
void processInlineQueryResults(const QVariantMap &receivedInformation);
void processCallbackQueryAnswer(const QVariantMap &receivedInformation);
}; };
#endif // TDLIBRECEIVER_H #endif // TDLIBRECEIVER_H

View file

@ -131,6 +131,8 @@ TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface,
connect(this->tdLibReceiver, SIGNAL(messageEditedUpdated(qlonglong, qlonglong, QVariantMap)), this, SIGNAL(messageEditedUpdated(qlonglong, qlonglong, QVariantMap))); 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(chatIsMarkedAsUnreadUpdated(qlonglong, bool)), this, SIGNAL(chatIsMarkedAsUnreadUpdated(qlonglong, bool)));
connect(this->tdLibReceiver, SIGNAL(chatDraftMessageUpdated(qlonglong, QVariantMap, QString)), this, SIGNAL(chatDraftMessageUpdated(qlonglong, QVariantMap, QString))); connect(this->tdLibReceiver, SIGNAL(chatDraftMessageUpdated(qlonglong, QVariantMap, QString)), this, SIGNAL(chatDraftMessageUpdated(qlonglong, QVariantMap, QString)));
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)));
connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList))); connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList)));
@ -685,13 +687,12 @@ void TDLibWrapper::deleteMessages(const QString &chatId, const QVariantList mess
this->sendRequest(requestObject); 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); LOG("Getting Map Thumbnail File" << chatId);
QVariantMap location; QVariantMap location;
location.insert("latitude", latitude); location.insert("latitude", latitude);
location.insert("longitude", longitude); location.insert("longitude", longitude);
// ensure dimensions are in bounds (16 - 1024) // ensure dimensions are in bounds (16 - 1024)
int boundsWidth = std::min(std::max(width, 16), 1024); int boundsWidth = std::min(std::max(width, 16), 1024);
int boundsHeight = std::min(std::max(height, 16), 1024); int boundsHeight = std::min(std::max(height, 16), 1024);
@ -704,6 +705,7 @@ void TDLibWrapper::getMapThumbnailFile(const QString &chatId, double latitude, d
requestObject.insert("height", boundsHeight); requestObject.insert("height", boundsHeight);
requestObject.insert("scale", 1); // 1-3 requestObject.insert("scale", 1); // 1-3
requestObject.insert(CHAT_ID, chatId); requestObject.insert(CHAT_ID, chatId);
requestObject.insert(_EXTRA, extra);
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
@ -769,43 +771,43 @@ void TDLibWrapper::getUserFullInfo(const QString &userId)
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::createPrivateChat(const QString &userId) void TDLibWrapper::createPrivateChat(const QString &userId, const QString &extra)
{ {
LOG("Creating Private Chat"); LOG("Creating Private Chat");
QVariantMap requestObject; QVariantMap requestObject;
requestObject.insert(_TYPE, "createPrivateChat"); requestObject.insert(_TYPE, "createPrivateChat");
requestObject.insert("user_id", userId); 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); this->sendRequest(requestObject);
} }
void TDLibWrapper::createNewSecretChat(const QString &userId) void TDLibWrapper::createNewSecretChat(const QString &userId, const QString &extra)
{ {
LOG("Creating new secret chat"); LOG("Creating new secret chat");
QVariantMap requestObject; QVariantMap requestObject;
requestObject.insert(_TYPE, "createNewSecretChat"); requestObject.insert(_TYPE, "createNewSecretChat");
requestObject.insert("user_id", userId); 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); this->sendRequest(requestObject);
} }
void TDLibWrapper::createSupergroupChat(const QString &supergroupId) void TDLibWrapper::createSupergroupChat(const QString &supergroupId, const QString &extra)
{ {
LOG("Creating Supergroup Chat"); LOG("Creating Supergroup Chat");
QVariantMap requestObject; QVariantMap requestObject;
requestObject.insert(_TYPE, "createSupergroupChat"); requestObject.insert(_TYPE, "createSupergroupChat");
requestObject.insert("supergroup_id", supergroupId); 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); this->sendRequest(requestObject);
} }
void TDLibWrapper::createBasicGroupChat(const QString &basicGroupId) void TDLibWrapper::createBasicGroupChat(const QString &basicGroupId, const QString &extra)
{ {
LOG("Creating Basic Group Chat"); LOG("Creating Basic Group Chat");
QVariantMap requestObject; QVariantMap requestObject;
requestObject.insert(_TYPE, "createBasicGroupChat"); requestObject.insert(_TYPE, "createBasicGroupChat");
requestObject.insert("basic_group_id", basicGroupId); 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); this->sendRequest(requestObject);
} }
@ -929,12 +931,15 @@ void TDLibWrapper::getPollVoters(const QString &chatId, qlonglong messageId, int
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::searchPublicChat(const QString &userName) void TDLibWrapper::searchPublicChat(const QString &userName, bool doOpenOnFound)
{ {
LOG("Search public chat" << userName); LOG("Search public chat" << userName);
this->activeChatSearchName = userName; if(doOpenOnFound) {
this->activeChatSearchName = userName;
}
QVariantMap requestObject; QVariantMap requestObject;
requestObject.insert(_TYPE, "searchPublicChat"); requestObject.insert(_TYPE, "searchPublicChat");
requestObject.insert(_EXTRA, "searchPublicChat:"+userName);
requestObject.insert(USERNAME, userName); requestObject.insert(USERNAME, userName);
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
@ -1075,6 +1080,53 @@ void TDLibWrapper::setChatDraftMessage(qlonglong chatId, qlonglong threadId, qlo
this->sendRequest(requestObject); 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 &parameter, 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) void TDLibWrapper::searchEmoji(const QString &queryString)
{ {
LOG("Searching emoji" << queryString); LOG("Searching emoji" << queryString);
@ -1349,12 +1401,12 @@ void TDLibWrapper::handleChatReceived(const QVariantMap &chatInformation)
if (receivedChatType == ChatTypeBasicGroup) { if (receivedChatType == ChatTypeBasicGroup) {
LOG("Found basic group for active search" << this->activeChatSearchName); LOG("Found basic group for active search" << this->activeChatSearchName);
this->activeChatSearchName.clear(); this->activeChatSearchName.clear();
this->createBasicGroupChat(chatType.value("basic_group_id").toString()); this->createBasicGroupChat(chatType.value("basic_group_id").toString(), "openDirectly");
} }
if (receivedChatType == ChatTypeSupergroup) { if (receivedChatType == ChatTypeSupergroup) {
LOG("Found supergroup for active search" << this->activeChatSearchName); LOG("Found supergroup for active search" << this->activeChatSearchName);
this->activeChatSearchName.clear(); this->activeChatSearchName.clear();
this->createSupergroupChat(chatType.value("supergroup_id").toString()); this->createSupergroupChat(chatType.value("supergroup_id").toString(), "openDirectly");
} }
} }
} }
@ -1381,7 +1433,7 @@ void TDLibWrapper::handleBasicGroupUpdated(qlonglong groupId, const QVariantMap
if (!this->activeChatSearchName.isEmpty() && this->activeChatSearchName == groupInformation.value(USERNAME).toString()) { if (!this->activeChatSearchName.isEmpty() && this->activeChatSearchName == groupInformation.value(USERNAME).toString()) {
LOG("Found basic group for active search" << this->activeChatSearchName); LOG("Found basic group for active search" << this->activeChatSearchName);
this->activeChatSearchName.clear(); this->activeChatSearchName.clear();
this->createBasicGroupChat(groupInformation.value(ID).toString()); this->createBasicGroupChat(groupInformation.value(ID).toString(), "openDirectly");
} }
} }
@ -1391,7 +1443,7 @@ void TDLibWrapper::handleSuperGroupUpdated(qlonglong groupId, const QVariantMap
if (!this->activeChatSearchName.isEmpty() && this->activeChatSearchName == groupInformation.value(USERNAME).toString()) { if (!this->activeChatSearchName.isEmpty() && this->activeChatSearchName == groupInformation.value(USERNAME).toString()) {
LOG("Found supergroup for active search" << this->activeChatSearchName); LOG("Found supergroup for active search" << this->activeChatSearchName);
this->activeChatSearchName.clear(); this->activeChatSearchName.clear();
this->createSupergroupChat(groupInformation.value(ID).toString()); this->createSupergroupChat(groupInformation.value(ID).toString(), "openDirectly");
} }
} }

View file

@ -152,17 +152,17 @@ public:
Q_INVOKABLE void setChatNotificationSettings(const QString &chatId, const QVariantMap &notificationSettings); Q_INVOKABLE void setChatNotificationSettings(const QString &chatId, const QVariantMap &notificationSettings);
Q_INVOKABLE void editMessageText(const QString &chatId, const QString &messageId, const QString &message); 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 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 getRecentStickers();
Q_INVOKABLE void getInstalledStickerSets(); Q_INVOKABLE void getInstalledStickerSets();
Q_INVOKABLE void getStickerSet(const QString &setId); Q_INVOKABLE void getStickerSet(const QString &setId);
Q_INVOKABLE void getSupergroupMembers(const QString &groupId, int limit, int offset); Q_INVOKABLE void getSupergroupMembers(const QString &groupId, int limit, int offset);
Q_INVOKABLE void getGroupFullInfo(const QString &groupId, bool isSuperGroup); Q_INVOKABLE void getGroupFullInfo(const QString &groupId, bool isSuperGroup);
Q_INVOKABLE void getUserFullInfo(const QString &userId); Q_INVOKABLE void getUserFullInfo(const QString &userId);
Q_INVOKABLE void createPrivateChat(const QString &userId); Q_INVOKABLE void createPrivateChat(const QString &userId, const QString &extra);
Q_INVOKABLE void createNewSecretChat(const QString &userId); Q_INVOKABLE void createNewSecretChat(const QString &userId, const QString &extra);
Q_INVOKABLE void createSupergroupChat(const QString &supergroupId); Q_INVOKABLE void createSupergroupChat(const QString &supergroupId, const QString &extra);
Q_INVOKABLE void createBasicGroupChat(const QString &basicGroupId); Q_INVOKABLE void createBasicGroupChat(const QString &basicGroupId, const QString &extra);
Q_INVOKABLE void getGroupsInCommon(const QString &userId, int limit, int offset); 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 getUserProfilePhotos(const QString &userId, int limit, int offset);
Q_INVOKABLE void setChatPermissions(const QString &chatId, const QVariantMap &chatPermissions); Q_INVOKABLE void setChatPermissions(const QString &chatId, const QVariantMap &chatPermissions);
@ -174,7 +174,7 @@ public:
Q_INVOKABLE void setPollAnswer(const QString &chatId, qlonglong messageId, QVariantList optionIds); Q_INVOKABLE void setPollAnswer(const QString &chatId, qlonglong messageId, QVariantList optionIds);
Q_INVOKABLE void stopPoll(const QString &chatId, qlonglong messageId); 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 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 joinChatByInviteLink(const QString &inviteLink);
Q_INVOKABLE void getDeepLinkInfo(const QString &link); Q_INVOKABLE void getDeepLinkInfo(const QString &link);
Q_INVOKABLE void getContacts(); Q_INVOKABLE void getContacts();
@ -187,6 +187,9 @@ public:
Q_INVOKABLE void toggleChatIsMarkedAsUnread(qlonglong chatId, bool isMarkedAsUnread); Q_INVOKABLE void toggleChatIsMarkedAsUnread(qlonglong chatId, bool isMarkedAsUnread);
Q_INVOKABLE void toggleChatIsPinned(qlonglong chatId, bool isPinned); Q_INVOKABLE void toggleChatIsPinned(qlonglong chatId, bool isPinned);
Q_INVOKABLE void setChatDraftMessage(qlonglong chatId, qlonglong threadId, qlonglong replyToMessageId, const QString &draft); 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 &parameter, const QString &extra);
// Others (candidates for extraction ;)) // Others (candidates for extraction ;))
Q_INVOKABLE void searchEmoji(const QString &queryString); Q_INVOKABLE void searchEmoji(const QString &queryString);
@ -259,6 +262,8 @@ signals:
void messageNotFound(qlonglong chatId, qlonglong messageId); void messageNotFound(qlonglong chatId, qlonglong messageId);
void chatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread); void chatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread);
void chatDraftMessageUpdated(qlonglong chatId, const QVariantMap &draftMessage, const QString &order); 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: public slots:
void handleVersionDetected(const QString &version); void handleVersionDetected(const QString &version);

View file

@ -823,6 +823,30 @@
<comment>myself</comment> <comment>myself</comment>
<translation>haben %1 vom Chat entfernt</translation> <translation>haben %1 vom Chat entfernt</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation>
<numerusform>haben %Ln Punkt erziehlt</numerusform>
<numerusform>haben %Ln Punkte erziehlt</numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points</source>
<translation type="unfinished">
<numerusform>hat %Ln Punkt erziehlt</numerusform>
<numerusform>hat %Ln Punkte erziehlt</numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation>haben ein Spiel gesendet</translation>
</message>
<message>
<source>sent a game</source>
<translation>hat ein Spiel gesendet</translation>
</message>
</context> </context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
@ -973,6 +997,21 @@
<source>You</source> <source>You</source>
<translation>Sie</translation> <translation>Sie</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform>haben %Ln Punkt bei %2 erziehlt</numerusform>
<numerusform>haben %Ln Punkte bei %2 erziehlt</numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<translation type="unfinished">
<numerusform>hat %Ln Punkt bei %2 erziehlt</numerusform>
<numerusform>hat %Ln Punkte bei %2 erziehlt</numerusform>
</translation>
</message>
</context> </context>
<context> <context>
<name>MessageOverlayFlickable</name> <name>MessageOverlayFlickable</name>
@ -985,6 +1024,14 @@
<translation>Diese Nachricht wurde weitergeleitet. Ursprünglicher Autor: %1</translation> <translation>Diese Nachricht wurde weitergeleitet. Ursprünglicher Autor: %1</translation>
</message> </message>
</context> </context>
<context>
<name>MessageViaLabel</name>
<message>
<source>via %1</source>
<comment>message posted via bot user</comment>
<translation type="unfinished">via %1</translation>
</message>
</context>
<context> <context>
<name>NewChatPage</name> <name>NewChatPage</name>
<message> <message>
@ -1445,6 +1492,18 @@
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation>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.</translation> <translation>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.</translation>
</message> </message>
<message>
<source>Privacy</source>
<translation>Privatsphäre</translation>
</message>
<message>
<source>Allow sending Location to inline bots</source>
<translation>Erlaubt Standortsendung an Inline-Bots</translation>
</message>
<message>
<source>Some inline bots request location data when using them</source>
<translation>Einige Inline-Bots fragen bei Nutzung Standortdaten an</translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1912,5 +1971,22 @@
<comment>myself</comment> <comment>myself</comment>
<translation>haben %1 vom Chat entfernt</translation> <translation>haben %1 vom Chat entfernt</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform>haben %Ln Punkt erziehlt</numerusform>
<numerusform>haben %Ln Punkte erziehlt</numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation>haben ein Spiel gesendet</translation>
</message>
<message>
<source>sent a game</source>
<translation>hat ein Spiel gesendet</translation>
</message>
</context> </context>
</TS> </TS>

View file

@ -823,6 +823,30 @@
<comment>myself</comment> <comment>myself</comment>
<translation>have removed %1 from the chat</translation> <translation>have removed %1 from the chat</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation>
<numerusform>scored %Ln point</numerusform>
<numerusform>scored %Ln points</numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points</source>
<translation>
<numerusform>scored %Ln point</numerusform>
<numerusform>scored %Ln points</numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation>sent a game</translation>
</message>
<message>
<source>sent a game</source>
<translation>sent a game</translation>
</message>
</context> </context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
@ -973,6 +997,21 @@
<source>You</source> <source>You</source>
<translation>You</translation> <translation>You</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<comment>myself</comment>
<translation>
<numerusform>scored %Ln point in %2</numerusform>
<numerusform>scored %Ln points in %2</numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<translation>
<numerusform>scored %Ln point in %2</numerusform>
<numerusform>scored %Ln points in %2</numerusform>
</translation>
</message>
</context> </context>
<context> <context>
<name>MessageOverlayFlickable</name> <name>MessageOverlayFlickable</name>
@ -985,6 +1024,14 @@
<translation>This message was forwarded. Original author: %1</translation> <translation>This message was forwarded. Original author: %1</translation>
</message> </message>
</context> </context>
<context>
<name>MessageViaLabel</name>
<message>
<source>via %1</source>
<comment>message posted via bot user</comment>
<translation type="unfinished">via %1</translation>
</message>
</context>
<context> <context>
<name>NewChatPage</name> <name>NewChatPage</name>
<message> <message>
@ -1445,6 +1492,18 @@
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</translation> <translation>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</translation>
</message> </message>
<message>
<source>Privacy</source>
<translation>Privacy</translation>
</message>
<message>
<source>Allow sending Location to inline bots</source>
<translation>Allow sending Location to inline bots</translation>
</message>
<message>
<source>Some inline bots request location data when using them</source>
<translation>Some inline bots request location data when using them</translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1912,5 +1971,22 @@
<comment>myself</comment> <comment>myself</comment>
<translation>have removed %1 from the chat</translation> <translation>have removed %1 from the chat</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation>
<numerusform>scored %Ln point</numerusform>
<numerusform>scored %Ln points</numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation>sent a game</translation>
</message>
<message>
<source>sent a game</source>
<translation>sent a game</translation>
</message>
</context> </context>
</TS> </TS>

View file

@ -813,6 +813,28 @@
<comment>myself</comment> <comment>myself</comment>
<translation>ha quitado %1 de charla</translation> <translation>ha quitado %1 de charla</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points</source>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
@ -963,6 +985,19 @@
<source>You</source> <source>You</source>
<translation>Usted</translation> <translation>Usted</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
</context> </context>
<context> <context>
<name>MessageOverlayFlickable</name> <name>MessageOverlayFlickable</name>
@ -975,6 +1010,14 @@
<translation>Este mensaje fue reenviado. Autor original: %1</translation> <translation>Este mensaje fue reenviado. Autor original: %1</translation>
</message> </message>
</context> </context>
<context>
<name>MessageViaLabel</name>
<message>
<source>via %1</source>
<comment>message posted via bot user</comment>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NewChatPage</name> <name>NewChatPage</name>
<message> <message>
@ -1424,6 +1467,18 @@
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation>Deshabilita el almacenamiento en caché sin conexión. Algunas funciones pueden estar limitadas o ausentes en este modo. Se requiere reiniciar Fernschreiber para su efecto.</translation> <translation>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.</translation>
</message> </message>
<message>
<source>Privacy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow sending Location to inline bots</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Some inline bots request location data when using them</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1891,5 +1946,21 @@
<comment>myself</comment> <comment>myself</comment>
<translation>ha añadido %1 de la charla</translation> <translation>ha añadido %1 de la charla</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>

View file

@ -824,6 +824,30 @@
<comment>myself</comment> <comment>myself</comment>
<translation>poistit käyttäjän %1 keskustelusta</translation> <translation>poistit käyttäjän %1 keskustelusta</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
@ -974,6 +998,21 @@
<source>You</source> <source>You</source>
<translation>Sinä</translation> <translation>Sinä</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
</context> </context>
<context> <context>
<name>MessageOverlayFlickable</name> <name>MessageOverlayFlickable</name>
@ -986,6 +1025,14 @@
<translation>Välitetty viesti. Alkuperäinen lähettäjä: %1</translation> <translation>Välitetty viesti. Alkuperäinen lähettäjä: %1</translation>
</message> </message>
</context> </context>
<context>
<name>MessageViaLabel</name>
<message>
<source>via %1</source>
<comment>message posted via bot user</comment>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NewChatPage</name> <name>NewChatPage</name>
<message> <message>
@ -1444,6 +1491,18 @@
</message> </message>
<message> <message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation></translation>
</message>
<message>
<source>Privacy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow sending Location to inline bots</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Some inline bots request location data when using them</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
@ -1913,5 +1972,22 @@
<comment>myself</comment> <comment>myself</comment>
<translation>poistit käyttäjän %1 keskustelusta</translation> <translation>poistit käyttäjän %1 keskustelusta</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>

View file

@ -813,6 +813,28 @@
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points</source>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
@ -963,6 +985,19 @@
<source>You</source> <source>You</source>
<translation type="unfinished">Te</translation> <translation type="unfinished">Te</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
</context> </context>
<context> <context>
<name>MessageOverlayFlickable</name> <name>MessageOverlayFlickable</name>
@ -975,6 +1010,14 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>MessageViaLabel</name>
<message>
<source>via %1</source>
<comment>message posted via bot user</comment>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NewChatPage</name> <name>NewChatPage</name>
<message> <message>
@ -1422,6 +1465,18 @@
</message> </message>
<message> <message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation></translation>
</message>
<message>
<source>Privacy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow sending Location to inline bots</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Some inline bots request location data when using them</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
@ -1891,5 +1946,21 @@
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>

View file

@ -823,6 +823,30 @@
<comment>myself</comment> <comment>myself</comment>
<translation>hai rimosso %1 dalla chat</translation> <translation>hai rimosso %1 dalla chat</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
@ -973,6 +997,21 @@
<source>You</source> <source>You</source>
<translation>Tu</translation> <translation>Tu</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
</context> </context>
<context> <context>
<name>MessageOverlayFlickable</name> <name>MessageOverlayFlickable</name>
@ -985,6 +1024,14 @@
<translation>Questo è un messaggio inoltrato. Autore originale: %1</translation> <translation>Questo è un messaggio inoltrato. Autore originale: %1</translation>
</message> </message>
</context> </context>
<context>
<name>MessageViaLabel</name>
<message>
<source>via %1</source>
<comment>message posted via bot user</comment>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NewChatPage</name> <name>NewChatPage</name>
<message> <message>
@ -1445,6 +1492,18 @@
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Privacy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow sending Location to inline bots</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Some inline bots request location data when using them</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1912,5 +1971,22 @@
<comment>myself</comment> <comment>myself</comment>
<translation>hai rimosso %1 dalla chat</translation> <translation>hai rimosso %1 dalla chat</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>

View file

@ -833,6 +833,32 @@
<comment>myself</comment> <comment>myself</comment>
<translation>usunąłem %1 z czatu</translation> <translation>usunąłem %1 z czatu</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
@ -983,6 +1009,23 @@
<source>You</source> <source>You</source>
<translation>Ty</translation> <translation>Ty</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
</context> </context>
<context> <context>
<name>MessageOverlayFlickable</name> <name>MessageOverlayFlickable</name>
@ -995,6 +1038,14 @@
<translation>Ta wiadomość została przekazana. Oryginalny autor: %1</translation> <translation>Ta wiadomość została przekazana. Oryginalny autor: %1</translation>
</message> </message>
</context> </context>
<context>
<name>MessageViaLabel</name>
<message>
<source>via %1</source>
<comment>message posted via bot user</comment>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NewChatPage</name> <name>NewChatPage</name>
<message> <message>
@ -1466,6 +1517,18 @@
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Privacy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow sending Location to inline bots</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Some inline bots request location data when using them</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1933,5 +1996,23 @@
<comment>myself</comment> <comment>myself</comment>
<translation>usunąłem %1 z czatu</translation> <translation>usunąłem %1 z czatu</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>

View file

@ -833,6 +833,32 @@
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished">%1 удалены из чата</translation> <translation type="unfinished">%1 удалены из чата</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
@ -983,6 +1009,23 @@
<source>You</source> <source>You</source>
<translation>Вы</translation> <translation>Вы</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
</context> </context>
<context> <context>
<name>MessageOverlayFlickable</name> <name>MessageOverlayFlickable</name>
@ -995,6 +1038,14 @@
<translation>Это сообщение было переадресовано. Первоначальный автор: %1</translation> <translation>Это сообщение было переадресовано. Первоначальный автор: %1</translation>
</message> </message>
</context> </context>
<context>
<name>MessageViaLabel</name>
<message>
<source>via %1</source>
<comment>message posted via bot user</comment>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NewChatPage</name> <name>NewChatPage</name>
<message> <message>
@ -1466,6 +1517,18 @@
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation type="unfinished">При нём не будет использоваться автономное кэширование и некоторые функции могут быть ограничены или отсутствовать. Изменения вступят в силу после перезапуска Fernschreiber.</translation> <translation type="unfinished">При нём не будет использоваться автономное кэширование и некоторые функции могут быть ограничены или отсутствовать. Изменения вступят в силу после перезапуска Fernschreiber.</translation>
</message> </message>
<message>
<source>Privacy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow sending Location to inline bots</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Some inline bots request location data when using them</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1933,5 +1996,23 @@
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished">%1 удалены из чата</translation> <translation type="unfinished">%1 удалены из чата</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>

View file

@ -823,6 +823,30 @@
<comment>myself</comment> <comment>myself</comment>
<translation>har tagit bort %1 från chatten</translation> <translation>har tagit bort %1 från chatten</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
@ -973,6 +997,21 @@
<source>You</source> <source>You</source>
<translation>Du</translation> <translation>Du</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
</context> </context>
<context> <context>
<name>MessageOverlayFlickable</name> <name>MessageOverlayFlickable</name>
@ -985,6 +1024,14 @@
<translation>Detta meddelande är vidarebefordrat. Ursprunglig avsändare: %1</translation> <translation>Detta meddelande är vidarebefordrat. Ursprunglig avsändare: %1</translation>
</message> </message>
</context> </context>
<context>
<name>MessageViaLabel</name>
<message>
<source>via %1</source>
<comment>message posted via bot user</comment>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NewChatPage</name> <name>NewChatPage</name>
<message> <message>
@ -1443,6 +1490,18 @@
</message> </message>
<message> <message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation></translation>
</message>
<message>
<source>Privacy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow sending Location to inline bots</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Some inline bots request location data when using them</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
@ -1912,5 +1971,22 @@
<comment>myself</comment> <comment>myself</comment>
<translation>har tagit bort %1 från chatten</translation> <translation>har tagit bort %1 från chatten</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>

View file

@ -813,6 +813,28 @@
<comment>myself</comment> <comment>myself</comment>
<translation> %1</translation> <translation> %1</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points</source>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
@ -963,6 +985,19 @@
<source>You</source> <source>You</source>
<translation></translation> <translation></translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
</context> </context>
<context> <context>
<name>MessageOverlayFlickable</name> <name>MessageOverlayFlickable</name>
@ -975,6 +1010,14 @@
<translation>: %1</translation> <translation>: %1</translation>
</message> </message>
</context> </context>
<context>
<name>MessageViaLabel</name>
<message>
<source>via %1</source>
<comment>message posted via bot user</comment>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NewChatPage</name> <name>NewChatPage</name>
<message> <message>
@ -1424,6 +1467,18 @@
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation>线 fernschreiber </translation> <translation>线 fernschreiber </translation>
</message> </message>
<message>
<source>Privacy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow sending Location to inline bots</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Some inline bots request location data when using them</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StickerPicker</name> <name>StickerPicker</name>
@ -1891,5 +1946,21 @@
<comment>myself</comment> <comment>myself</comment>
<translation> %1</translation> <translation> %1</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>

View file

@ -823,6 +823,30 @@
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ImagePage</name> <name>ImagePage</name>
@ -973,6 +997,21 @@
<source>You</source> <source>You</source>
<translation>You</translation> <translation>You</translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<source>scored %Ln points in %2</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
</context> </context>
<context> <context>
<name>MessageOverlayFlickable</name> <name>MessageOverlayFlickable</name>
@ -985,6 +1024,14 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>MessageViaLabel</name>
<message>
<source>via %1</source>
<comment>message posted via bot user</comment>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NewChatPage</name> <name>NewChatPage</name>
<message> <message>
@ -1443,6 +1490,18 @@
</message> </message>
<message> <message>
<source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source> <source>Disables offline caching. Certain features may be limited or missing in this mode. Changes require a restart of Fernschreiber to take effect.</source>
<translation></translation>
</message>
<message>
<source>Privacy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow sending Location to inline bots</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Some inline bots request location data when using them</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
@ -1912,5 +1971,22 @@
<comment>myself</comment> <comment>myself</comment>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message numerus="yes">
<source>scored %Ln points</source>
<comment>myself</comment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>sent a game</source>
<comment>myself</comment>
<translation type="unfinished"></translation>
</message>
<message>
<source>sent a game</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>