Compare commits

...

54 commits

Author SHA1 Message Date
Sebastian Wolf
0236586e44 Switch to gh tool for release creation, see https://github.com/actions/runner-images/issues/8362 2023-12-03 23:52:21 +03:00
Sebastian Wolf
e13c7ae68c Prepare 0.17 release 2023-12-03 23:52:19 +03:00
Sebastian Wolf
1d68123ea1 Restore content width in landscape as per discussion in #540 2023-12-03 23:52:06 +03:00
Slava Monich
cf7b706582 Tweaked the logic of moving reactions into the view (#543)
There's no need to reposition list items if reactions bar are already
fully visible.
2023-12-03 23:50:55 +03:00
free software
c42d030d02 Update harbour-fernschreiber-es.ts (#542) 2023-12-03 23:50:55 +03:00
Sebastian Wolf
edfce9b492 Highlight message that was jumped to 2023-12-03 23:50:55 +03:00
mbarashkov
e5d1ecd9f3 Jump to post from quote (#538)
* Jump to post from quote

* Add a setting to go to quoted message.

---------

Co-authored-by: Mikhail Barashkov <git@mbarashkov.ru>
2023-12-03 23:50:23 +03:00
Sebastian Wolf
42a7813776 Not only tablets have widescreen ;) 2023-12-03 23:50:21 +03:00
mbarashkov
627faba0db Improve chat UI on tablets by making messages narrower and limiting content items width as well. (#540)
Co-authored-by: Mikhail Barashkov <git@mbarashkov.ru>
2023-12-03 23:49:14 +03:00
Slava Monich
a4ebaf52f5 Fix broken signal-slot connection (#541)
QObject::connect: No such signal TDLibWrapper::chatAvailableReactionsUpdated(qlonglong, QString) in src/chatlistmodel.cpp:410
2023-12-03 23:46:48 +03:00
Sebastian Wolf
660fe527f2 Handle event updateChatAvailableReactions 2023-12-03 23:46:46 +03:00
Sebastian Wolf
1a0ed7f298 Only one star per list, restore users in poll results 2023-12-03 23:46:30 +03:00
062f5d3811 Merge pull request 'upstream_changes_251123' (#14) from upstream_changes_251123 into master
Reviewed-on: medvedych/harbour-fernschreiber#14
2023-11-25 13:50:00 +03:00
034c70542f Update build number 2023-11-25 13:49:08 +03:00
Patrick Hervieux
ef244c3319 Update French translation (#532)
Co-authored-by: Patrick Hervieux <patrick.hervieux+git@pherjung.ch>
2023-11-25 13:34:26 +03:00
free software
556505894c Update harbour-fernschreiber-es.ts (#531) 2023-11-25 13:34:11 +03:00
Sebastian Wolf
7c63ad66f9 Interaction hint for new reactions behavior 2023-11-25 13:33:45 +03:00
Sebastian Wolf
26772a48eb Show reactions on double-click 2023-11-25 13:33:13 +03:00
Slava Monich
2ded14deda Added "unread mention" indicator to the chat list (#530)
It's displayed in place of the "unread reaction" indicator. In case
if there are both unread mentions and reactions, "unread mention"
takes precedence.
2023-11-25 13:31:50 +03:00
free software
465c082328 Update harbour-fernschreiber-es.ts (#529)
* Update harbour-fernschreiber-es.ts

* Update harbour-fernschreiber-es.ts
2023-11-25 13:31:35 +03:00
Slava Monich
25e6660e8e Updated Russian translation (#528) 2023-11-25 13:31:25 +03:00
f58afe92cb Merge pull request 'upstream_changes' (#13) from upstream_changes into master
Reviewed-on: medvedych/harbour-fernschreiber#13
2023-11-21 14:24:05 +03:00
2b3ca0dff9 Update gitignore 2023-11-21 14:23:24 +03:00
Sebastian Wolf
1e88a31f90 Display reactions on last message again 2023-11-21 14:18:20 +03:00
Sebastian Wolf
026d32c92a Restore search in chats 2023-11-21 14:18:20 +03:00
Sebastian Wolf
a28b9df6b5 Minor tweaks for the new session timout setting 2023-11-21 14:18:12 +03:00
d2bde99e1f Merge pull request 'Revert executable rename' (#12) from revert_executable_rename into master
Reviewed-on: medvedych/harbour-fernschreiber#12
2023-11-20 22:31:56 +03:00
f8ffedb5db Revert executable rename 2023-11-20 19:31:56 +00:00
4b7d17c02f Merge pull request 'upstream_changes' (#11) from upstream_changes into master
Reviewed-on: medvedych/harbour-fernschreiber#11
2023-11-20 00:54:47 +03:00
0ae98f96b5 Release version ++ 2023-11-19 21:54:39 +00:00
Slava Monich
fb3d314ee2 Added UI for configuring session inactivity timeout (#527) 2023-11-20 00:50:41 +03:00
Sebastian Wolf
b032b32db1 More places affected by new username handling 2023-11-20 00:50:37 +03:00
Sebastian Wolf
63f4b37655 Hide unsupported emojis for reactions 2023-11-20 00:49:35 +03:00
Sebastian Wolf
bdc0423bf3 Make replies backward-compatible again 2023-11-20 00:49:12 +03:00
749f05816c Merge pull request 'adapt_to_changes_in_tdlib' (#10) from adapt_to_changes_in_tdlib into master
Reviewed-on: medvedych/harbour-fernschreiber#10
2023-11-19 16:35:16 +03:00
06ebe95309 Fix some validator warnings 2023-11-19 16:35:45 +03:00
Sebastian Wolf
40ec4b0968 Fix chat permissions handling 2023-11-19 16:16:46 +03:00
Slava Monich
c4c9dc83c0 Tweaked notification feedback settings UI (#526) 2023-11-19 16:16:36 +03:00
9a37db94ae Update tdlib headers 2023-11-19 16:11:29 +03:00
Sebastian Wolf
0ba3a8cd7f Trouble with usernames 2023-11-19 12:23:42 +03:00
Sebastian Wolf
0aeaf50c92 Some minor adjustments 2023-11-19 12:23:23 +03:00
Johannes Bachmann
a9947ff9f7 Always append last message content to notifications (#514)
* always append last message content to notifications

* make "always show notification" configurable

* add unfinished translations

* Fix spacing if no sender is printed
2023-11-19 12:22:22 +03:00
Peter G
c7324c020b Add option to suppress notification previews (#521)
* Add Switch in Settings

* Don't set notification preview body

* Support the setting in appSettings

* fixup! Add Switch in Settings

* Just show message count

* Also show only when notifications are enabled at all

---------

Co-authored-by: nephros <nemo@pgxperiiia10>
2023-11-19 12:22:13 +03:00
Peter G
46419b0960 Highlight unread Converstations (#513)
* Highlight unread conversations

See: #512

* make highlighting configurable

* more verbose variable names

* remove the rectangle gain, it is too annoying

* respect the setting

---------

Co-authored-by: nephros <nemo@pgxperiiia10>
2023-11-19 12:22:04 +03:00
Slava Monich
c1c8729023 Adapt to changes in reply message info (#525)
1.8.14:
https://github.com/tdlib/td/commit/fa94aba

1.8.21:
https://github.com/tdlib/td/commit/811a7c6
https://github.com/tdlib/td/commit/5216ea1
2023-11-19 12:21:17 +03:00
Sebastian Wolf
9bcc9ab690 Only expect chat partner information in private chats 2023-11-19 12:01:42 +03:00
Slava Monich
0b6a2db2f1 Adapt to changes in TdLib (#524)
* Adapt setTdlibParameters for TdLib > 1.8.5

For some reason tdlibParameters were inlined between 1.8.5 and 1.8.6
See https://github.com/tdlib/td/commit/f6a2ecd

* sponsoredMessage => sponsoredMessages in TdLib 1.8.8

See https://github.com/tdlib/td/commit/ec1310a

* Support another variant of messageReaction

The reaction field has changed from string to ReactionType somewhere
between 1.8.5 and 1.8.6

See https://github.com/tdlib/td/commit/b14708f

* Add support for new message reactions API

It has changed between 1.8.5 and 1.8.6

https://github.com/tdlib/td/commit/b14708f (ReactionType)
https://github.com/tdlib/td/commit/0b8e143 (ChatAvailableReactions)
https://github.com/tdlib/td/commit/6b2f6b4 (addMessageReaction)
https://github.com/tdlib/td/commit/d29d367 (updateActiveEmojiReactions)

etc.

* Highlight chosen reaction

* Support username in the new format

username attribute has been replaced with usernames in 1.8.8 and
now looks like this:

    "usernames": {
        "@type": "usernames",
        "active_usernames": [
            "whatever"
        ],
        "disabled_usernames": [
        ],
        "editable_username": "whatever"
    }

See https://github.com/tdlib/td/commit/897032e

* Support new reply_to message attribute

Since 1.8.15 it replaces reply_to_message_id and reply_in_chat_id.
Looks like this:

    "reply_to": {
        "@type": "messageReplyToMessage",
        "chat_id": -1001234567890,
        "is_quote_manual": false,
        "message_id": 234567890,
        "origin_send_date": 0
    },

See https://github.com/tdlib/td/commit/6116573

* Added support for MessageOrigin values

All of a sudden MessageForwardOrigin has been renamed into MessageOrigin
in TdLib 1.8.20 just because why not:

https://github.com/tdlib/td/commit/10c9e40
2023-11-19 12:01:27 +03:00
61a04b034a Merge pull request 'improve_unread_ballons' (#9) from sprainbrains/harbour-fernschreiber:improve_unread_ballons into master
Reviewed-on: medvedych/harbour-fernschreiber#9
2023-11-18 22:38:58 +03:00
jgibbon
ca42a5e7e0 Add specific unread info for higher counts of unread messages 2023-11-18 22:38:44 +03:00
400cda8dcd Merge pull request 'origin_merged' (#8) from sprainbrains/harbour-fernschreiber:origin_merged into master
Reviewed-on: medvedych/harbour-fernschreiber#8
2023-11-18 22:36:19 +03:00
fe50ead4ee Merged 2023-10-14 00:21:35 +03:00
f1717cbd29 Merge remote-tracking branch 'origin_sailfish/master' into origin_merged 2023-10-14 00:19:37 +03:00
Sebastian Wolf
5394fde136
Update chat list more reliably 2023-08-08 22:50:04 +02:00
Sebastian Wolf
29621b739a
Switch to icon-m-video as placeholder 2023-06-20 09:46:01 +02:00
55 changed files with 19558 additions and 2689 deletions

View file

@ -84,7 +84,7 @@ jobs:
assets+=("-a" "$asset") assets+=("-a" "$asset")
done done
tag_name="${GITHUB_REF##*/}" tag_name="${GITHUB_REF##*/}"
hub release create "${assets[@]}" -m "$tag_name" "$tag_name" gh release create "$tag_name" "${assets[@]}"
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -97,6 +97,6 @@ jobs:
assets+=("-a" "$asset") assets+=("-a" "$asset")
done done
tag_name="${GITHUB_REF##*/}" tag_name="${GITHUB_REF##*/}"
hub release create -p "${assets[@]}" -m "$tag_name" -m "This is a pre-release for testing purposes only. It may or may not be unstable." -m "Join the Telegram group to help out: https://github.com/Wunderfitz/harbour-fernschreiber/issues/162" "$tag_name" gh release create "$tag_name" -p -n "This is a pre-release for testing purposes only. It may or may not be unstable." "${assets[@]}"
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

3
.gitignore vendored
View file

@ -53,3 +53,6 @@ compile_commands.json
# TDLib API Secrets # TDLib API Secrets
tdlibsecrets.h tdlibsecrets.h
#Convinience scripts
*.sh

View file

@ -14,7 +14,11 @@ Fernschreiber wouldn't be the same without all the people helping in making it b
- Chat info page, performance improvements to chat page, location support, app initialization/registration with Telegram, project dependencies, emoji handling, qml/js optimizations, multi-message actions, i18n fixes, chat permission handling, code reviews, logging categories, bot support, github build: [jgibbon](https://github.com/jgibbon) - Chat info page, performance improvements to chat page, location support, app initialization/registration with Telegram, project dependencies, emoji handling, qml/js optimizations, multi-message actions, i18n fixes, chat permission handling, code reviews, logging categories, bot support, github build: [jgibbon](https://github.com/jgibbon)
- Copy message to clipboard: [Christian Stemmle](https://github.com/chstem) - Copy message to clipboard: [Christian Stemmle](https://github.com/chstem)
- Hide send message button if send-by-enter is switched on, focus text input on entering a chat: [santhoshmanikandan](https://github.com/santhoshmanikandan) - Hide send message button if send-by-enter is switched on, focus text input on entering a chat: [santhoshmanikandan](https://github.com/santhoshmanikandan)
- Integration of logout and sesison options to settings page: [Peter G.](https://github.com/nephros) - Integration of logout and sesison options to settings page, search results optimization, highlight unread conversations: [Peter G.](https://github.com/nephros)
- Option to always append last message in notifications: [Johannes Bachmann](https://github.com/dscheinah)
- Option to jump to quoted message, widescreen UI adjustments: [Mikhail Barashkov](https://github.com/mbarashkov)
This list might not be complete. In case I forgot something/somebody, please let me know or create a PR, thanks! :)
### Logo/Icon ### Logo/Icon
- Designed by [Matteo](https://github.com/iamnomeutente), adjustments by [Slava Monich](https://github.com/monich) - Designed by [Matteo](https://github.com/iamnomeutente), adjustments by [Slava Monich](https://github.com/monich)
@ -48,7 +52,7 @@ const char TDLIB_API_HASH[] = "1234567890abcdef1234567890abcdef";
You get the Telegram API ID and hash as soon as you've registered your own application on [https://my.telegram.org](https://my.telegram.org). You get the Telegram API ID and hash as soon as you've registered your own application on [https://my.telegram.org](https://my.telegram.org).
Moreover, you need to have a compiled version of [TDLib 1.8.3](https://github.com/tdlib/td) or higher in the sub-directory `tdlib`. This sub-directory must contain another sub-directory that fits to the target device architecture (e.g. armv7hl, i486). Within this directory, there needs to be a folder called `lib` that contains at least `libtdjson.so`. For armv7hl the relative path would consequently be `tdlib/armv7hl/lib`. Moreover, you need to have a compiled version of [TDLib 1.8.21](https://github.com/tdlib/td) or higher in the sub-directory `tdlib`. This sub-directory must contain another sub-directory that fits to the target device architecture (e.g. armv7hl, i486). Within this directory, there needs to be a folder called `lib` that contains at least `libtdjson.so`. For armv7hl the relative path would consequently be `tdlib/armv7hl/lib`.
You may just want to download the [tdlib.zip from our fork](https://github.com/Wunderfitz/td/releases) to just use the exact version of the latest official Fernschreiber release. To use it, you need to extract it into your local `tdlib/` folder as described above. If so, you're done and can compile Fernschreiber using the Sailfish SDK. If you want to build TDLib for yourself, please keep on reading. You may just want to download the [tdlib.zip from our fork](https://github.com/Wunderfitz/td/releases) to just use the exact version of the latest official Fernschreiber release. To use it, you need to extract it into your local `tdlib/` folder as described above. If so, you're done and can compile Fernschreiber using the Sailfish SDK. If you want to build TDLib for yourself, please keep on reading.

View file

@ -1,6 +1,6 @@
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
X-Nemo-Application-Type=generic X-Nemo-Application-Type=silica-qt5
Icon=harbour-fernschreiber Icon=harbour-fernschreiber
Exec=harbour-fernschreiber Exec=harbour-fernschreiber
Name=Fernschreiber Name=Fernschreiber

View file

@ -40,7 +40,7 @@ Loader {
property string chatId property string chatId
property string userName property string userName
property bool userNameIsValid: userName !== "" && inlineBotInformation && userName.toLowerCase() === inlineBotInformation.username.toLowerCase() property bool userNameIsValid: userName !== "" && inlineBotInformation && userName.toLowerCase() === inlineBotInformation.usernames.editable_username.toLowerCase()
property string query property string query
property int currentOffset: 0 property int currentOffset: 0
property string responseExtra: chatId+"|"+userName+"|"+query+"|"+currentOffset property string responseExtra: chatId+"|"+userName+"|"+query+"|"+currentOffset

View file

@ -47,6 +47,7 @@ ListItem {
readonly property bool canDeleteMessage: myMessage.can_be_deleted_for_all_users || (myMessage.can_be_deleted_only_for_self && myMessage.chat_id === page.myUserId) readonly property bool canDeleteMessage: myMessage.can_be_deleted_for_all_users || (myMessage.can_be_deleted_only_for_self && myMessage.chat_id === page.myUserId)
property bool hasContentComponent property bool hasContentComponent
property bool additionalOptionsOpened property bool additionalOptionsOpened
property bool wasNavigatedTo: false
readonly property var additionalItemsModel: (extraContentLoader.item && ("extraContextMenuItems" in extraContentLoader.item)) ? readonly property var additionalItemsModel: (extraContentLoader.item && ("extraContextMenuItems" in extraContentLoader.item)) ?
extraContentLoader.item.extraContextMenuItems : 0 extraContentLoader.item.extraContextMenuItems : 0
@ -64,9 +65,10 @@ ListItem {
readonly property bool showForwardMessageMenuItem: (baseContextMenuItemCount + 2) <= maxContextMenuItemCount readonly property bool showForwardMessageMenuItem: (baseContextMenuItemCount + 2) <= maxContextMenuItemCount
// And don't count "More Options..." for "Delete Message" if "Delete Message" is the only extra option // And don't count "More Options..." for "Delete Message" if "Delete Message" is the only extra option
readonly property bool haveSpaceForDeleteMessageMenuItem: (baseContextMenuItemCount + 3 - (deleteMessageIsOnlyExtraOption ? 1 : 0)) <= maxContextMenuItemCount readonly property bool haveSpaceForDeleteMessageMenuItem: (baseContextMenuItemCount + 3 - (deleteMessageIsOnlyExtraOption ? 1 : 0)) <= maxContextMenuItemCount
property var chatReactions
property var messageReactions property var messageReactions
highlighted: (down || isSelected || additionalOptionsOpened) && !menuOpen highlighted: (down || isSelected || additionalOptionsOpened || wasNavigatedTo) && !menuOpen
openMenuOnPressAndHold: !messageListItem.precalculatedValues.pageIsSelecting openMenuOnPressAndHold: !messageListItem.precalculatedValues.pageIsSelecting
signal replyToMessage() signal replyToMessage()
@ -94,20 +96,43 @@ ListItem {
} }
} }
function getInteractionText(viewCount, reactions) { function getInteractionText(viewCount, reactions, size, highlightColor) {
var interactionText = ""; var interactionText = "";
if (viewCount > 0) { if (viewCount > 0) {
interactionText = Emoji.emojify("👁️", Theme.fontSizeTiny) + Functions.getShortenedCount(viewCount); interactionText = Emoji.emojify("👁️ ", size) + Functions.getShortenedCount(viewCount);
} }
for (var i = 0; i < reactions.length; i++) { for (var i = 0; i < reactions.length; i++) {
interactionText += ( "&nbsp;" + Emoji.emojify(reactions[i].reaction, Theme.fontSizeTiny) ); var reaction = reactions[i]
if (!chatPage.isPrivateChat) { var reactionText = reaction.reaction ? reaction.reaction : (reaction.type && reaction.type.emoji) ? reaction.type.emoji : ""
interactionText += ( " " + Functions.getShortenedCount(reactions[i].total_count) ); if (reactionText) {
interactionText += ( "&nbsp;" + Emoji.emojify(reactionText, size) );
if (!chatPage.isPrivateChat) {
var count = Functions.getShortenedCount(reaction.total_count)
interactionText += " "
interactionText += (reaction.is_chosen ? ( "<font color='" + highlightColor + "'><b>" + count + "</b></font>" ) : count)
}
} }
} }
return interactionText; return interactionText;
} }
function openReactions() {
if (messageListItem.chatReactions) {
Debug.log("Using chat reactions")
messageListItem.messageReactions = chatReactions
showItemCompletelyTimer.requestedIndex = index;
showItemCompletelyTimer.start();
} else {
Debug.log("Obtaining message reactions")
tdLibWrapper.getMessageAvailableReactions(messageListItem.chatId, messageListItem.messageId);
}
selectReactionBubble.visible = false;
}
function getContentWidthMultiplier() {
return Functions.isWidescreen(appWindow) ? 0.4 : 1.0
}
onClicked: { onClicked: {
if (messageListItem.precalculatedValues.pageIsSelecting) { if (messageListItem.precalculatedValues.pageIsSelecting) {
page.toggleMessageSelection(myMessage); page.toggleMessageSelection(myMessage);
@ -125,12 +150,26 @@ ListItem {
if (messageListItem.messageReactions) { if (messageListItem.messageReactions) {
messageListItem.messageReactions = null; messageListItem.messageReactions = null;
selectReactionBubble.visible = false;
} else { } else {
tdLibWrapper.getMessageAvailableReactions(messageListItem.chatId, messageListItem.messageId); selectReactionBubble.visible = !selectReactionBubble.visible;
elementSelected(index);
} }
} }
} }
onDoubleClicked: {
if (messageListItem.chatReactions) {
Debug.log("Using chat reactions")
messageListItem.messageReactions = chatReactions
showItemCompletelyTimer.requestedIndex = index;
showItemCompletelyTimer.start();
} else {
Debug.log("Obtaining message reactions")
tdLibWrapper.getMessageAvailableReactions(messageListItem.chatId, messageListItem.messageId);
}
}
onPressAndHold: { onPressAndHold: {
if (openMenuOnPressAndHold) { if (openMenuOnPressAndHold) {
openContextMenu() openContextMenu()
@ -154,6 +193,25 @@ ListItem {
} }
} }
Connections {
target: chatPage
onResetElements: {
messageListItem.messageReactions = null;
selectReactionBubble.visible = false;
}
onElementSelected: {
if (elementIndex !== index) {
selectReactionBubble.visible = false;
}
}
onNavigatedTo: {
if (targetIndex === index) {
messageListItem.wasNavigatedTo = true;
restoreNormalityTimer.start();
}
}
}
Loader { Loader {
id: contextMenuLoader id: contextMenuLoader
active: false active: false
@ -258,6 +316,9 @@ ListItem {
messageListItem.messageReactions = null; messageListItem.messageReactions = null;
} }
} }
onReactionsUpdated: {
chatReactions = tdLibWrapper.getChatReactions(page.chatInformation.id);
}
} }
Timer { Timer {
@ -270,16 +331,34 @@ ListItem {
interval: 200 interval: 200
triggeredOnStart: false triggeredOnStart: false
onTriggered: { onTriggered: {
Debug.log("Show item completely timer triggered, requested index: " + requestedIndex + ", current index: " + index)
if (requestedIndex === index) { if (requestedIndex === index) {
chatView.highlightMoveDuration = -1; var p = chatView.contentItem.mapFromItem(reactionsColumn, 0, 0)
chatView.highlightResizeDuration = -1; if (chatView.contentY > p.y || p.y + reactionsColumn.height > chatView.contentY + chatView.height) {
chatView.scrollToIndex(requestedIndex); Debug.log("Moving reactions for item at", requestedIndex, "info the view")
chatView.highlightMoveDuration = 0; chatView.highlightMoveDuration = -1
chatView.highlightResizeDuration = 0; chatView.highlightResizeDuration = -1
chatView.scrollToIndex(requestedIndex, height <= chatView.height ? ListView.Contain : ListView.End)
chatView.highlightMoveDuration = 0
chatView.highlightResizeDuration = 0
}
} }
} }
} }
Timer {
id: restoreNormalityTimer
repeat: false
running: false
interval: 1000
triggeredOnStart: false
onTriggered: {
Debug.log("Restore normality for index " + index);
messageListItem.wasNavigatedTo = false;
}
}
Component.onCompleted: { Component.onCompleted: {
delegateComponentLoadingTimer.start(); delegateComponentLoadingTimer.start();
if (myMessage.reply_to_message_id) { if (myMessage.reply_to_message_id) {
@ -322,8 +401,10 @@ ListItem {
id: messageTextRow id: messageTextRow
spacing: Theme.paddingSmall spacing: Theme.paddingSmall
width: precalculatedValues.entryWidth width: precalculatedValues.entryWidth
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: Functions.isWidescreen(appWindow) ? undefined : parent.horizontalCenter
anchors.left: Functions.isWidescreen(appWindow) ? parent.left : undefined
y: Theme.paddingSmall y: Theme.paddingSmall
anchors.leftMargin: Functions.isWidescreen(appWindow) ? Theme.paddingMedium : undefined
Loader { Loader {
id: profileThumbnailLoader id: profileThumbnailLoader
@ -440,8 +521,12 @@ ListItem {
page.toggleMessageSelection(myMessage) page.toggleMessageSelection(myMessage)
} else { } else {
messageOptionsDrawer.open = false messageOptionsDrawer.open = false
messageOverlayLoader.overlayMessage = messageInReplyToRow.inReplyToMessage if(appSettings.goToQuotedMessage) {
messageOverlayLoader.active = true chatPage.showMessage(messageInReplyToRow.inReplyToMessage.id, true)
} else {
messageOverlayLoader.active = true
messageOverlayLoader.overlayMessage = messageInReplyToRow.inReplyToMessage
}
} }
} }
onPressAndHold: { onPressAndHold: {
@ -467,11 +552,12 @@ ListItem {
width: parent.width width: parent.width
Component.onCompleted: { Component.onCompleted: {
if (myMessage.forward_info.origin["@type"] === "messageForwardOriginChannel") { var originType = myMessage.forward_info.origin["@type"]
if (originType === "messageOriginChannel" || originType === "messageForwardOriginChannel") {
var otherChatInformation = tdLibWrapper.getChat(myMessage.forward_info.origin.chat_id); var otherChatInformation = tdLibWrapper.getChat(myMessage.forward_info.origin.chat_id);
forwardedThumbnail.photoData = (typeof otherChatInformation.photo !== "undefined") ? otherChatInformation.photo.small : {}; forwardedThumbnail.photoData = (typeof otherChatInformation.photo !== "undefined") ? otherChatInformation.photo.small : {};
forwardedChannelText.text = Emoji.emojify(otherChatInformation.title, Theme.fontSizeExtraSmall); forwardedChannelText.text = Emoji.emojify(otherChatInformation.title, Theme.fontSizeExtraSmall);
} else if (myMessage.forward_info.origin["@type"] === "messageForwardOriginUser") { } else if (originType === "messageOriginUser" || originType === "messageForwardOriginUser") {
var otherUserInformation = tdLibWrapper.getUserInformation(myMessage.forward_info.origin.sender_user_id); var otherUserInformation = tdLibWrapper.getUserInformation(myMessage.forward_info.origin.sender_user_id);
forwardedThumbnail.photoData = (typeof otherUserInformation.profile_photo !== "undefined") ? otherUserInformation.profile_photo.small : {}; forwardedThumbnail.photoData = (typeof otherUserInformation.profile_photo !== "undefined") ? otherUserInformation.profile_photo.small : {};
forwardedChannelText.text = Emoji.emojify(Functions.getUserName(otherUserInformation), Theme.fontSizeExtraSmall); forwardedChannelText.text = Emoji.emojify(Functions.getUserName(otherUserInformation), Theme.fontSizeExtraSmall);
@ -552,7 +638,7 @@ ListItem {
id: webPagePreviewLoader id: webPagePreviewLoader
active: false active: false
asynchronous: true asynchronous: true
width: parent.width width: parent.width * getContentWidthMultiplier()
height: (status === Loader.Ready) ? item.implicitHeight : myMessage.content.web_page ? precalculatedValues.webPagePreviewHeight : 0 height: (status === Loader.Ready) ? item.implicitHeight : myMessage.content.web_page ? precalculatedValues.webPagePreviewHeight : 0
sourceComponent: Component { sourceComponent: Component {
@ -566,7 +652,7 @@ ListItem {
Loader { Loader {
id: extraContentLoader id: extraContentLoader
width: parent.width width: parent.width * getContentWidthMultiplier()
asynchronous: true asynchronous: true
height: item ? item.height : (messageListItem.hasContentComponent ? chatView.getContentComponentHeight(model.content_type, myMessage.content, width) : 0) height: item ? item.height : (messageListItem.hasContentComponent ? chatView.getContentComponentHeight(model.content_type, myMessage.content, width) : 0)
} }
@ -625,7 +711,7 @@ ListItem {
height: ( ( chatPage.isChannel && messageViewCount > 0 ) || reactions.length > 0 ) ? ( Theme.fontSizeExtraSmall + Theme.paddingSmall ) : 0 height: ( ( chatPage.isChannel && messageViewCount > 0 ) || reactions.length > 0 ) ? ( Theme.fontSizeExtraSmall + Theme.paddingSmall ) : 0
sourceComponent: Component { sourceComponent: Component {
Label { Label {
text: getInteractionText(messageViewCount, reactions) text: getInteractionText(messageViewCount, reactions, font.pixelSize, Theme.highlightColor)
width: parent.width width: parent.width
font.pixelSize: Theme.fontSizeTiny font.pixelSize: Theme.fontSizeTiny
color: messageListItem.isOwnMessage ? Theme.secondaryHighlightColor : Theme.secondaryColor color: messageListItem.isOwnMessage ? Theme.secondaryHighlightColor : Theme.secondaryColor
@ -677,7 +763,7 @@ ListItem {
Image { Image {
id: emojiPicture id: emojiPicture
source: Emoji.getEmojiPath(modelData) source: Emoji.getEmojiPath(modelData)
width: Theme.fontSizeLarge width: status === Image.Ready ? Theme.fontSizeLarge : 0
height: Theme.fontSizeLarge height: Theme.fontSizeLarge
} }

View file

@ -40,9 +40,11 @@ Flickable {
function getOriginalAuthor(forwardInformation, fontSize) { function getOriginalAuthor(forwardInformation, fontSize) {
switch (forwardInformation.origin["@type"]) { switch (forwardInformation.origin["@type"]) {
case "messageOriginChannel":
case "messageForwardOriginChannel": case "messageForwardOriginChannel":
var otherChatInformation = tdLibWrapper.getChat(forwardInformation.origin.chat_id); var otherChatInformation = tdLibWrapper.getChat(forwardInformation.origin.chat_id);
return Emoji.emojify(otherChatInformation.title, fontSize); return Emoji.emojify(otherChatInformation.title, fontSize);
case "messageOriginUser":
case "messageForwardOriginUser": case "messageForwardOriginUser":
var otherUserInformation = tdLibWrapper.getUserInformation(forwardInformation.origin.sender_id.user_id); var otherUserInformation = tdLibWrapper.getUserInformation(forwardInformation.origin.sender_id.user_id);
return Emoji.emojify(Functions.getUserName(otherUserInformation), fontSize); return Emoji.emojify(Functions.getUserName(otherUserInformation), fontSize);

View file

@ -31,12 +31,12 @@ Loader {
property var botUserInformation: tdLibWrapper.getUserInformation(message.via_bot_user_id) property var botUserInformation: tdLibWrapper.getUserInformation(message.via_bot_user_id)
color: Theme.secondaryColor color: Theme.secondaryColor
font.pixelSize: Theme.fontSizeExtraSmall 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>") 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.usernames.editable_username, font.pixelSize)+"</a>")
textFormat: Text.RichText textFormat: Text.RichText
truncationMode: TruncationMode.Fade truncationMode: TruncationMode.Fade
onLinkActivated: { onLinkActivated: {
if(link === "userId://" + message.via_bot_user_id && botUserInformation.type.is_inline) { if(link === "userId://" + message.via_bot_user_id && botUserInformation.type.is_inline) {
newMessageTextField.text = "@"+botUserInformation.username+" " newMessageTextField.text = "@"+botUserInformation.usernames.editable_username+" "
newMessageTextField.cursorPosition = newMessageTextField.text.length newMessageTextField.cursorPosition = newMessageTextField.text.length
lostFocusTimer.start(); lostFocusTimer.start();
} }

View file

@ -1,6 +1,7 @@
import QtQuick 2.6 import QtQuick 2.6
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import WerkWolf.Fernschreiber 1.0 import WerkWolf.Fernschreiber 1.0
import "../js/functions.js" as Functions
ListItem { ListItem {
id: chatListViewItem id: chatListViewItem
@ -103,31 +104,42 @@ ListItem {
anchors.centerIn: chatUnreadMessagesCountBackground anchors.centerIn: chatUnreadMessagesCountBackground
visible: chatListViewItem.unreadCount > 0 visible: chatListViewItem.unreadCount > 0
opacity: isMuted ? Theme.opacityHigh : 1.0 opacity: isMuted ? Theme.opacityHigh : 1.0
text: chatListViewItem.unreadCount > 99 ? "99+" : chatListViewItem.unreadCount text: Functions.formatUnreadCount(chatListViewItem.unreadCount)
} }
Rectangle { Rectangle {
id: chatUnreadReactionCountBackground
color: isMuted ? ((Theme.colorScheme === Theme.DarkOnLight) ? "lightgray" : "dimgray") : Theme.highlightBackgroundColor color: isMuted ? ((Theme.colorScheme === Theme.DarkOnLight) ? "lightgray" : "dimgray") : Theme.highlightBackgroundColor
width: Theme.fontSizeLarge width: Theme.fontSizeLarge
height: Theme.fontSizeLarge height: Theme.fontSizeLarge
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
radius: parent.width / 2 radius: parent.width / 2
visible: chatListViewItem.unreadReactionCount > 0 visible: chatListViewItem.unreadReactionCount > 0 || chatListViewItem.unreadMentionCount > 0
}
Icon { Icon {
source: "image://theme/icon-s-favorite" source: "image://theme/icon-s-favorite"
height: Theme.iconSizeExtraSmall height: Theme.iconSizeExtraSmall
width: Theme.iconSizeExtraSmall width: Theme.iconSizeExtraSmall
highlighted: chatListViewItem.highlighted highlighted: chatListViewItem.highlighted
anchors.centerIn: chatUnreadReactionCountBackground anchors.centerIn: parent
visible: chatListViewItem.unreadReactionCount > 0 visible: chatListViewItem.unreadReactionCount > 0 && !chatListViewItem.unreadMentionCount
} }
Text {
font {
pixelSize: Theme.iconSizeExtraSmall
bold: true
}
color: Theme.primaryColor
anchors.centerIn: parent
visible: chatListViewItem.unreadMentionCount > 0
opacity: isMuted ? Theme.opacityHigh : 1.0
text: "@"
}
}
} }
} }
Column { Column {
id: contentColumn id: contentColumn
anchors { anchors {
@ -150,6 +162,9 @@ ListItem {
truncationMode: TruncationMode.Fade truncationMode: TruncationMode.Fade
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: Math.min(contentColumn.width - (verifiedImage.visible ? (verifiedImage.width + primaryTextRow.spacing) : 0) - (mutedImage.visible ? (mutedImage.width + primaryTextRow.spacing) : 0), implicitWidth) width: Math.min(contentColumn.width - (verifiedImage.visible ? (verifiedImage.width + primaryTextRow.spacing) : 0) - (mutedImage.visible ? (mutedImage.width + primaryTextRow.spacing) : 0), implicitWidth)
font.bold: appSettings.highlightUnreadConversations && ( !chatListViewItem.isMuted && (chatListViewItem.unreadCount > 0 || chatListViewItem.isMarkedAsUnread) )
font.italic: appSettings.highlightUnreadConversations && (chatListViewItem.unreadReactionCount > 0)
color: (appSettings.highlightUnreadConversations && (chatListViewItem.unreadCount > 0)) ? Theme.highlightColor : Theme.primaryColor
} }
Image { Image {

View file

@ -60,12 +60,12 @@ Column {
}, },
inlineKeyboardButtonTypeSwitchInline: function() { inlineKeyboardButtonTypeSwitchInline: function() {
if(modelData.type.in_current_chat) { if(modelData.type.in_current_chat) {
chatPage.setMessageText("@" + userInformation.username + " "+(modelData.type.query || "")) chatPage.setMessageText("@" + userInformation.usernames.editable_username + " "+(modelData.type.query || ""))
} else { } else {
pageStack.push(Qt.resolvedUrl("../pages/ChatSelectionPage.qml"), { pageStack.push(Qt.resolvedUrl("../pages/ChatSelectionPage.qml"), {
myUserId: chatPage.myUserId, myUserId: chatPage.myUserId,
payload: { neededPermissions: ["can_send_other_messages"], text:"@" + userInformation.username + " "+(modelData.type.query || "")}, payload: { neededPermissions: ["can_send_other_messages"], text:"@" + userInformation.usernames.editable_username + " "+(modelData.type.query || "")},
state: "fillTextArea" state: "fillTextArea"
}) })
} }

View file

@ -300,8 +300,8 @@ SilicaFlickable {
} }
leftMargin: imageContainer.getEased((imageContainer.minDimension + Theme.paddingMedium), 0, imageContainer.tweenFactor) + Theme.horizontalPageMargin leftMargin: imageContainer.getEased((imageContainer.minDimension + Theme.paddingMedium), 0, imageContainer.tweenFactor) + Theme.horizontalPageMargin
title: chatInformationPage.chatInformation.title !== "" ? Emoji.emojify(chatInformationPage.chatInformation.title, Theme.fontSizeLarge) : qsTr("Unknown") title: chatInformationPage.chatInformation.title !== "" ? Emoji.emojify(chatInformationPage.chatInformation.title, Theme.fontSizeLarge) : qsTr("Unknown")
description: ((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && chatInformationPage.privateChatUserInformation.username) description: ((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && chatInformationPage.privateChatUserInformation.usernames.editable_username)
? ("@"+chatInformationPage.privateChatUserInformation.username) : "" ? ("@"+chatInformationPage.privateChatUserInformation.usernames.editable_username) : ""
} }
SilicaFlickable { SilicaFlickable {

View file

@ -79,7 +79,7 @@ ChatInformationTabItemBase {
// chat title // chat title
primaryText.text: Emoji.emojify(Functions.getUserName(user), primaryText.font.pixelSize) primaryText.text: Emoji.emojify(Functions.getUserName(user), primaryText.font.pixelSize)
// last user // last user
prologSecondaryText.text: "@"+(user.username !== "" ? user.username : member_id.user_id) + (member_id.user_id === chatInformationPage.myUserId ? " " + qsTr("You") : "") prologSecondaryText.text: "@"+(user.username ? user.username : member_id.user_id) + (member_id.user_id === chatInformationPage.myUserId ? " " + qsTr("You") : "")
secondaryText { secondaryText {
horizontalAlignment: Text.AlignRight horizontalAlignment: Text.AlignRight
property string statusText: Functions.getChatMemberStatusText(model.status["@type"]) property string statusText: Functions.getChatMemberStatusText(model.status["@type"])
@ -180,6 +180,9 @@ ChatInformationTabItemBase {
for(var memberIndex in members) { for(var memberIndex in members) {
var memberData = members[memberIndex]; var memberData = members[memberIndex];
var userInfo = tdLibWrapper.getUserInformation(memberData.member_id.user_id) || {user:{}, bot_info:{}}; var userInfo = tdLibWrapper.getUserInformation(memberData.member_id.user_id) || {user:{}, bot_info:{}};
if (!userInfo.username && userInfo.usernames && userInfo.usernames.active_usernames) {
userInfo.username = userInfo.usernames.active_usernames[0]
}
memberData.user = userInfo; memberData.user = userInfo;
memberData.bot_info = memberData.bot_info || {}; memberData.bot_info = memberData.bot_info || {};
pageContent.membersList.append(memberData); pageContent.membersList.append(memberData);

View file

@ -27,7 +27,7 @@ MessageContentBase {
property var stickerData: messageListItem ? messageListItem.myMessage.content.sticker : overlayFlickable.overlayMessage.content.sticker; property var stickerData: messageListItem ? messageListItem.myMessage.content.sticker : overlayFlickable.overlayMessage.content.sticker;
readonly property bool asEmoji: appSettings.showStickersAsEmojis readonly property bool asEmoji: appSettings.showStickersAsEmojis
readonly property bool animated: stickerData.type["@type"] === "stickerTypeAnimated" && appSettings.animateStickers readonly property bool animated: stickerData.format["@type"] === "stickerFormatTgs" && appSettings.animateStickers
readonly property bool stickerVisible: staticStickerLoader.item ? staticStickerLoader.item.visible : readonly property bool stickerVisible: staticStickerLoader.item ? staticStickerLoader.item.visible :
animatedStickerLoader.item ? animatedStickerLoader.item.visible : false animatedStickerLoader.item ? animatedStickerLoader.item.visible : false
readonly property bool isOwnSticker : messageListItem ? messageListItem.isOwnMessage : overlayFlickable.isOwnMessage readonly property bool isOwnSticker : messageListItem ? messageListItem.isOwnMessage : overlayFlickable.isOwnMessage

View file

@ -19,9 +19,10 @@
import QtQuick 2.6 import QtQuick 2.6
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import "../../js/functions.js" as Functions
Grid { Grid {
width: parent.width - ( 2 * x ) width: parent.width - ( 2 * x )
columns: (appWindow.deviceOrientation & Orientation.LandscapeMask) || Screen.sizeCategory === Screen.Large || Screen.sizeCategory === Screen.ExtraLarge ? 2 : 1 columns: Functions.isWidescreen(appWindow) ? 2 : 1
readonly property real columnWidth: width/columns readonly property real columnWidth: width/columns
} }

View file

@ -70,6 +70,17 @@ AccordionItem {
} }
} }
TextSwitch {
width: parent.columnWidth
checked: appSettings.highlightUnreadConversations
text: qsTr("Highlight unread messages")
description: qsTr("Highlight Conversations with unread messages")
automaticCheck: false
onClicked: {
appSettings.highlightUnreadConversations = !checked
}
}
TextSwitch { TextSwitch {
width: parent.columnWidth width: parent.columnWidth
checked: appSettings.useOpenWith checked: appSettings.useOpenWith
@ -81,6 +92,28 @@ AccordionItem {
} }
} }
TextSwitch {
width: parent.columnWidth
checked: appSettings.notificationAlwaysShowPreview
text: qsTr("Always append message preview to notifications")
description: qsTr("In addition to showing the number of unread messages, the latest message will also be appended to notifications.")
automaticCheck: false
onClicked: {
appSettings.notificationAlwaysShowPreview = !checked
}
}
TextSwitch {
width: parent.columnWidth
checked: appSettings.goToQuotedMessage
text: qsTr("Go to quoted message")
description: qsTr("When tapping a quoted message, open it in chat instead of showing it in an overlay.")
automaticCheck: false
onClicked: {
appSettings.goToQuotedMessage = !checked
}
}
ComboBox { ComboBox {
id: feedbackComboBox id: feedbackComboBox
width: parent.columnWidth width: parent.columnWidth
@ -135,35 +168,53 @@ AccordionItem {
} }
} }
TextSwitch { Item {
width: parent.columnWidth // Occupies one grid cell so that the column ends up under the combo box
checked: appSettings.notificationTurnsDisplayOn && enabled // in the landscape layout
text: qsTr("Notification turns on the display") visible: parent.columns === 2
enabled: appSettings.notificationFeedback !== AppSettings.NotificationFeedbackNone width: 1
height: enabled ? implicitHeight: 0 height: 1
clip: height < implicitHeight
visible: height > 0
automaticCheck: false
onClicked: {
appSettings.notificationTurnsDisplayOn = !checked
}
Behavior on height { SmoothedAnimation { duration: 200 } }
} }
TextSwitch { Column {
width: parent.columnWidth
checked: appSettings.notificationSoundsEnabled && enabled
text: qsTr("Enable notification sounds")
description: qsTr("When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.")
enabled: appSettings.notificationFeedback !== AppSettings.NotificationFeedbackNone enabled: appSettings.notificationFeedback !== AppSettings.NotificationFeedbackNone
width: parent.columnWidth
height: enabled ? implicitHeight: 0 height: enabled ? implicitHeight: 0
clip: height < implicitHeight clip: height < implicitHeight
visible: height > 0 visible: height > 0
automaticCheck: false
onClicked: {
appSettings.notificationSoundsEnabled = !checked
}
Behavior on height { SmoothedAnimation { duration: 200 } } Behavior on height { SmoothedAnimation { duration: 200 } }
TextSwitch {
checked: appSettings.notificationSuppressContent && enabled
text: qsTr("Hide content in notifications")
enabled: parent.enabled
automaticCheck: false
onClicked: {
appSettings.notificationSuppressContent = !checked
}
}
TextSwitch {
checked: appSettings.notificationTurnsDisplayOn && enabled
text: qsTr("Notification turns on the display")
enabled: parent.enabled
automaticCheck: false
onClicked: {
appSettings.notificationTurnsDisplayOn = !checked
}
}
TextSwitch {
checked: appSettings.notificationSoundsEnabled && enabled
text: qsTr("Enable notification sounds")
description: qsTr("When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.")
enabled: parent.enabled
automaticCheck: false
onClicked: {
appSettings.notificationSoundsEnabled = !checked
}
}
} }
} }
} }

View file

@ -30,153 +30,183 @@ AccordionItem {
Column { Column {
id: activeSessionsItem id: activeSessionsItem
bottomPadding: Theme.paddingMedium bottomPadding: Theme.paddingMedium
property variant activeSessions; property variant activeSessions
property bool loaded : false; property int inactiveSessionsTtlDays
Component.onCompleted: { Component.onCompleted: {
if (!activeSessions) { if (!activeSessions) {
tdLibWrapper.getActiveSessions(); tdLibWrapper.getActiveSessions();
} else {
activeSessionsItem.loaded = true;
} }
} }
Connections { Connections {
target: tdLibWrapper target: tdLibWrapper
onSessionsReceived: { onSessionsReceived: {
activeSessionsItem.activeSessions = sessions; activeSessionsItem.activeSessions = sessions
activeSessionsItem.loaded = true; activeSessionsItem.inactiveSessionsTtlDays = inactive_session_ttl_days
} }
onOkReceived: { onOkReceived: {
if (request === "terminateSession") { if (request === "terminateSession") {
appNotification.show(qsTr("Session was terminated")); appNotification.show(qsTr("Session was terminated"));
activeSessionsItem.loaded = false;
tdLibWrapper.getActiveSessions(); tdLibWrapper.getActiveSessions();
} }
} }
} }
Loader { Loader {
id: sessionInformationLoader
active: tdLibWrapper.authorizationState === TelegramAPI.AuthorizationReady active: tdLibWrapper.authorizationState === TelegramAPI.AuthorizationReady
width: parent.width width: parent.width
sourceComponent: Component { sourceComponent: Component {
SilicaListView { Column {
id: activeSessionsListView BusyIndicator {
width: parent.width anchors.horizontalCenter: parent.horizontalCenter
height: contentHeight running: !activeSessionsListView.count && !activeSessionsItem.inactiveSessionsTtlDays
model: activeSessionsItem.activeSessions size: BusyIndicatorSize.Medium
headerPositioning: ListView.OverlayHeader visible: opacity > 0
header: Separator { height: running ? implicitHeight : 0
width: parent.width
color: Theme.primaryColor
horizontalAlignment: Qt.AlignHCenter
} }
delegate: ListItem {
id: activeSessionListItem SilicaListView {
id: activeSessionsListView
width: parent.width width: parent.width
contentHeight: activeSessionColumn.height + ( 2 * Theme.paddingMedium ) height: contentHeight
model: activeSessionsItem.activeSessions
menu: ContextMenu { headerPositioning: ListView.OverlayHeader
hasContent: !modelData.is_current header: Separator {
onHeightChanged: {
if (parent && flickable) {
// Make sure we are inside the screen area
var bottom = parent.mapToItem(flickable, x, y).y + height
if (bottom > flickable.height) {
flickable.contentY += bottom - flickable.height
}
}
}
MenuItem {
onClicked: {
var sessionId = modelData.id;
Remorse.itemAction(activeSessionListItem, qsTr("Terminating session"), function() { tdLibWrapper.terminateSession(sessionId); });
}
text: qsTr("Terminate Session")
}
}
Column {
id: activeSessionColumn
width: parent.width - ( 2 * Theme.horizontalPageMargin )
spacing: Theme.paddingSmall
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Label {
width: parent.width
text: qsTr("This app")
font.pixelSize: Theme.fontSizeMedium
font.bold: true
visible: modelData.is_current
color: Theme.highlightColor
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Label {
width: parent.width
text: modelData.application_name + " " + modelData.application_version
font.pixelSize: Theme.fontSizeMedium
font.bold: true
color: Theme.primaryColor
maximumLineCount: 1
elide: Text.ElideRight
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Label {
width: parent.width
text: modelData.device_model + ", " + (modelData.platform + " " + modelData.system_version).trim()
font.pixelSize: Theme.fontSizeSmall
color: Theme.primaryColor
maximumLineCount: 1
truncationMode: TruncationMode.Fade
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Label {
width: parent.width
text: qsTr("IP address: %1, origin: %2").arg(modelData.ip).arg(modelData.country)
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.secondaryColor
maximumLineCount: 1
truncationMode: TruncationMode.Fade
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Label {
width: parent.width
text: qsTr("Active since: %1, last online: %2").arg(Functions.getDateTimeTimepoint(modelData.log_in_date)).arg(Functions.getDateTimeElapsed(modelData.last_active_date))
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.primaryColor
maximumLineCount: 1
truncationMode: TruncationMode.Fade
anchors {
horizontalCenter: parent.horizontalCenter
}
}
}
Separator {
id: separator
anchors {
bottom: parent.bottom
}
width: parent.width width: parent.width
color: Theme.primaryColor color: Theme.primaryColor
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
visible: activeSessionsListView.count > 0
}
delegate: ListItem {
id: activeSessionListItem
width: parent.width
contentHeight: activeSessionColumn.height + ( 2 * Theme.paddingMedium )
menu: ContextMenu {
hasContent: !modelData.is_current
onHeightChanged: {
if (parent && flickable) {
// Make sure we are inside the screen area
var bottom = parent.mapToItem(flickable, x, y).y + height
if (bottom > flickable.height) {
flickable.contentY += bottom - flickable.height
}
}
}
MenuItem {
onClicked: {
var sessionId = modelData.id;
Remorse.itemAction(activeSessionListItem, qsTr("Terminating session"), function() { tdLibWrapper.terminateSession(sessionId); });
}
text: qsTr("Terminate Session")
}
}
Column {
id: activeSessionColumn
width: parent.width - ( 2 * Theme.horizontalPageMargin )
spacing: Theme.paddingSmall
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Label {
width: parent.width
text: qsTr("This app")
font.pixelSize: Theme.fontSizeMedium
font.bold: true
visible: modelData.is_current
color: Theme.highlightColor
}
Label {
width: parent.width
text: modelData.application_name + " " + modelData.application_version
font.pixelSize: Theme.fontSizeMedium
font.bold: true
maximumLineCount: 1
elide: Text.ElideRight
}
Label {
width: parent.width
text: modelData.device_model + ", " + (modelData.platform + " " + modelData.system_version).trim()
font.pixelSize: Theme.fontSizeSmall
maximumLineCount: 1
truncationMode: TruncationMode.Fade
}
Label {
width: parent.width
text: qsTr("Active since: %1, last online: %2").arg(Functions.getDateTimeTimepoint(modelData.log_in_date)).arg(Functions.getDateTimeElapsed(modelData.last_active_date))
font.pixelSize: Theme.fontSizeExtraSmall
maximumLineCount: 1
truncationMode: TruncationMode.Fade
}
}
Separator {
anchors {
bottom: parent.bottom
}
width: parent.width
color: Theme.primaryColor
horizontalAlignment: Qt.AlignHCenter
}
}
}
ComboBox {
readonly property int ttl: activeSessionsItem.inactiveSessionsTtlDays
label: qsTr("Session Timeout")
description: qsTr("Inactive sessions will be terminated after this timeframe")
value: (currentItem && currentItem.text) ? currentItem.text : qsTr("%1 day(s)", "", ttl).arg(ttl)
visible: ttl > 0
menu: ContextMenu {
id: ttlMenu
MenuItem {
readonly property int days: 7
text: qsTr("1 week")
onClicked: tdLibWrapper.setInactiveSessionTtl(days)
}
MenuItem {
readonly property int days: 30
text: qsTr("1 month")
onClicked: tdLibWrapper.setInactiveSessionTtl(days)
}
MenuItem {
readonly property int days: 90
text: qsTr("3 months")
onClicked: tdLibWrapper.setInactiveSessionTtl(days)
}
MenuItem {
readonly property int days: 180
text: qsTr("6 months")
onClicked: tdLibWrapper.setInactiveSessionTtl(days)
}
MenuItem {
readonly property int days: 365
text: qsTr("1 year")
onClicked: tdLibWrapper.setInactiveSessionTtl(days)
}
} }
Component.onCompleted: updateSelection()
onTtlChanged: updateSelection()
function updateSelection() {
var menuItems = ttlMenu.children
var n = menuItems.length
for (var i = 0; i < n; i++) {
if (menuItems[i].days === ttl) {
currentIndex = i
return
}
}
currentIndex = -1
}
} }
} }
} }

View file

@ -143,7 +143,7 @@ AccordionItem {
visible: true visible: true
canEdit: true canEdit: true
headerText: qsTr("Username", "user name of the logged-in profile - header") headerText: qsTr("Username", "user name of the logged-in profile - header")
text: userInformation.username text: userInformation.usernames.editable_username
width: parent.columnWidth width: parent.columnWidth
headerLeftAligned: true headerLeftAligned: true

View file

@ -27,6 +27,14 @@ function setGlobals(globals) {
tdLibWrapper = globals.tdLibWrapper; tdLibWrapper = globals.tdLibWrapper;
appNotification = globals.appNotification; appNotification = globals.appNotification;
} }
function formatUnreadCount(value) {
if(value < 1000) {
return value;
} else if(value > 9000) {
return '9k+';
}
return ''+Math.floor(value / 1000)+'k'+((value % 1000)>0 ? '+' : '');
}
function getUserName(userInformation) { function getUserName(userInformation) {
return ((userInformation.first_name || "") + " " + (userInformation.last_name || "")).trim(); return ((userInformation.first_name || "") + " " + (userInformation.last_name || "")).trim();
@ -527,3 +535,7 @@ function getMessagesNeededForwardPermissions(messages) {
} }
return neededPermissions return neededPermissions
} }
function isWidescreen(appWindow) {
return (appWindow.deviceOrientation & Silica.Orientation.LandscapeMask) || Silica.Screen.sizeCategory === Silica.Screen.Large || Silica.Screen.sizeCategory === Silica.Screen.ExtraLarge
}

View file

@ -54,6 +54,7 @@ Page {
property bool iterativeInitialization: false; property bool iterativeInitialization: false;
property var messageToShow; property var messageToShow;
property string messageIdToShow; property string messageIdToShow;
property string messageIdToScrollTo;
readonly property bool userIsMember: ((isPrivateChat || isSecretChat) && chatInformation["@type"]) || // should be optimized readonly property bool userIsMember: ((isPrivateChat || isSecretChat) && chatInformation["@type"]) || // should be optimized
(isBasicGroup || isSuperGroup) && ( (isBasicGroup || isSuperGroup) && (
(chatGroupInformation.status["@type"] === "chatMemberStatusMember") (chatGroupInformation.status["@type"] === "chatMemberStatusMember")
@ -63,9 +64,13 @@ 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_basic_messages")
property bool doSendBotStartMessage property bool doSendBotStartMessage
property string sendBotStartMessageParameter property string sendBotStartMessageParameter
property var availableReactions
signal resetElements()
signal elementSelected(int elementIndex)
signal navigatedTo(int targetIndex)
states: [ states: [
State { State {
@ -184,7 +189,7 @@ Page {
} }
tdLibWrapper.getChatPinnedMessage(chatInformation.id); tdLibWrapper.getChatPinnedMessage(chatInformation.id);
tdLibWrapper.toggleChatIsMarkedAsUnread(chatInformation.id, false); tdLibWrapper.toggleChatIsMarkedAsUnread(chatInformation.id, false);
availableReactions = tdLibWrapper.getChatReactions(chatInformation.id);
} }
function getMessageStatusText(message, listItemIndex, lastReadSentIndex, useElapsed) { function getMessageStatusText(message, listItemIndex, lastReadSentIndex, useElapsed) {
@ -406,6 +411,24 @@ Page {
chatPage.focus = true; chatPage.focus = true;
} }
function showMessage(messageId, initialRun) {
// Means we tapped a quoted message and had to load it.
if(initialRun) {
chatPage.messageIdToScrollTo = messageId
}
if (chatPage.messageIdToScrollTo && chatPage.messageIdToScrollTo != "") {
var index = chatModel.getMessageIndex(chatPage.messageIdToScrollTo);
if(index !== -1) {
chatPage.messageIdToScrollTo = "";
chatView.scrollToIndex(index);
navigatedTo(index);
} else if(initialRun) {
// we only want to do this once.
chatModel.triggerLoadHistoryForMessage(chatPage.messageIdToScrollTo)
}
}
}
Timer { Timer {
id: forwardMessagesTimer id: forwardMessagesTimer
interval: 200 interval: 200
@ -439,7 +462,8 @@ Page {
Component.onDestruction: { Component.onDestruction: {
if (chatPage.canSendMessages && !chatPage.isDeletedUser) { if (chatPage.canSendMessages && !chatPage.isDeletedUser) {
tdLibWrapper.setChatDraftMessage(chatInformation.id, 0, newMessageColumn.replyToMessageId, newMessageTextField.text); tdLibWrapper.setChatDraftMessage(chatInformation.id, 0, newMessageColumn.replyToMessageId, newMessageTextField.text,
newMessageInReplyToRow.inReplyToMessage ? newMessageInReplyToRow.inReplyToMessage.id : 0);
} }
fernschreiberUtils.stopGeoLocationUpdates(); fernschreiberUtils.stopGeoLocationUpdates();
tdLibWrapper.closeChat(chatInformation.id); tdLibWrapper.closeChat(chatInformation.id);
@ -478,7 +502,10 @@ Page {
if (pageStack.depth === 1) { if (pageStack.depth === 1) {
// Only clear chat model if navigated back to overview page. In other cases we keep the information... // Only clear chat model if navigated back to overview page. In other cases we keep the information...
chatModel.clear(); chatModel.clear();
} else {
resetElements();
} }
break; break;
} }
} }
@ -562,12 +589,12 @@ Page {
} }
} }
onUserFullInfoReceived: { onUserFullInfoReceived: {
if(userFullInfo["@extra"] === chatPartnerInformation.id.toString()) { if ((isPrivateChat || isSecretChat) && userFullInfo["@extra"] === chatPartnerInformation.id.toString()) {
chatPage.botInformation = userFullInfo; chatPage.botInformation = userFullInfo;
} }
} }
onUserFullInfoUpdated: { onUserFullInfoUpdated: {
if(userId === chatPartnerInformation.id) { if ((isPrivateChat || isSecretChat) && userId === chatPartnerInformation.id) {
chatPage.botInformation = userFullInfo; chatPage.botInformation = userFullInfo;
} }
} }
@ -607,6 +634,15 @@ Page {
chatViewCooldownTimer.restart(); chatViewCooldownTimer.restart();
chatViewStartupReadTimer.restart(); chatViewStartupReadTimer.restart();
var remainingDoubleTapHints = appSettings.remainingDoubleTapHints;
Debug.log("Remaining double tap hints: " + remainingDoubleTapHints);
if (remainingDoubleTapHints > 0) {
doubleTapHintTimer.start();
tapHint.visible = true;
tapHintLabel.visible = true;
appSettings.remainingDoubleTapHints = remainingDoubleTapHints - 1;
}
} }
onNewMessageReceived: { onNewMessageReceived: {
if (( chatView.manuallyScrolledToBottom && Qt.application.state === Qt.ApplicationActive ) || message.sender_id.user_id === chatPage.myUserId) { if (( chatView.manuallyScrolledToBottom && Qt.application.state === Qt.ApplicationActive ) || message.sender_id.user_id === chatPage.myUserId) {
@ -618,8 +654,8 @@ Page {
onUnreadCountUpdated: { onUnreadCountUpdated: {
Debug.log("[ChatPage] Unread count updated, new count: ", unreadCount); Debug.log("[ChatPage] Unread count updated, new count: ", unreadCount);
chatInformation.unread_count = unreadCount; chatInformation.unread_count = unreadCount;
chatUnreadMessagesItem.visible = ( !chatPage.loading && chatInformation.unread_count > 0 && chatOverviewItem.visible ); chatUnreadMessagesItem.visible = ( !chatPage.loading && unreadCount > 0 && chatOverviewItem.visible );
chatUnreadMessagesCount.text = unreadCount > 99 ? "99+" : unreadCount; chatUnreadMessagesCount.text = Functions.formatUnreadCount(unreadCount)
} }
onLastReadSentMessageUpdated: { onLastReadSentMessageUpdated: {
Debug.log("[ChatPage] Updating last read sent index, new index: ", lastReadSentIndex); Debug.log("[ChatPage] Updating last read sent index, new index: ", lastReadSentIndex);
@ -634,6 +670,8 @@ Page {
if (chatView.height > chatView.contentHeight) { if (chatView.height > chatView.contentHeight) {
Debug.log("[ChatPage] Chat content quite small..."); Debug.log("[ChatPage] Chat content quite small...");
viewMessageTimer.queueViewMessage(chatView.count - 1); viewMessageTimer.queueViewMessage(chatView.count - 1);
} else if (chatPage.messageIdToScrollTo && chatPage.messageIdToScrollTo != "") {
showMessage(chatPage.messageIdToScrollTo, false)
} }
chatViewCooldownTimer.restart(); chatViewCooldownTimer.restart();
chatViewStartupReadTimer.restart(); chatViewStartupReadTimer.restart();
@ -1170,7 +1208,7 @@ Page {
readonly property int profileThumbnailDimensions: showUserInfo ? Theme.itemSizeSmall : 0 readonly property int profileThumbnailDimensions: showUserInfo ? Theme.itemSizeSmall : 0
readonly property int pageMarginDouble: 2 * Theme.horizontalPageMargin readonly property int pageMarginDouble: 2 * Theme.horizontalPageMargin
readonly property int paddingMediumDouble: 2 * Theme.paddingMedium readonly property int paddingMediumDouble: 2 * Theme.paddingMedium
readonly property int entryWidth: chatView.width - pageMarginDouble //ширина полной строки сообщения вместе с аватаркой readonly property int entryWidth: chatView.width - pageMarginDouble
readonly property int textItemWidth: entryWidth - profileThumbnailDimensions - Theme.paddingSmall readonly property int textItemWidth: entryWidth - profileThumbnailDimensions - Theme.paddingSmall
readonly property int backgroundWidth: page.isPrivateChat ? textItemWidth - pageMarginDouble : textItemWidth //уменьшенная ширина сообщений для приватных чатов readonly property int backgroundWidth: page.isPrivateChat ? textItemWidth - pageMarginDouble : textItemWidth //уменьшенная ширина сообщений для приватных чатов
readonly property int backgroundRadius: textItemWidth/50 readonly property int backgroundRadius: textItemWidth/50
@ -1196,10 +1234,9 @@ Page {
manuallyScrolledToBottom = chatView.atYEnd manuallyScrolledToBottom = chatView.atYEnd
} }
function scrollToIndex(index) { function scrollToIndex(index, mode) {
if(index > 0 && index < chatView.count) { if(index > 0 && index < chatView.count) {
positionViewAtIndex(index, ListView.Contain) positionViewAtIndex(index, (mode === undefined) ? ListView.Contain : mode)
// currentIndex = index;
if(index === chatView.count - 1) { if(index === chatView.count - 1) {
manuallyScrolledToBottom = true; manuallyScrolledToBottom = true;
} }
@ -1348,6 +1385,7 @@ Page {
messageId: model.message_id messageId: model.message_id
messageViewCount: model.view_count messageViewCount: model.view_count
reactions: model.reactions reactions: model.reactions
chatReactions: availableReactions
messageIndex: model.index messageIndex: model.index
hasContentComponent: !!myMessage.content && chatView.delegateMessagesContent.indexOf(model.content_type) > -1 hasContentComponent: !!myMessage.content && chatView.delegateMessagesContent.indexOf(model.content_type) > -1
canReplyToMessage: chatPage.canSendMessages canReplyToMessage: chatPage.canSendMessages
@ -1427,7 +1465,7 @@ Page {
color: Theme.primaryColor color: Theme.primaryColor
anchors.centerIn: chatUnreadMessagesCountBackground anchors.centerIn: chatUnreadMessagesCountBackground
visible: chatUnreadMessagesItem.visible visible: chatUnreadMessagesItem.visible
text: chatInformation.unread_count > 99 ? "99+" : chatInformation.unread_count text: Functions.formatUnreadCount(chatInformation.unread_count)
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@ -1596,7 +1634,7 @@ Page {
IconButton { IconButton {
id: attachImageIconButton id: attachImageIconButton
visible: chatPage.hasSendPrivilege("can_send_media_messages") visible: chatPage.hasSendPrivilege("can_send_photos")
icon.source: "image://theme/icon-m-image" icon.source: "image://theme/icon-m-image"
onClicked: { onClicked: {
var picker = pageStack.push("Sailfish.Pickers.ImagePickerPage", { var picker = pageStack.push("Sailfish.Pickers.ImagePickerPage", {
@ -1612,7 +1650,7 @@ Page {
} }
} }
IconButton { IconButton {
visible: chatPage.hasSendPrivilege("can_send_media_messages") visible: chatPage.hasSendPrivilege("can_send_videos")
icon.source: "image://theme/icon-m-video" icon.source: "image://theme/icon-m-video"
onClicked: { onClicked: {
var picker = pageStack.push("Sailfish.Pickers.VideoPickerPage", { var picker = pageStack.push("Sailfish.Pickers.VideoPickerPage", {
@ -1628,7 +1666,7 @@ Page {
} }
} }
IconButton { IconButton {
visible: chatPage.hasSendPrivilege("can_send_media_messages") visible: chatPage.hasSendPrivilege("can_send_voice_notes")
icon.source: "image://theme/icon-m-mic" icon.source: "image://theme/icon-m-mic"
icon.sourceSize { icon.sourceSize {
width: Theme.iconSizeMedium width: Theme.iconSizeMedium
@ -1641,7 +1679,7 @@ Page {
} }
} }
IconButton { IconButton {
visible: chatPage.hasSendPrivilege("can_send_media_messages") visible: chatPage.hasSendPrivilege("can_send_documents")
icon.source: "image://theme/icon-m-document" icon.source: "image://theme/icon-m-document"
onClicked: { onClicked: {
var picker = pageStack.push("Sailfish.Pickers.FilePickerPage", { var picker = pageStack.push("Sailfish.Pickers.FilePickerPage", {
@ -2154,4 +2192,31 @@ Page {
} }
} }
} }
Timer {
id: doubleTapHintTimer
running: true
triggeredOnStart: false
repeat: false
interval: 6000
onTriggered: {
tapHint.visible = false;
tapHintLabel.visible = false;
}
}
TapInteractionHint {
id: tapHint
loops: Animation.Infinite
taps: 2
anchors.centerIn: parent
visible: false
}
InteractionHintLabel {
id: tapHintLabel
anchors.bottom: parent.bottom
text: qsTr("Double-tap on a message to choose a reaction")
visible: false
}
} }

View file

@ -143,10 +143,11 @@ Page {
Connections { Connections {
target: tdLibWrapper target: tdLibWrapper
onUsersReceived: { onMessageSendersReceived: {
Debug.log("Received poll users...")
if(extra === optionDelegate.usersResponseIdentifierString) { if(extra === optionDelegate.usersResponseIdentifierString) {
for(var i = 0; i < userIds.length; i += 1) { for(var i = 0; i < senders.length; i += 1) {
optionDelegate.users.append({id: userIds[i], user:tdLibWrapper.getUserInformation(userIds[i])}); optionDelegate.users.append({id: senders[i].user_id, user:tdLibWrapper.getUserInformation(senders[i].user_id)});
} }
loadUsersTimer.start(); loadUsersTimer.start();
} }

View file

@ -12,6 +12,26 @@
# * date Author's Name <author's email> version-release # * date Author's Name <author's email> version-release
# - Summary of changes # - Summary of changes
* Sun Dec 03 2023 Sebastian J. Wolf <sebastian@ygriega.de> 0.17
- Update to TDLib 1.8.21, expect some hiccups ;)
- Added contacts sync (OpenRepos builds only)
- Tweaks to reaction handling (opens now on double click or click + star)
- Option to jump to quoted message
- Option to highlight unread conversations
- Option to suppress notification previews
- Option to append last message content to notifications
- Added "unread mention" indicator to chat list
- Improve message when search yields no results
- New unread info for chats with high amount of unread messages
- Setting for session inactivity timeout
- UI improvements in landscape mode
- Fix: Restore video functionality on SFOS 4.5
- Fix: Chat list timestamp now updated more reliably
- Fix: Faster reconnect after network changes
- Fix: Some URLs couldn't be opened
- Updated translations for several languages
- Thanks to monich, nephros, arustg, jgibbon, carlosgonz0, okruhliak, dscheinah, pherjung and mbarashkov for your contributions
* Sun Jun 12 2022 Sebastian J. Wolf <sebastian@ygriega.de> 0.16 * Sun Jun 12 2022 Sebastian J. Wolf <sebastian@ygriega.de> 0.16
- Support message reactions - Support message reactions
- Support t.me/+... links - Support t.me/+... links

View file

@ -10,9 +10,9 @@ Name: harbour-fernschreiber
%define __requires_exclude ^lib(tdjson|ssl|crypto).*$ %define __requires_exclude ^lib(tdjson|ssl|crypto).*$
# << macros # << macros
Summary: Fernschreiber is a Telegram client for Sailfish OS Summary: Fernschreiber is a Telegram client for Aurora OS
Version: 0.17 Version: 0.17
Release: 3 Release: 12
Group: Qt/Qt Group: Qt/Qt
License: LICENSE License: LICENSE
URL: http://werkwolf.eu/ URL: http://werkwolf.eu/

View file

@ -29,14 +29,19 @@ namespace {
const QString KEY_ANIMATE_STICKERS("animateStickers"); const QString KEY_ANIMATE_STICKERS("animateStickers");
const QString KEY_NOTIFICATION_TURNS_DISPLAY_ON("notificationTurnsDisplayOn"); const QString KEY_NOTIFICATION_TURNS_DISPLAY_ON("notificationTurnsDisplayOn");
const QString KEY_NOTIFICATION_SOUNDS_ENABLED("notificationSoundsEnabled"); const QString KEY_NOTIFICATION_SOUNDS_ENABLED("notificationSoundsEnabled");
const QString KEY_NOTIFICATION_SUPPRESS_ENABLED("notificationSuppressContent");
const QString KEY_NOTIFICATION_FEEDBACK("notificationFeedback"); const QString KEY_NOTIFICATION_FEEDBACK("notificationFeedback");
const QString KEY_NOTIFICATION_ALWAYS_SHOW_PREVIEW("notificationAlwaysShowPreview");
const QString KEY_GO_TO_QUOTED_MESSAGE("goToQuotedMessage");
const QString KEY_STORAGE_OPTIMIZER("useStorageOptimizer"); const QString KEY_STORAGE_OPTIMIZER("useStorageOptimizer");
const QString KEY_INLINEBOT_LOCATION_ACCESS("allowInlineBotLocationAccess"); const QString KEY_INLINEBOT_LOCATION_ACCESS("allowInlineBotLocationAccess");
const QString KEY_REMAINING_INTERACTION_HINTS("remainingInteractionHints"); const QString KEY_REMAINING_INTERACTION_HINTS("remainingInteractionHints");
const QString KEY_REMAINING_DOUBLE_TAP_HINTS("remainingDoubleTapHints");
const QString KEY_ONLINE_ONLY_MODE("onlineOnlyMode"); const QString KEY_ONLINE_ONLY_MODE("onlineOnlyMode");
const QString KEY_DELAY_MESSAGE_READ("delayMessageRead"); const QString KEY_DELAY_MESSAGE_READ("delayMessageRead");
const QString KEY_FOCUS_TEXTAREA_ON_CHAT_OPEN("focusTextAreaOnChatOpen"); const QString KEY_FOCUS_TEXTAREA_ON_CHAT_OPEN("focusTextAreaOnChatOpen");
const QString KEY_SPONSORED_MESS("sponsoredMess"); const QString KEY_SPONSORED_MESS("sponsoredMess");
const QString KEY_HIGHLIGHT_UNREADCONVS("highlightUnreadConversations");
} }
AppSettings::AppSettings(QObject *parent) : QObject(parent), settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/de.ygriega/fernschreiber/settings.conf", QSettings::NativeFormat) AppSettings::AppSettings(QObject *parent) : QObject(parent), settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/de.ygriega/fernschreiber/settings.conf", QSettings::NativeFormat)
@ -155,6 +160,20 @@ void AppSettings::setNotificationSoundsEnabled(bool enable)
} }
} }
bool AppSettings::notificationSuppressContent() const
{
return settings.value(KEY_NOTIFICATION_SUPPRESS_ENABLED, false).toBool();
}
void AppSettings::setNotificationSuppressContent(bool enable)
{
if (notificationSuppressContent() != enable) {
LOG(KEY_NOTIFICATION_SUPPRESS_ENABLED << enable);
settings.setValue(KEY_NOTIFICATION_SUPPRESS_ENABLED, enable);
emit notificationSuppressContentChanged();
}
}
AppSettings::NotificationFeedback AppSettings::notificationFeedback() const AppSettings::NotificationFeedback AppSettings::notificationFeedback() const
{ {
return (NotificationFeedback) settings.value(KEY_NOTIFICATION_FEEDBACK, (int) NotificationFeedbackAll).toInt(); return (NotificationFeedback) settings.value(KEY_NOTIFICATION_FEEDBACK, (int) NotificationFeedbackAll).toInt();
@ -169,6 +188,34 @@ void AppSettings::setNotificationFeedback(NotificationFeedback feedback)
} }
} }
bool AppSettings::notificationAlwaysShowPreview() const
{
return settings.value(KEY_NOTIFICATION_ALWAYS_SHOW_PREVIEW, false).toBool();
}
void AppSettings::setNotificationAlwaysShowPreview(bool enable)
{
if (notificationAlwaysShowPreview() != enable) {
LOG(KEY_NOTIFICATION_ALWAYS_SHOW_PREVIEW << enable);
settings.setValue(KEY_NOTIFICATION_ALWAYS_SHOW_PREVIEW, enable);
emit notificationAlwaysShowPreviewChanged();
}
}
bool AppSettings::goToQuotedMessage() const
{
return settings.value(KEY_GO_TO_QUOTED_MESSAGE, false).toBool();
}
void AppSettings::setGoToQuotedMessage(bool enable)
{
if (goToQuotedMessage() != enable) {
LOG(KEY_GO_TO_QUOTED_MESSAGE << enable);
settings.setValue(KEY_GO_TO_QUOTED_MESSAGE, enable);
emit goToQuotedMessageChanged();
}
}
bool AppSettings::storageOptimizer() const bool AppSettings::storageOptimizer() const
{ {
return settings.value(KEY_STORAGE_OPTIMIZER, true).toBool(); return settings.value(KEY_STORAGE_OPTIMIZER, true).toBool();
@ -212,6 +259,20 @@ void AppSettings::setRemainingInteractionHints(int remainingHints)
} }
} }
int AppSettings::remainingDoubleTapHints() const
{
return settings.value(KEY_REMAINING_DOUBLE_TAP_HINTS, 3).toInt();
}
void AppSettings::setRemainingDoubleTapHints(int remainingHints)
{
if (remainingDoubleTapHints() != remainingHints) {
LOG(KEY_REMAINING_DOUBLE_TAP_HINTS << remainingHints);
settings.setValue(KEY_REMAINING_DOUBLE_TAP_HINTS, remainingHints);
emit remainingDoubleTapHintsChanged();
}
}
bool AppSettings::onlineOnlyMode() const bool AppSettings::onlineOnlyMode() const
{ {
return settings.value(KEY_ONLINE_ONLY_MODE, false).toBool(); return settings.value(KEY_ONLINE_ONLY_MODE, false).toBool();
@ -240,6 +301,20 @@ void AppSettings::setDelayMessageRead(bool enable)
} }
} }
bool AppSettings::highlightUnreadConversations() const
{
return settings.value(KEY_HIGHLIGHT_UNREADCONVS, true).toBool();
}
void AppSettings::setHighlightUnreadConversations(bool enable)
{
if (highlightUnreadConversations() != enable) {
LOG(KEY_HIGHLIGHT_UNREADCONVS << enable);
settings.setValue(KEY_HIGHLIGHT_UNREADCONVS, enable);
emit highlightUnreadConversationsChanged();
}
}
bool AppSettings::getFocusTextAreaOnChatOpen() const bool AppSettings::getFocusTextAreaOnChatOpen() const
{ {
return settings.value(KEY_FOCUS_TEXTAREA_ON_CHAT_OPEN, false).toBool(); return settings.value(KEY_FOCUS_TEXTAREA_ON_CHAT_OPEN, false).toBool();

View file

@ -32,14 +32,19 @@ class AppSettings : public QObject {
Q_PROPERTY(bool animateStickers READ animateStickers WRITE setAnimateStickers NOTIFY animateStickersChanged) Q_PROPERTY(bool animateStickers READ animateStickers WRITE setAnimateStickers NOTIFY animateStickersChanged)
Q_PROPERTY(bool notificationTurnsDisplayOn READ notificationTurnsDisplayOn WRITE setNotificationTurnsDisplayOn NOTIFY notificationTurnsDisplayOnChanged) Q_PROPERTY(bool notificationTurnsDisplayOn READ notificationTurnsDisplayOn WRITE setNotificationTurnsDisplayOn NOTIFY notificationTurnsDisplayOnChanged)
Q_PROPERTY(bool notificationSoundsEnabled READ notificationSoundsEnabled WRITE setNotificationSoundsEnabled NOTIFY notificationSoundsEnabledChanged) Q_PROPERTY(bool notificationSoundsEnabled READ notificationSoundsEnabled WRITE setNotificationSoundsEnabled NOTIFY notificationSoundsEnabledChanged)
Q_PROPERTY(bool notificationSuppressContent READ notificationSuppressContent WRITE setNotificationSuppressContent NOTIFY notificationSuppressContentChanged)
Q_PROPERTY(NotificationFeedback notificationFeedback READ notificationFeedback WRITE setNotificationFeedback NOTIFY notificationFeedbackChanged) Q_PROPERTY(NotificationFeedback notificationFeedback READ notificationFeedback WRITE setNotificationFeedback NOTIFY notificationFeedbackChanged)
Q_PROPERTY(bool notificationAlwaysShowPreview READ notificationAlwaysShowPreview WRITE setNotificationAlwaysShowPreview NOTIFY notificationAlwaysShowPreviewChanged)
Q_PROPERTY(bool goToQuotedMessage READ goToQuotedMessage WRITE setGoToQuotedMessage NOTIFY goToQuotedMessageChanged)
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(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(int remainingDoubleTapHints READ remainingDoubleTapHints WRITE setRemainingDoubleTapHints NOTIFY remainingDoubleTapHintsChanged)
Q_PROPERTY(bool onlineOnlyMode READ onlineOnlyMode WRITE setOnlineOnlyMode NOTIFY onlineOnlyModeChanged) Q_PROPERTY(bool onlineOnlyMode READ onlineOnlyMode WRITE setOnlineOnlyMode NOTIFY onlineOnlyModeChanged)
Q_PROPERTY(bool delayMessageRead READ delayMessageRead WRITE setDelayMessageRead NOTIFY delayMessageReadChanged) Q_PROPERTY(bool delayMessageRead READ delayMessageRead WRITE setDelayMessageRead NOTIFY delayMessageReadChanged)
Q_PROPERTY(bool focusTextAreaOnChatOpen READ getFocusTextAreaOnChatOpen WRITE setFocusTextAreaOnChatOpen NOTIFY focusTextAreaOnChatOpenChanged) Q_PROPERTY(bool focusTextAreaOnChatOpen READ getFocusTextAreaOnChatOpen WRITE setFocusTextAreaOnChatOpen NOTIFY focusTextAreaOnChatOpenChanged)
Q_PROPERTY(SponsoredMess sponsoredMess READ getSponsoredMess WRITE setSponsoredMess NOTIFY sponsoredMessChanged) Q_PROPERTY(SponsoredMess sponsoredMess READ getSponsoredMess WRITE setSponsoredMess NOTIFY sponsoredMessChanged)
Q_PROPERTY(bool highlightUnreadConversations READ highlightUnreadConversations WRITE setHighlightUnreadConversations NOTIFY highlightUnreadConversationsChanged)
public: public:
enum SponsoredMess { enum SponsoredMess {
@ -83,9 +88,18 @@ public:
bool notificationSoundsEnabled() const; bool notificationSoundsEnabled() const;
void setNotificationSoundsEnabled(bool enable); void setNotificationSoundsEnabled(bool enable);
bool notificationSuppressContent() const;
void setNotificationSuppressContent(bool enable);
NotificationFeedback notificationFeedback() const; NotificationFeedback notificationFeedback() const;
void setNotificationFeedback(NotificationFeedback feedback); void setNotificationFeedback(NotificationFeedback feedback);
bool notificationAlwaysShowPreview() const;
void setNotificationAlwaysShowPreview(bool enable);
bool goToQuotedMessage() const;
void setGoToQuotedMessage(bool enable);
bool storageOptimizer() const; bool storageOptimizer() const;
void setStorageOptimizer(bool enable); void setStorageOptimizer(bool enable);
@ -95,6 +109,9 @@ public:
int remainingInteractionHints() const; int remainingInteractionHints() const;
void setRemainingInteractionHints(int remainingHints); void setRemainingInteractionHints(int remainingHints);
int remainingDoubleTapHints() const;
void setRemainingDoubleTapHints(int remainingHints);
bool onlineOnlyMode() const; bool onlineOnlyMode() const;
void setOnlineOnlyMode(bool enable); void setOnlineOnlyMode(bool enable);
@ -107,6 +124,9 @@ public:
SponsoredMess getSponsoredMess() const; SponsoredMess getSponsoredMess() const;
void setSponsoredMess(SponsoredMess sponsoredMess); void setSponsoredMess(SponsoredMess sponsoredMess);
bool highlightUnreadConversations() const;
void setHighlightUnreadConversations(bool enable);
signals: signals:
void sendByEnterChanged(); void sendByEnterChanged();
void focusTextAreaAfterSendChanged(); void focusTextAreaAfterSendChanged();
@ -116,14 +136,19 @@ signals:
void animateStickersChanged(); void animateStickersChanged();
void notificationTurnsDisplayOnChanged(); void notificationTurnsDisplayOnChanged();
void notificationSoundsEnabledChanged(); void notificationSoundsEnabledChanged();
void notificationSuppressContentChanged();
void notificationFeedbackChanged(); void notificationFeedbackChanged();
void notificationAlwaysShowPreviewChanged();
void goToQuotedMessageChanged();
void storageOptimizerChanged(); void storageOptimizerChanged();
void allowInlineBotLocationAccessChanged(); void allowInlineBotLocationAccessChanged();
void remainingInteractionHintsChanged(); void remainingInteractionHintsChanged();
void remainingDoubleTapHintsChanged();
void onlineOnlyModeChanged(); void onlineOnlyModeChanged();
void delayMessageReadChanged(); void delayMessageReadChanged();
void focusTextAreaOnChatOpenChanged(); void focusTextAreaOnChatOpenChanged();
void sponsoredMessChanged(); void sponsoredMessChanged();
void highlightUnreadConversationsChanged();
private: private:
QSettings settings; QSettings settings;

View file

@ -44,6 +44,7 @@ namespace {
const QString UNREAD_COUNT("unread_count"); const QString UNREAD_COUNT("unread_count");
const QString UNREAD_MENTION_COUNT("unread_mention_count"); const QString UNREAD_MENTION_COUNT("unread_mention_count");
const QString UNREAD_REACTION_COUNT("unread_reaction_count"); const QString UNREAD_REACTION_COUNT("unread_reaction_count");
const QString AVAILABLE_REACTIONS("available_reactions");
const QString NOTIFICATION_SETTINGS("notification_settings"); const QString NOTIFICATION_SETTINGS("notification_settings");
const QString LAST_READ_INBOX_MESSAGE_ID("last_read_inbox_message_id"); const QString LAST_READ_INBOX_MESSAGE_ID("last_read_inbox_message_id");
const QString LAST_READ_OUTBOX_MESSAGE_ID("last_read_outbox_message_id"); const QString LAST_READ_OUTBOX_MESSAGE_ID("last_read_outbox_message_id");
@ -70,6 +71,7 @@ public:
int unreadCount() const; int unreadCount() const;
int unreadMentionCount() const; int unreadMentionCount() const;
int unreadReactionCount() const; int unreadReactionCount() const;
QVariant availableReactions() const;
QVariant photoSmall() const; QVariant photoSmall() const;
qlonglong lastReadInboxMessageId() const; qlonglong lastReadInboxMessageId() const;
qlonglong senderUserId() const; qlonglong senderUserId() const;
@ -168,6 +170,11 @@ int ChatListModel::ChatData::unreadMentionCount() const
return chatData.value(UNREAD_MENTION_COUNT).toInt(); return chatData.value(UNREAD_MENTION_COUNT).toInt();
} }
QVariant ChatListModel::ChatData::availableReactions() const
{
return chatData.value(AVAILABLE_REACTIONS);
}
int ChatListModel::ChatData::unreadReactionCount() const int ChatListModel::ChatData::unreadReactionCount() const
{ {
return chatData.value(UNREAD_REACTION_COUNT).toInt(); return chatData.value(UNREAD_REACTION_COUNT).toInt();
@ -400,6 +407,7 @@ ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper, AppSettings *appSetting
connect(tdLibWrapper, SIGNAL(chatDraftMessageUpdated(qlonglong, QVariantMap, QString)), this, SLOT(handleChatDraftMessageUpdated(qlonglong, QVariantMap, QString))); connect(tdLibWrapper, SIGNAL(chatDraftMessageUpdated(qlonglong, QVariantMap, QString)), this, SLOT(handleChatDraftMessageUpdated(qlonglong, QVariantMap, QString)));
connect(tdLibWrapper, SIGNAL(chatUnreadMentionCountUpdated(qlonglong, int)), this, SLOT(handleChatUnreadMentionCountUpdated(qlonglong, int))); connect(tdLibWrapper, SIGNAL(chatUnreadMentionCountUpdated(qlonglong, int)), this, SLOT(handleChatUnreadMentionCountUpdated(qlonglong, int)));
connect(tdLibWrapper, SIGNAL(chatUnreadReactionCountUpdated(qlonglong, int)), this, SLOT(handleChatUnreadReactionCountUpdated(qlonglong, int))); connect(tdLibWrapper, SIGNAL(chatUnreadReactionCountUpdated(qlonglong, int)), this, SLOT(handleChatUnreadReactionCountUpdated(qlonglong, int)));
connect(tdLibWrapper, SIGNAL(chatAvailableReactionsUpdated(qlonglong,QVariantMap)), this, SLOT(handleChatAvailableReactionsUpdated(qlonglong,QVariantMap)));
// Don't start the timer until we have at least one chat // Don't start the timer until we have at least one chat
relativeTimeRefreshTimer = new QTimer(this); relativeTimeRefreshTimer = new QTimer(this);
@ -436,6 +444,7 @@ QHash<int,QByteArray> ChatListModel::roleNames() const
roles.insert(ChatListModel::RoleUnreadCount, "unread_count"); roles.insert(ChatListModel::RoleUnreadCount, "unread_count");
roles.insert(ChatListModel::RoleUnreadMentionCount, "unread_mention_count"); roles.insert(ChatListModel::RoleUnreadMentionCount, "unread_mention_count");
roles.insert(ChatListModel::RoleUnreadReactionCount, "unread_reaction_count"); roles.insert(ChatListModel::RoleUnreadReactionCount, "unread_reaction_count");
roles.insert(ChatListModel::RoleAvailableReactions, "available_reactions");
roles.insert(ChatListModel::RoleLastReadInboxMessageId, "last_read_inbox_message_id"); roles.insert(ChatListModel::RoleLastReadInboxMessageId, "last_read_inbox_message_id");
roles.insert(ChatListModel::RoleLastMessageSenderId, "last_message_sender_id"); roles.insert(ChatListModel::RoleLastMessageSenderId, "last_message_sender_id");
roles.insert(ChatListModel::RoleLastMessageDate, "last_message_date"); roles.insert(ChatListModel::RoleLastMessageDate, "last_message_date");
@ -472,6 +481,7 @@ QVariant ChatListModel::data(const QModelIndex &index, int role) const
case ChatListModel::RolePhotoSmall: return data->photoSmall(); case ChatListModel::RolePhotoSmall: return data->photoSmall();
case ChatListModel::RoleUnreadCount: return data->unreadCount(); case ChatListModel::RoleUnreadCount: return data->unreadCount();
case ChatListModel::RoleUnreadMentionCount: return data->unreadMentionCount(); case ChatListModel::RoleUnreadMentionCount: return data->unreadMentionCount();
case ChatListModel::RoleAvailableReactions: return data->availableReactions();
case ChatListModel::RoleUnreadReactionCount: return data->unreadReactionCount(); case ChatListModel::RoleUnreadReactionCount: return data->unreadReactionCount();
case ChatListModel::RoleLastReadInboxMessageId: return data->lastReadInboxMessageId(); case ChatListModel::RoleLastReadInboxMessageId: return data->lastReadInboxMessageId();
case ChatListModel::RoleLastMessageSenderId: return data->senderUserId(); case ChatListModel::RoleLastMessageSenderId: return data->senderUserId();
@ -561,6 +571,15 @@ int ChatListModel::updateChatOrder(int chatIndex)
return newIndex; return newIndex;
} }
void ChatListModel::enableRefreshTimer()
{
// Start timestamp refresh timer if not yet active (usually when the first visible chat is discovered)
if (!relativeTimeRefreshTimer->isActive()) {
LOG("Enabling refresh timer");
relativeTimeRefreshTimer->start();
}
}
void ChatListModel::calculateUnreadState() void ChatListModel::calculateUnreadState()
{ {
if (this->appSettings->onlineOnlyMode()) { if (this->appSettings->onlineOnlyMode()) {
@ -599,6 +618,7 @@ void ChatListModel::addVisibleChat(ChatData *chat)
this->tdLibWrapper->registerJoinChat(); this->tdLibWrapper->registerJoinChat();
emit chatJoined(chat->chatId, chat->chatData.value("title").toString()); emit chatJoined(chat->chatId, chat->chatData.value("title").toString());
} }
enableRefreshTimer();
} }
void ChatListModel::updateChatVisibility(const TDLibWrapper::Group *group) void ChatListModel::updateChatVisibility(const TDLibWrapper::Group *group)
@ -687,6 +707,7 @@ void ChatListModel::setShowAllChats(bool showAll)
void ChatListModel::handleChatDiscovered(const QString &, const QVariantMap &chatToBeAdded) void ChatListModel::handleChatDiscovered(const QString &, const QVariantMap &chatToBeAdded)
{ {
LOG("New chat discovered");
ChatData *chat = new ChatData(tdLibWrapper, chatToBeAdded); ChatData *chat = new ChatData(tdLibWrapper, chatToBeAdded);
const TDLibWrapper::Group *group = tdLibWrapper->getGroup(chat->groupId); const TDLibWrapper::Group *group = tdLibWrapper->getGroup(chat->groupId);
@ -705,12 +726,8 @@ void ChatListModel::handleChatDiscovered(const QString &, const QVariantMap &cha
LOG("Hidden chat" << chat->chatId); LOG("Hidden chat" << chat->chatId);
hiddenChats.insert(chat->chatId, chat); hiddenChats.insert(chat->chatId, chat);
} else { } else {
LOG("Visible chat" << chat->chatId);
addVisibleChat(chat); addVisibleChat(chat);
// Start timestamp refresh timer when the first visible chat is discovered
if (!relativeTimeRefreshTimer->isActive()) {
relativeTimeRefreshTimer->start();
}
} }
} }
@ -1029,10 +1046,31 @@ void ChatListModel::handleChatUnreadReactionCountUpdated(qlonglong chatId, int u
} }
} }
void ChatListModel::handleChatAvailableReactionsUpdated(qlonglong chatId, const QVariantMap availableReactions)
{
if (chatIndexMap.contains(chatId)) {
LOG("Updating available reaction type for" << chatId << availableReactions);
const int chatIndex = chatIndexMap.value(chatId);
ChatData *chat = chatList.at(chatIndex);
chat->chatData.insert(AVAILABLE_REACTIONS, availableReactions);
QVector<int> changedRoles;
changedRoles.append(ChatListModel::RoleAvailableReactions);
const QModelIndex modelIndex(index(chatIndex));
emit dataChanged(modelIndex, modelIndex, changedRoles);
} else {
ChatData *chat = hiddenChats.value(chatId);
if (chat) {
LOG("Updating available reaction type for hidden chat" << chatId << availableReactions);
chat->chatData.insert(AVAILABLE_REACTIONS, availableReactions);
}
}
}
void ChatListModel::handleRelativeTimeRefreshTimer() void ChatListModel::handleRelativeTimeRefreshTimer()
{ {
LOG("Refreshing timestamps"); LOG("Refreshing timestamps");
QVector<int> roles; QVector<int> roles;
roles.append(ChatListModel::RoleLastMessageDate); roles.append(ChatListModel::RoleLastMessageDate);
roles.append(ChatListModel::RoleLastMessageStatus);
emit dataChanged(index(0), index(chatList.size() - 1), roles); emit dataChanged(index(0), index(chatList.size() - 1), roles);
} }

View file

@ -42,6 +42,7 @@ public:
RoleUnreadCount, RoleUnreadCount,
RoleUnreadMentionCount, RoleUnreadMentionCount,
RoleUnreadReactionCount, RoleUnreadReactionCount,
RoleAvailableReactions,
RoleLastReadInboxMessageId, RoleLastReadInboxMessageId,
RoleLastMessageSenderId, RoleLastMessageSenderId,
RoleLastMessageDate, RoleLastMessageDate,
@ -93,6 +94,7 @@ private slots:
void handleChatDraftMessageUpdated(qlonglong chatId, const QVariantMap &draftMessage, const QString &order); void handleChatDraftMessageUpdated(qlonglong chatId, const QVariantMap &draftMessage, const QString &order);
void handleChatUnreadMentionCountUpdated(qlonglong chatId, int unreadMentionCount); void handleChatUnreadMentionCountUpdated(qlonglong chatId, int unreadMentionCount);
void handleChatUnreadReactionCountUpdated(qlonglong chatId, int unreadReactionCount); void handleChatUnreadReactionCountUpdated(qlonglong chatId, int unreadReactionCount);
void handleChatAvailableReactionsUpdated(qlonglong chatId, const QVariantMap availableReactions);
void handleRelativeTimeRefreshTimer(); void handleRelativeTimeRefreshTimer();
signals: signals:
@ -108,6 +110,7 @@ private:
void updateChatVisibility(const TDLibWrapper::Group *group); void updateChatVisibility(const TDLibWrapper::Group *group);
void updateSecretChatVisibility(const QVariantMap secretChatDetails); void updateSecretChatVisibility(const QVariantMap secretChatDetails);
int updateChatOrder(int chatIndex); int updateChatOrder(int chatIndex);
void enableRefreshTimer();
private: private:
TDLibWrapper *tdLibWrapper; TDLibWrapper *tdLibWrapper;

View file

@ -363,6 +363,15 @@ void ChatModel::initialize(const QVariantMap &chatInformation)
tdLibWrapper->getChatHistory(chatId, this->chatInformation.value(LAST_READ_INBOX_MESSAGE_ID).toLongLong()); tdLibWrapper->getChatHistory(chatId, this->chatInformation.value(LAST_READ_INBOX_MESSAGE_ID).toLongLong());
} }
void ChatModel::triggerLoadHistoryForMessage(qlonglong messageId)
{
if (!this->inIncrementalUpdate && !messages.isEmpty()) {
LOG("Trigger loading message with id..." << messageId);
this->inIncrementalUpdate = true;
this->tdLibWrapper->getChatHistory(chatId, messageId);
}
}
void ChatModel::triggerLoadMoreHistory() void ChatModel::triggerLoadMoreHistory()
{ {
if (!this->inIncrementalUpdate && !messages.isEmpty()) { if (!this->inIncrementalUpdate && !messages.isEmpty()) {
@ -400,6 +409,17 @@ QVariantMap ChatModel::getMessage(int index)
return QVariantMap(); return QVariantMap();
} }
int ChatModel::getMessageIndex(qlonglong messageId)
{
if (messages.size() == 0) {
return -1;
}
if (messageIndexMap.contains(messageId)) {
return messageIndexMap.value(messageId);
}
return -1;
}
int ChatModel::getLastReadMessageIndex() int ChatModel::getLastReadMessageIndex()
{ {
LOG("Obtaining last read message index"); LOG("Obtaining last read message index");

View file

@ -40,12 +40,14 @@ public:
Q_INVOKABLE void clear(bool contentOnly = false); Q_INVOKABLE void clear(bool contentOnly = false);
Q_INVOKABLE void initialize(const QVariantMap &chatInformation); Q_INVOKABLE void initialize(const QVariantMap &chatInformation);
Q_INVOKABLE void triggerLoadMoreHistory(); Q_INVOKABLE void triggerLoadMoreHistory();
Q_INVOKABLE void triggerLoadHistoryForMessage(qlonglong messageId);
Q_INVOKABLE void triggerLoadMoreFuture(); Q_INVOKABLE void triggerLoadMoreFuture();
Q_INVOKABLE QVariantMap getChatInformation(); Q_INVOKABLE QVariantMap getChatInformation();
Q_INVOKABLE QVariantMap getMessage(int index); Q_INVOKABLE QVariantMap getMessage(int index);
Q_INVOKABLE int getLastReadMessageIndex(); Q_INVOKABLE int getLastReadMessageIndex();
Q_INVOKABLE void setSearchQuery(const QString newSearchQuery); Q_INVOKABLE void setSearchQuery(const QString newSearchQuery);
Q_INVOKABLE int getMessageIndex(qlonglong messageId);
QVariantMap smallPhoto() const; QVariantMap smallPhoto() const;
qlonglong getChatId() const; qlonglong getChatId() const;

View file

@ -68,7 +68,7 @@ QVariant ContactsModel::data(const QModelIndex &index, int role) const
case ContactRole::RoleDisplay: return requestedContact; case ContactRole::RoleDisplay: return requestedContact;
case ContactRole::RoleTitle: return QString(requestedContact.value("first_name").toString() + " " + requestedContact.value("last_name").toString()).trimmed(); case ContactRole::RoleTitle: return QString(requestedContact.value("first_name").toString() + " " + requestedContact.value("last_name").toString()).trimmed();
case ContactRole::RoleUserId: return requestedContact.value("id"); case ContactRole::RoleUserId: return requestedContact.value("id");
case ContactRole::RoleUsername: return requestedContact.value("username"); case ContactRole::RoleUsername: return requestedContact.value("usernames").toMap().value("editable_username").toString();
case ContactRole::RolePhotoSmall: return requestedContact.value("profile_photo").toMap().value("small"); case ContactRole::RolePhotoSmall: return requestedContact.value("profile_photo").toMap().value("small");
case ContactRole::RoleUserStatus: return requestedContact.value("status").toMap().value("@type"); case ContactRole::RoleUserStatus: return requestedContact.value("status").toMap().value("@type");
case ContactRole::RoleUserLastOnline: return requestedContact.value("status").toMap().value("was_online"); case ContactRole::RoleUserLastOnline: return requestedContact.value("status").toMap().value("was_online");

View file

@ -56,10 +56,10 @@ QVariant KnownUsersModel::data(const QModelIndex &index, int role) const
case KnownUserRole::RoleDisplay: return requestedUser; case KnownUserRole::RoleDisplay: return requestedUser;
case KnownUserRole::RoleUserId: return requestedUser.value("id"); case KnownUserRole::RoleUserId: return requestedUser.value("id");
case KnownUserRole::RoleTitle: return QString(requestedUser.value("first_name").toString() + " " + requestedUser.value("last_name").toString()).trimmed(); case KnownUserRole::RoleTitle: return QString(requestedUser.value("first_name").toString() + " " + requestedUser.value("last_name").toString()).trimmed();
case KnownUserRole::RoleUsername: return requestedUser.value("username"); case KnownUserRole::RoleUsername: return requestedUser.value("usernames").toMap().value("editable_username").toString();
case KnownUserRole::RoleUserHandle: return QString("@" + (requestedUser.value("username").toString().isEmpty() ? requestedUser.value("id").toString() : requestedUser.value("username").toString())); case KnownUserRole::RoleUserHandle: return QString("@" + (requestedUser.value("usernames").toMap().value("editable_username").toString().isEmpty() ? requestedUser.value("id").toString() : requestedUser.value("usernames").toMap().value("editable_username").toString()));
case KnownUserRole::RolePhotoSmall: return requestedUser.value("profile_photo").toMap().value("small"); case KnownUserRole::RolePhotoSmall: return requestedUser.value("profile_photo").toMap().value("small");
case KnownUserRole::RoleFilter: return QString(requestedUser.value("first_name").toString() + " " + requestedUser.value("last_name").toString() + " " + requestedUser.value("username").toString()).trimmed(); case KnownUserRole::RoleFilter: return QString(requestedUser.value("first_name").toString() + " " + requestedUser.value("last_name").toString() + " " + requestedUser.value("usernames").toMap().value("editable_username").toString()).trimmed();
} }
} }
return QVariant(); return QVariant();

View file

@ -341,8 +341,18 @@ void NotificationManager::publishNotification(const NotificationGroup *notificat
QString notificationBody; QString notificationBody;
const QVariantMap senderInformation = messageMap.value(SENDER_ID).toMap(); const QVariantMap senderInformation = messageMap.value(SENDER_ID).toMap();
if (notificationGroup->totalCount == 1 && !messageMap.isEmpty()) { bool outputMessageCount = notificationGroup->totalCount > 1;
bool messageIsEmpty = messageMap.isEmpty();
if (outputMessageCount || messageIsEmpty) {
// Either we have more than one notification or we have no content to display
LOG("Group" << notificationGroup->notificationGroupId << "has" << notificationGroup->totalCount << "notifications");
notificationBody = tr("%Ln unread messages", "", notificationGroup->totalCount);
}
if ((!outputMessageCount || appSettings->notificationAlwaysShowPreview()) && !messageIsEmpty) {
LOG("Group" << notificationGroup->notificationGroupId << "has 1 notification"); LOG("Group" << notificationGroup->notificationGroupId << "has 1 notification");
if (outputMessageCount) {
notificationBody += "; ";
}
if (chatInformation && (chatInformation->type == TDLibWrapper::ChatTypeBasicGroup || if (chatInformation && (chatInformation->type == TDLibWrapper::ChatTypeBasicGroup ||
(chatInformation->type == TDLibWrapper::ChatTypeSupergroup && !chatInformation->isChannel))) { (chatInformation->type == TDLibWrapper::ChatTypeSupergroup && !chatInformation->isChannel))) {
// Add author // Add author
@ -352,15 +362,9 @@ void NotificationManager::publishNotification(const NotificationGroup *notificat
} else { } else {
fullName = FernschreiberUtils::getUserName(tdLibWrapper->getUserInformation(senderInformation.value(USER_ID).toString())); fullName = FernschreiberUtils::getUserName(tdLibWrapper->getUserInformation(senderInformation.value(USER_ID).toString()));
} }
notificationBody += fullName.trimmed() + ": ";
notificationBody = notificationBody + fullName.trimmed() + ": ";
} }
notificationBody += FernschreiberUtils::getMessageShortText(tdLibWrapper, messageMap.value(CONTENT).toMap(), (chatInformation ? chatInformation->isChannel : false), tdLibWrapper->getUserInformation().value(ID).toLongLong(), senderInformation ); notificationBody += FernschreiberUtils::getMessageShortText(tdLibWrapper, messageMap.value(CONTENT).toMap(), (chatInformation ? chatInformation->isChannel : false), tdLibWrapper->getUserInformation().value(ID).toLongLong(), senderInformation );
nemoNotification->setBody(notificationBody);
} else {
// Either we have more than one notification or we have no content to display
LOG("Group" << notificationGroup->notificationGroupId << "has" << notificationGroup->totalCount << "notifications");
notificationBody = tr("%Ln unread messages", "", notificationGroup->totalCount);
} }
const QString summary(chatInformation ? chatInformation->title : QString()); const QString summary(chatInformation ? chatInformation->title : QString());
@ -377,7 +381,11 @@ void NotificationManager::publishNotification(const NotificationGroup *notificat
nemoNotification->setHintValue(HINT_VISIBILITY, QString()); nemoNotification->setHintValue(HINT_VISIBILITY, QString());
nemoNotification->setUrgency(Notification::Low); nemoNotification->setUrgency(Notification::Low);
} else { } else {
nemoNotification->setPreviewBody(notificationBody); if (!appSettings->notificationSuppressContent()) {
nemoNotification->setPreviewBody(notificationBody);
} else {
nemoNotification->setPreviewBody(tr("%Ln unread messages", "", notificationGroup->totalCount));
}
nemoNotification->setPreviewSummary(summary); nemoNotification->setPreviewSummary(summary);
nemoNotification->setHintValue(HINT_SUPPRESS_SOUND, !appSettings->notificationSoundsEnabled()); nemoNotification->setHintValue(HINT_SUPPRESS_SOUND, !appSettings->notificationSoundsEnabled());
nemoNotification->setHintValue(HINT_DISPLAY_ON, appSettings->notificationTurnsDisplayOn()); nemoNotification->setHintValue(HINT_DISPLAY_ON, appSettings->notificationTurnsDisplayOn());

View file

@ -46,6 +46,7 @@ namespace {
const QString UNREAD_COUNT("unread_count"); const QString UNREAD_COUNT("unread_count");
const QString UNREAD_MENTION_COUNT("unread_mention_count"); const QString UNREAD_MENTION_COUNT("unread_mention_count");
const QString UNREAD_REACTION_COUNT("unread_reaction_count"); const QString UNREAD_REACTION_COUNT("unread_reaction_count");
const QString AVAILABLE_REACTIONS("available_reactions");
const QString TEXT("text"); const QString TEXT("text");
const QString LAST_READ_INBOX_MESSAGE_ID("last_read_inbox_message_id"); const QString LAST_READ_INBOX_MESSAGE_ID("last_read_inbox_message_id");
const QString LAST_READ_OUTBOX_MESSAGE_ID("last_read_outbox_message_id"); const QString LAST_READ_OUTBOX_MESSAGE_ID("last_read_outbox_message_id");
@ -60,6 +61,11 @@ namespace {
const QString CONTENT("content"); const QString CONTENT("content");
const QString NEW_CONTENT("new_content"); const QString NEW_CONTENT("new_content");
const QString SETS("sets"); const QString SETS("sets");
const QString EMOJIS("emojis");
const QString REPLY_TO("reply_to");
const QString REPLY_IN_CHAT_ID("reply_in_chat_id");
const QString REPLY_TO_MESSAGE_ID("reply_to_message_id");
const QString DRAFT_MESSAGE("draft_message");
const QString _TYPE("@type"); const QString _TYPE("@type");
const QString _EXTRA("@extra"); const QString _EXTRA("@extra");
@ -70,8 +76,11 @@ namespace {
const QString TYPE_MESSAGE("message"); const QString TYPE_MESSAGE("message");
const QString TYPE_STICKER("sticker"); const QString TYPE_STICKER("sticker");
const QString TYPE_MESSAGE_STICKER("messageSticker"); const QString TYPE_MESSAGE_STICKER("messageSticker");
const QString TYPE_MESSAGE_REPLY_TO_MESSAGE("messageReplyToMessage");
const QString TYPE_MESSAGE_ANIMATED_EMOJI("messageAnimatedEmoji"); const QString TYPE_MESSAGE_ANIMATED_EMOJI("messageAnimatedEmoji");
const QString TYPE_ANIMATED_EMOJI("animatedEmoji"); const QString TYPE_ANIMATED_EMOJI("animatedEmoji");
const QString TYPE_INPUT_MESSAGE_REPLY_TO_MESSAGE("inputMessageReplyToMessage");
const QString TYPE_DRAFT_MESSAGE("draftMessage");
} }
static QString getChatPositionOrder(const QVariantMap &position) static QString getChatPositionOrder(const QVariantMap &position)
@ -115,11 +124,14 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren
handlers.insert("updateChatPosition", &TDLibReceiver::processUpdateChatPosition); handlers.insert("updateChatPosition", &TDLibReceiver::processUpdateChatPosition);
handlers.insert("updateChatReadInbox", &TDLibReceiver::processUpdateChatReadInbox); handlers.insert("updateChatReadInbox", &TDLibReceiver::processUpdateChatReadInbox);
handlers.insert("updateChatReadOutbox", &TDLibReceiver::processUpdateChatReadOutbox); handlers.insert("updateChatReadOutbox", &TDLibReceiver::processUpdateChatReadOutbox);
handlers.insert("updateChatAvailableReactions", &TDLibReceiver::processUpdateChatAvailableReactions);
handlers.insert("updateBasicGroup", &TDLibReceiver::processUpdateBasicGroup); handlers.insert("updateBasicGroup", &TDLibReceiver::processUpdateBasicGroup);
handlers.insert("updateSupergroup", &TDLibReceiver::processUpdateSuperGroup); handlers.insert("updateSupergroup", &TDLibReceiver::processUpdateSuperGroup);
handlers.insert("updateChatOnlineMemberCount", &TDLibReceiver::processChatOnlineMemberCountUpdated); handlers.insert("updateChatOnlineMemberCount", &TDLibReceiver::processChatOnlineMemberCountUpdated);
handlers.insert("messages", &TDLibReceiver::processMessages); handlers.insert("messages", &TDLibReceiver::processMessages);
handlers.insert("sponsoredMessage", &TDLibReceiver::processSponsoredMessage); handlers.insert("foundChatMessages", &TDLibReceiver::processFoundChatMessages);
handlers.insert("sponsoredMessage", &TDLibReceiver::processSponsoredMessage); // TdLib <= 1.8.7
handlers.insert("sponsoredMessages", &TDLibReceiver::processSponsoredMessages); // TdLib >= 1.8.8
handlers.insert("updateNewMessage", &TDLibReceiver::processUpdateNewMessage); handlers.insert("updateNewMessage", &TDLibReceiver::processUpdateNewMessage);
handlers.insert("message", &TDLibReceiver::processMessage); handlers.insert("message", &TDLibReceiver::processMessage);
handlers.insert("messageLinkInfo", &TDLibReceiver::processMessageLinkInfo); handlers.insert("messageLinkInfo", &TDLibReceiver::processMessageLinkInfo);
@ -150,6 +162,7 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren
handlers.insert("updateChatPinnedMessage", &TDLibReceiver::processUpdateChatPinnedMessage); handlers.insert("updateChatPinnedMessage", &TDLibReceiver::processUpdateChatPinnedMessage);
handlers.insert("updateMessageIsPinned", &TDLibReceiver::processUpdateMessageIsPinned); handlers.insert("updateMessageIsPinned", &TDLibReceiver::processUpdateMessageIsPinned);
handlers.insert("users", &TDLibReceiver::processUsers); handlers.insert("users", &TDLibReceiver::processUsers);
handlers.insert("messageSenders", &TDLibReceiver::processMessageSenders);
handlers.insert("error", &TDLibReceiver::processError); handlers.insert("error", &TDLibReceiver::processError);
handlers.insert("ok", &TDLibReceiver::ok); handlers.insert("ok", &TDLibReceiver::ok);
handlers.insert("secretChat", &TDLibReceiver::processSecretChat); handlers.insert("secretChat", &TDLibReceiver::processSecretChat);
@ -165,8 +178,10 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren
handlers.insert("updateMessageInteractionInfo", &TDLibReceiver::processUpdateMessageInteractionInfo); handlers.insert("updateMessageInteractionInfo", &TDLibReceiver::processUpdateMessageInteractionInfo);
handlers.insert("sessions", &TDLibReceiver::processSessions); handlers.insert("sessions", &TDLibReceiver::processSessions);
handlers.insert("availableReactions", &TDLibReceiver::processAvailableReactions); handlers.insert("availableReactions", &TDLibReceiver::processAvailableReactions);
handlers.insert("updateMessageMentionRead", &TDLibReceiver::processUpdateChatUnreadMentionCount);
handlers.insert("updateChatUnreadMentionCount", &TDLibReceiver::processUpdateChatUnreadMentionCount); handlers.insert("updateChatUnreadMentionCount", &TDLibReceiver::processUpdateChatUnreadMentionCount);
handlers.insert("updateChatUnreadReactionCount", &TDLibReceiver::processUpdateChatUnreadReactionCount); handlers.insert("updateChatUnreadReactionCount", &TDLibReceiver::processUpdateChatUnreadReactionCount);
handlers.insert("updateActiveEmojiReactions", &TDLibReceiver::processUpdateActiveEmojiReactions);
} }
void TDLibReceiver::setActive(bool active) void TDLibReceiver::setActive(bool active)
@ -350,6 +365,14 @@ void TDLibReceiver::processUpdateChatReadOutbox(const QVariantMap &receivedInfor
emit chatReadOutboxUpdated(chat_id, last_read_outbox_message_id); emit chatReadOutboxUpdated(chat_id, last_read_outbox_message_id);
} }
void TDLibReceiver::processUpdateChatAvailableReactions(const QVariantMap &receivedInformation)
{
const qlonglong chat_id(receivedInformation.value(CHAT_ID).toLongLong());
const QVariantMap available_reactions(receivedInformation.value(AVAILABLE_REACTIONS).toMap());
LOG("Available reactions updated for" << chat_id << "new information:" << available_reactions);
emit chatAvailableReactionsUpdated(chat_id, available_reactions);
}
void TDLibReceiver::processUpdateBasicGroup(const QVariantMap &receivedInformation) void TDLibReceiver::processUpdateBasicGroup(const QVariantMap &receivedInformation)
{ {
const QVariantMap basicGroup(receivedInformation.value(BASIC_GROUP).toMap()); const QVariantMap basicGroup(receivedInformation.value(BASIC_GROUP).toMap());
@ -380,13 +403,33 @@ void TDLibReceiver::processMessages(const QVariantMap &receivedInformation)
emit messagesReceived(cleanupList(receivedInformation.value(MESSAGES).toList()), total_count); emit messagesReceived(cleanupList(receivedInformation.value(MESSAGES).toList()), total_count);
} }
void TDLibReceiver::processFoundChatMessages(const QVariantMap &receivedInformation)
{
const int total_count = receivedInformation.value(TOTAL_COUNT).toInt();
LOG("Received found chat messages, amount: " << total_count);
emit messagesReceived(cleanupList(receivedInformation.value(MESSAGES).toList()), total_count);
}
void TDLibReceiver::processSponsoredMessage(const QVariantMap &receivedInformation) void TDLibReceiver::processSponsoredMessage(const QVariantMap &receivedInformation)
{ {
// TdLib <= 1.8.7
const qlonglong chatId = receivedInformation.value(_EXTRA).toLongLong(); // See TDLibWrapper::getChatSponsoredMessage const qlonglong chatId = receivedInformation.value(_EXTRA).toLongLong(); // See TDLibWrapper::getChatSponsoredMessage
LOG("Received sponsored message for chat" << chatId); LOG("Received sponsored message for chat" << chatId);
emit sponsoredMessageReceived(chatId, receivedInformation); emit sponsoredMessageReceived(chatId, receivedInformation);
} }
void TDLibReceiver::processSponsoredMessages(const QVariantMap &receivedInformation)
{
// TdLib >= 1.8.8
const qlonglong chatId = receivedInformation.value(_EXTRA).toLongLong(); // See TDLibWrapper::getChatSponsoredMessage
const QVariantList messages(receivedInformation.value(MESSAGES).toList());
LOG("Received" << messages.count() << "sponsored messages for chat" << chatId);
QListIterator<QVariant> it(messages);
while (it.hasNext()) {
emit sponsoredMessageReceived(chatId, it.next().toMap());
}
}
void TDLibReceiver::processUpdateNewMessage(const QVariantMap &receivedInformation) void TDLibReceiver::processUpdateNewMessage(const QVariantMap &receivedInformation)
{ {
const QVariantMap message = receivedInformation.value(MESSAGE).toMap(); const QVariantMap message = receivedInformation.value(MESSAGE).toMap();
@ -400,7 +443,7 @@ void TDLibReceiver::processMessage(const QVariantMap &receivedInformation)
const qlonglong chatId = receivedInformation.value(CHAT_ID).toLongLong(); const qlonglong chatId = receivedInformation.value(CHAT_ID).toLongLong();
const qlonglong messageId = receivedInformation.value(ID).toLongLong(); const qlonglong messageId = receivedInformation.value(ID).toLongLong();
LOG("Received message " << chatId << messageId); LOG("Received message " << chatId << messageId);
emit messageInformation(chatId, messageId, receivedInformation); emit messageInformation(chatId, messageId, cleanupMap(receivedInformation));
} }
void TDLibReceiver::processMessageLinkInfo(const QVariantMap &receivedInformation) void TDLibReceiver::processMessageLinkInfo(const QVariantMap &receivedInformation)
@ -425,7 +468,7 @@ void TDLibReceiver::processMessageSendSucceeded(const QVariantMap &receivedInfor
const QVariantMap message = receivedInformation.value(MESSAGE).toMap(); const QVariantMap message = receivedInformation.value(MESSAGE).toMap();
const qlonglong messageId = message.value(ID).toLongLong(); const qlonglong messageId = message.value(ID).toLongLong();
LOG("Message send succeeded" << messageId << oldMessageId); LOG("Message send succeeded" << messageId << oldMessageId);
emit messageSendSucceeded(messageId, oldMessageId, message); emit messageSendSucceeded(messageId, oldMessageId, cleanupMap(message));
} }
void TDLibReceiver::processUpdateActiveNotifications(const QVariantMap &receivedInformation) void TDLibReceiver::processUpdateActiveNotifications(const QVariantMap &receivedInformation)
@ -602,6 +645,12 @@ void TDLibReceiver::processUsers(const QVariantMap &receivedInformation)
emit usersReceived(receivedInformation.value(_EXTRA).toString(), receivedInformation.value("user_ids").toList(), receivedInformation.value(TOTAL_COUNT).toInt()); emit usersReceived(receivedInformation.value(_EXTRA).toString(), receivedInformation.value("user_ids").toList(), receivedInformation.value(TOTAL_COUNT).toInt());
} }
void TDLibReceiver::processMessageSenders(const QVariantMap &receivedInformation)
{
LOG("Received Message Senders");
emit messageSendersReceived(receivedInformation.value(_EXTRA).toString(), receivedInformation.value("senders").toList(), receivedInformation.value(TOTAL_COUNT).toInt());
}
void TDLibReceiver::processError(const QVariantMap &receivedInformation) void TDLibReceiver::processError(const QVariantMap &receivedInformation)
{ {
LOG("Received an error"); LOG("Received an error");
@ -652,7 +701,7 @@ void TDLibReceiver::processUpdateChatIsMarkedAsUnread(const QVariantMap &receive
void TDLibReceiver::processUpdateChatDraftMessage(const QVariantMap &receivedInformation) void TDLibReceiver::processUpdateChatDraftMessage(const QVariantMap &receivedInformation)
{ {
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(), cleanupMap(receivedInformation.value(DRAFT_MESSAGE).toMap()), findChatPositionOrder(receivedInformation.value(POSITIONS).toList()));
} }
void TDLibReceiver::processInlineQueryResults(const QVariantMap &receivedInformation) void TDLibReceiver::processInlineQueryResults(const QVariantMap &receivedInformation)
@ -689,8 +738,9 @@ void TDLibReceiver::processUpdateMessageInteractionInfo(const QVariantMap &recei
void TDLibReceiver::processSessions(const QVariantMap &receivedInformation) void TDLibReceiver::processSessions(const QVariantMap &receivedInformation)
{ {
int inactive_session_ttl_days = receivedInformation.value("inactive_session_ttl_days").toInt();
QVariantList sessions = receivedInformation.value("sessions").toList(); QVariantList sessions = receivedInformation.value("sessions").toList();
emit sessionsReceived(sessions); emit sessionsReceived(inactive_session_ttl_days, sessions);
} }
void TDLibReceiver::processAvailableReactions(const QVariantMap &receivedInformation) void TDLibReceiver::processAvailableReactions(const QVariantMap &receivedInformation)
@ -704,6 +754,8 @@ void TDLibReceiver::processAvailableReactions(const QVariantMap &receivedInforma
void TDLibReceiver::processUpdateChatUnreadMentionCount(const QVariantMap &receivedInformation) void TDLibReceiver::processUpdateChatUnreadMentionCount(const QVariantMap &receivedInformation)
{ {
// Handles both updateMessageMentionRead and updateChatUnreadMentionCount
// They both have chat_id and unread_mention_count which is all we need
const qlonglong chatId = receivedInformation.value(CHAT_ID).toLongLong(); const qlonglong chatId = receivedInformation.value(CHAT_ID).toLongLong();
const int unreadMentionCount = receivedInformation.value(UNREAD_MENTION_COUNT).toInt(); const int unreadMentionCount = receivedInformation.value(UNREAD_MENTION_COUNT).toInt();
LOG("Chat unread mention count updated" << chatId << unreadMentionCount); LOG("Chat unread mention count updated" << chatId << unreadMentionCount);
@ -718,6 +770,13 @@ void TDLibReceiver::processUpdateChatUnreadReactionCount(const QVariantMap &rece
emit chatUnreadReactionCountUpdated(chatId, unreadReactionCount); emit chatUnreadReactionCountUpdated(chatId, unreadReactionCount);
} }
void TDLibReceiver::processUpdateActiveEmojiReactions(const QVariantMap &receivedInformation)
{
// updateActiveEmojiReactions was introduced between 1.8.5 and 1.8.6
// See https://github.com/tdlib/td/commit/d29d367
emit activeEmojiReactionsUpdated(receivedInformation.value(EMOJIS).toStringList());
}
// Recursively removes (some) unused entries from QVariantMaps to reduce // Recursively removes (some) unused entries from QVariantMaps to reduce
// memory usage. QStrings allocated by QVariantMaps are the top consumers // memory usage. QStrings allocated by QVariantMaps are the top consumers
// of memory. The biggest saving is achieved by removing "outline" from // of memory. The biggest saving is achieved by removing "outline" from
@ -747,17 +806,65 @@ const QVariantMap TDLibReceiver::cleanupMap(const QVariantMap& map, bool *update
return animated_emoji; return animated_emoji;
} }
} else if (type == TYPE_MESSAGE) { } else if (type == TYPE_MESSAGE) {
bool cleaned = false; QVariantMap message(map);
const QVariantMap content(cleanupMap(map.value(CONTENT).toMap(), &cleaned)); bool messageChanged = false;
if (cleaned) { const QVariantMap content(cleanupMap(map.value(CONTENT).toMap(), &messageChanged));
QVariantMap message(map); if (messageChanged) {
message.remove(CONTENT); message.remove(CONTENT);
message.insert(CONTENT, content); message.insert(CONTENT, content);
}
if (map.contains(REPLY_TO)) {
// In TdLib 1.8.15 reply_to_message_id and reply_in_chat_id attributes
// had been replaced with reply_to structure, e.g:
//
// "reply_to": {
// "@type": "messageReplyToMessage",
// "chat_id": -1001234567890,
// "is_quote_manual": false,
// "message_id": 234567890,
// "origin_send_date": 0
// }
//
QVariantMap reply_to(message.value(REPLY_TO).toMap());
if (reply_to.value(_TYPE).toString() == TYPE_MESSAGE_REPLY_TO_MESSAGE) {
if (reply_to.contains(MESSAGE_ID) &&
!message.contains(REPLY_TO_MESSAGE_ID)) {
message.insert(REPLY_TO_MESSAGE_ID, reply_to.value(MESSAGE_ID));
}
if (reply_to.contains(CHAT_ID) &&
!message.contains(REPLY_IN_CHAT_ID)) {
message.insert(REPLY_IN_CHAT_ID, reply_to.value(CHAT_ID));
}
reply_to.remove(_TYPE);
reply_to.insert(_TYPE, TYPE_MESSAGE_REPLY_TO_MESSAGE);
message.insert(REPLY_TO, reply_to);
messageChanged = true;
}
}
if (messageChanged) {
message.remove(_TYPE); message.remove(_TYPE);
message.insert(_TYPE, TYPE_MESSAGE); // Replace with a shared value message.insert(_TYPE, TYPE_MESSAGE); // Replace with a shared value
if (updated) *updated = true; if (updated) *updated = true;
return message; return message;
} }
} else if (type == TYPE_DRAFT_MESSAGE) {
QVariantMap draftMessage(map);
QVariantMap reply_to(draftMessage.value(REPLY_TO).toMap());
// In TdLib 1.8.21 reply_to_message_id has been replaced with reply_to
if (reply_to.value(_TYPE).toString() == TYPE_INPUT_MESSAGE_REPLY_TO_MESSAGE) {
if (reply_to.contains(MESSAGE_ID) &&
!draftMessage.contains(REPLY_TO_MESSAGE_ID)) {
// reply_to_message_id is what QML (still) expects
draftMessage.insert(REPLY_TO_MESSAGE_ID, reply_to.value(MESSAGE_ID));
}
reply_to.remove(_TYPE);
reply_to.insert(_TYPE, TYPE_INPUT_MESSAGE_REPLY_TO_MESSAGE); // Shared value
draftMessage.insert(REPLY_TO, reply_to);
draftMessage.remove(_TYPE);
draftMessage.insert(_TYPE, DRAFT_MESSAGE); // Shared value
if (updated) *updated = true;
return draftMessage;
}
} else if (type == TYPE_MESSAGE_STICKER) { } else if (type == TYPE_MESSAGE_STICKER) {
bool cleaned = false; bool cleaned = false;
const QVariantMap content(cleanupMap(map.value(CONTENT).toMap(), &cleaned)); const QVariantMap content(cleanupMap(map.value(CONTENT).toMap(), &cleaned));

View file

@ -52,6 +52,7 @@ signals:
void chatPinnedUpdated(qlonglong chatId, bool isPinned); void chatPinnedUpdated(qlonglong chatId, bool isPinned);
void chatReadInboxUpdated(const QString &chatId, const QString &lastReadInboxMessageId, int unreadCount); void chatReadInboxUpdated(const QString &chatId, const QString &lastReadInboxMessageId, int unreadCount);
void chatReadOutboxUpdated(const QString &chatId, const QString &lastReadOutboxMessageId); void chatReadOutboxUpdated(const QString &chatId, const QString &lastReadOutboxMessageId);
void chatAvailableReactionsUpdated(const qlonglong &chatId, const QVariantMap &availableReactions);
void basicGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation); void basicGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation);
void superGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation); void superGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation);
void chatOnlineMemberCountUpdated(const QString &chatId, int onlineMemberCount); void chatOnlineMemberCountUpdated(const QString &chatId, int onlineMemberCount);
@ -88,7 +89,8 @@ signals:
void chatTitleUpdated(const QString &chatId, const QString &title); void chatTitleUpdated(const QString &chatId, const QString &title);
void chatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId); void chatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId);
void messageIsPinnedUpdated(qlonglong chatId, qlonglong messageId, bool isPinned); void messageIsPinnedUpdated(qlonglong chatId, qlonglong messageId, bool isPinned);
void usersReceived(const QString &extra, const QVariantList &userIds, int totalUsers); void usersReceived(const QString &extra, const QVariantList &senders, int totalUsers);
void messageSendersReceived(const QString &extra, const QVariantList &userIds, int totalUsers);
void errorReceived(const int code, const QString &message, const QString &extra); void errorReceived(const int code, const QString &message, const QString &extra);
void secretChat(qlonglong secretChatId, const QVariantMap &secretChat); void secretChat(qlonglong secretChatId, const QVariantMap &secretChat);
void secretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat); void secretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat);
@ -101,10 +103,11 @@ signals:
void userPrivacySettingRulesUpdated(const QVariantMap &updatedRules); void userPrivacySettingRulesUpdated(const QVariantMap &updatedRules);
void messageInteractionInfoUpdated(qlonglong chatId, qlonglong messageId, const QVariantMap &updatedInfo); void messageInteractionInfoUpdated(qlonglong chatId, qlonglong messageId, const QVariantMap &updatedInfo);
void okReceived(const QString &request); void okReceived(const QString &request);
void sessionsReceived(const QVariantList &sessions); void sessionsReceived(int inactive_session_ttl_days, const QVariantList &sessions);
void availableReactionsReceived(qlonglong messageId, const QStringList &reactions); void availableReactionsReceived(qlonglong messageId, const QStringList &reactions);
void chatUnreadMentionCountUpdated(qlonglong chatId, int unreadMentionCount); void chatUnreadMentionCountUpdated(qlonglong chatId, int unreadMentionCount);
void chatUnreadReactionCountUpdated(qlonglong chatId, int unreadReactionCount); void chatUnreadReactionCountUpdated(qlonglong chatId, int unreadReactionCount);
void activeEmojiReactionsUpdated(const QStringList& emojis);
private: private:
typedef void (TDLibReceiver::*Handler)(const QVariantMap &); typedef void (TDLibReceiver::*Handler)(const QVariantMap &);
@ -134,11 +137,14 @@ private:
void processUpdateChatPosition(const QVariantMap &receivedInformation); void processUpdateChatPosition(const QVariantMap &receivedInformation);
void processUpdateChatReadInbox(const QVariantMap &receivedInformation); void processUpdateChatReadInbox(const QVariantMap &receivedInformation);
void processUpdateChatReadOutbox(const QVariantMap &receivedInformation); void processUpdateChatReadOutbox(const QVariantMap &receivedInformation);
void processUpdateChatAvailableReactions(const QVariantMap &receivedInformation);
void processUpdateBasicGroup(const QVariantMap &receivedInformation); void processUpdateBasicGroup(const QVariantMap &receivedInformation);
void processUpdateSuperGroup(const QVariantMap &receivedInformation); void processUpdateSuperGroup(const QVariantMap &receivedInformation);
void processChatOnlineMemberCountUpdated(const QVariantMap &receivedInformation); void processChatOnlineMemberCountUpdated(const QVariantMap &receivedInformation);
void processMessages(const QVariantMap &receivedInformation); void processMessages(const QVariantMap &receivedInformation);
void processFoundChatMessages(const QVariantMap &receivedInformation);
void processSponsoredMessage(const QVariantMap &receivedInformation); void processSponsoredMessage(const QVariantMap &receivedInformation);
void processSponsoredMessages(const QVariantMap &receivedInformation);
void processUpdateNewMessage(const QVariantMap &receivedInformation); void processUpdateNewMessage(const QVariantMap &receivedInformation);
void processMessage(const QVariantMap &receivedInformation); void processMessage(const QVariantMap &receivedInformation);
void processMessageLinkInfo(const QVariantMap &receivedInformation); void processMessageLinkInfo(const QVariantMap &receivedInformation);
@ -170,6 +176,7 @@ private:
void processUpdateChatPinnedMessage(const QVariantMap &receivedInformation); void processUpdateChatPinnedMessage(const QVariantMap &receivedInformation);
void processUpdateMessageIsPinned(const QVariantMap &receivedInformation); void processUpdateMessageIsPinned(const QVariantMap &receivedInformation);
void processUsers(const QVariantMap &receivedInformation); void processUsers(const QVariantMap &receivedInformation);
void processMessageSenders(const QVariantMap &receivedInformation);
void processError(const QVariantMap &receivedInformation); void processError(const QVariantMap &receivedInformation);
void processSecretChat(const QVariantMap &receivedInformation); void processSecretChat(const QVariantMap &receivedInformation);
void processUpdateSecretChat(const QVariantMap &receivedInformation); void processUpdateSecretChat(const QVariantMap &receivedInformation);
@ -186,6 +193,7 @@ private:
void processAvailableReactions(const QVariantMap &receivedInformation); void processAvailableReactions(const QVariantMap &receivedInformation);
void processUpdateChatUnreadMentionCount(const QVariantMap &receivedInformation); void processUpdateChatUnreadMentionCount(const QVariantMap &receivedInformation);
void processUpdateChatUnreadReactionCount(const QVariantMap &receivedInformation); void processUpdateChatUnreadReactionCount(const QVariantMap &receivedInformation);
void processUpdateActiveEmojiReactions(const QVariantMap &receivedInformation);
}; };
#endif // TDLIBRECEIVER_H #endif // TDLIBRECEIVER_H

View file

@ -36,6 +36,9 @@
#define DEBUG_MODULE TDLibWrapper #define DEBUG_MODULE TDLibWrapper
#include "debuglog.h" #include "debuglog.h"
#define VERSION_NUMBER(x,y,z) \
((((x) & 0x3ff) << 20) | (((y) & 0x3ff) << 10) | ((z) & 0x3ff))
namespace { namespace {
const QString STATUS("status"); const QString STATUS("status");
const QString ID("id"); const QString ID("id");
@ -45,26 +48,40 @@ namespace {
const QString LAST_NAME("last_name"); const QString LAST_NAME("last_name");
const QString FIRST_NAME("first_name"); const QString FIRST_NAME("first_name");
const QString USERNAME("username"); const QString USERNAME("username");
const QString USERNAMES("usernames");
const QString EDITABLE_USERNAME("editable_username");
const QString THREAD_ID("thread_id"); const QString THREAD_ID("thread_id");
const QString VALUE("value"); const QString VALUE("value");
const QString CHAT_LIST_TYPE("chat_list_type"); const QString CHAT_LIST_TYPE("chat_list_type");
const QString REPLY_TO_MESSAGE_ID("reply_to_message_id");
const QString REPLY_TO("reply_to");
const QString _TYPE("@type"); const QString _TYPE("@type");
const QString _EXTRA("@extra"); const QString _EXTRA("@extra");
const QString CHAT_LIST_MAIN("chatListMain"); const QString CHAT_LIST_MAIN("chatListMain");
const QString CHAT_AVAILABLE_REACTIONS("available_reactions");
const QString CHAT_AVAILABLE_REACTIONS_ALL("chatAvailableReactionsAll");
const QString CHAT_AVAILABLE_REACTIONS_SOME("chatAvailableReactionsSome");
const QString REACTIONS("reactions");
const QString REACTION_TYPE("reaction_type");
const QString REACTION_TYPE_EMOJI("reactionTypeEmoji");
const QString EMOJI("emoji");
const QString TYPE_MESSAGE_REPLY_TO_MESSAGE("messageReplyToMessage");
const QString TYPE_INPUT_MESSAGE_REPLY_TO_MESSAGE("inputMessageReplyToMessage");
} }
TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface, QObject *parent) TDLibWrapper::TDLibWrapper(AppSettings *settings, MceInterface *mce, QObject *parent)
: QObject(parent) : QObject(parent)
, tdLibClient(td_json_client_create())
, manager(new QNetworkAccessManager(this)) , manager(new QNetworkAccessManager(this))
, networkConfigurationManager(new QNetworkConfigurationManager(this)) , networkConfigurationManager(new QNetworkConfigurationManager(this))
, appSettings(settings)
, mceInterface(mce)
, authorizationState(AuthorizationState::Closed)
, versionNumber(0)
, joinChatRequested(false) , joinChatRequested(false)
, isLoggingOut(false)
{ {
LOG("Initializing TD Lib..."); LOG("Initializing TD Lib...");
this->appSettings = appSettings;
this->mceInterface = mceInterface;
this->tdLibClient = td_json_client_create();
this->authorizationState = AuthorizationState::Closed;
this->isLoggingOut = false;
initializeTDLibReceiver(); initializeTDLibReceiver();
@ -120,6 +137,7 @@ void TDLibWrapper::initializeTDLibReceiver() {
connect(this->tdLibReceiver, SIGNAL(chatOrderUpdated(QString, QString)), this, SIGNAL(chatOrderUpdated(QString, QString))); connect(this->tdLibReceiver, SIGNAL(chatOrderUpdated(QString, QString)), this, SIGNAL(chatOrderUpdated(QString, QString)));
connect(this->tdLibReceiver, SIGNAL(chatReadInboxUpdated(QString, QString, int)), this, SIGNAL(chatReadInboxUpdated(QString, QString, int))); connect(this->tdLibReceiver, SIGNAL(chatReadInboxUpdated(QString, QString, int)), this, SIGNAL(chatReadInboxUpdated(QString, QString, int)));
connect(this->tdLibReceiver, SIGNAL(chatReadOutboxUpdated(QString, QString)), this, SIGNAL(chatReadOutboxUpdated(QString, QString))); connect(this->tdLibReceiver, SIGNAL(chatReadOutboxUpdated(QString, QString)), this, SIGNAL(chatReadOutboxUpdated(QString, QString)));
connect(this->tdLibReceiver, SIGNAL(chatAvailableReactionsUpdated(qlonglong, QVariantMap)), this, SLOT(handleAvailableReactionsUpdated(qlonglong, QVariantMap)));
connect(this->tdLibReceiver, SIGNAL(basicGroupUpdated(qlonglong, QVariantMap)), this, SLOT(handleBasicGroupUpdated(qlonglong, QVariantMap))); connect(this->tdLibReceiver, SIGNAL(basicGroupUpdated(qlonglong, QVariantMap)), this, SLOT(handleBasicGroupUpdated(qlonglong, QVariantMap)));
connect(this->tdLibReceiver, SIGNAL(superGroupUpdated(qlonglong, QVariantMap)), this, SLOT(handleSuperGroupUpdated(qlonglong, QVariantMap))); connect(this->tdLibReceiver, SIGNAL(superGroupUpdated(qlonglong, QVariantMap)), this, SLOT(handleSuperGroupUpdated(qlonglong, QVariantMap)));
connect(this->tdLibReceiver, SIGNAL(chatOnlineMemberCountUpdated(QString, int)), this, SIGNAL(chatOnlineMemberCountUpdated(QString, int))); connect(this->tdLibReceiver, SIGNAL(chatOnlineMemberCountUpdated(QString, int)), this, SIGNAL(chatOnlineMemberCountUpdated(QString, int)));
@ -159,6 +177,7 @@ void TDLibWrapper::initializeTDLibReceiver() {
connect(this->tdLibReceiver, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)), this, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong))); connect(this->tdLibReceiver, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)), this, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)));
connect(this->tdLibReceiver, SIGNAL(messageIsPinnedUpdated(qlonglong, qlonglong, bool)), this, SLOT(handleMessageIsPinnedUpdated(qlonglong, qlonglong, bool))); connect(this->tdLibReceiver, SIGNAL(messageIsPinnedUpdated(qlonglong, qlonglong, bool)), this, SLOT(handleMessageIsPinnedUpdated(qlonglong, qlonglong, bool)));
connect(this->tdLibReceiver, SIGNAL(usersReceived(QString, QVariantList, int)), this, SIGNAL(usersReceived(QString, QVariantList, int))); connect(this->tdLibReceiver, SIGNAL(usersReceived(QString, QVariantList, int)), this, SIGNAL(usersReceived(QString, QVariantList, int)));
connect(this->tdLibReceiver, SIGNAL(messageSendersReceived(QString, QVariantList, int)), this, SIGNAL(messageSendersReceived(QString, QVariantList, int)));
connect(this->tdLibReceiver, SIGNAL(errorReceived(int, QString, QString)), this, SLOT(handleErrorReceived(int, QString, QString))); connect(this->tdLibReceiver, SIGNAL(errorReceived(int, QString, QString)), this, SLOT(handleErrorReceived(int, QString, QString)));
connect(this->tdLibReceiver, SIGNAL(contactsImported(QVariantList, QVariantList)), this, SIGNAL(contactsImported(QVariantList, QVariantList))); connect(this->tdLibReceiver, SIGNAL(contactsImported(QVariantList, QVariantList)), this, SIGNAL(contactsImported(QVariantList, QVariantList)));
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)));
@ -170,10 +189,11 @@ void TDLibWrapper::initializeTDLibReceiver() {
connect(this->tdLibReceiver, SIGNAL(userPrivacySettingRulesUpdated(QVariantMap)), this, SLOT(handleUpdatedUserPrivacySettingRules(QVariantMap))); connect(this->tdLibReceiver, SIGNAL(userPrivacySettingRulesUpdated(QVariantMap)), this, SLOT(handleUpdatedUserPrivacySettingRules(QVariantMap)));
connect(this->tdLibReceiver, SIGNAL(messageInteractionInfoUpdated(qlonglong, qlonglong, QVariantMap)), this, SIGNAL(messageInteractionInfoUpdated(qlonglong, qlonglong, QVariantMap))); connect(this->tdLibReceiver, SIGNAL(messageInteractionInfoUpdated(qlonglong, qlonglong, QVariantMap)), this, SIGNAL(messageInteractionInfoUpdated(qlonglong, qlonglong, QVariantMap)));
connect(this->tdLibReceiver, SIGNAL(okReceived(QString)), this, SIGNAL(okReceived(QString))); connect(this->tdLibReceiver, SIGNAL(okReceived(QString)), this, SIGNAL(okReceived(QString)));
connect(this->tdLibReceiver, SIGNAL(sessionsReceived(QVariantList)), this, SIGNAL(sessionsReceived(QVariantList))); connect(this->tdLibReceiver, SIGNAL(sessionsReceived(int, QVariantList)), this, SIGNAL(sessionsReceived(int, QVariantList)));
connect(this->tdLibReceiver, SIGNAL(availableReactionsReceived(qlonglong, QStringList)), this, SIGNAL(availableReactionsReceived(qlonglong, QStringList))); connect(this->tdLibReceiver, SIGNAL(availableReactionsReceived(qlonglong, QStringList)), this, SIGNAL(availableReactionsReceived(qlonglong, QStringList)));
connect(this->tdLibReceiver, SIGNAL(chatUnreadMentionCountUpdated(qlonglong, int)), this, SIGNAL(chatUnreadMentionCountUpdated(qlonglong, int))); connect(this->tdLibReceiver, SIGNAL(chatUnreadMentionCountUpdated(qlonglong, int)), this, SIGNAL(chatUnreadMentionCountUpdated(qlonglong, int)));
connect(this->tdLibReceiver, SIGNAL(chatUnreadReactionCountUpdated(qlonglong, int)), this, SIGNAL(chatUnreadReactionCountUpdated(qlonglong, int))); connect(this->tdLibReceiver, SIGNAL(chatUnreadReactionCountUpdated(qlonglong, int)), this, SIGNAL(chatUnreadReactionCountUpdated(qlonglong, int)));
connect(this->tdLibReceiver, SIGNAL(activeEmojiReactionsUpdated(QStringList)), this, SLOT(handleActiveEmojiReactionsUpdated(QStringList)));
this->tdLibReceiver->start(); this->tdLibReceiver->start();
} }
@ -192,7 +212,7 @@ void TDLibWrapper::sendRequest(const QVariantMap &requestObject)
QString TDLibWrapper::getVersion() QString TDLibWrapper::getVersion()
{ {
return this->version; return this->versionString;
} }
TDLibWrapper::AuthorizationState TDLibWrapper::getAuthorizationState() TDLibWrapper::AuthorizationState TDLibWrapper::getAuthorizationState()
@ -255,7 +275,7 @@ void TDLibWrapper::logout()
{ {
LOG("Logging out"); LOG("Logging out");
QVariantMap requestObject; QVariantMap requestObject;
requestObject.insert("@type", "logOut"); requestObject.insert(_TYPE, "logOut");
this->sendRequest(requestObject); this->sendRequest(requestObject);
this->isLoggingOut = true; this->isLoggingOut = true;
@ -389,15 +409,24 @@ static bool compareReplacements(const QVariant &replacement1, const QVariant &re
} }
} }
void TDLibWrapper::sendTextMessage(const QString &chatId, const QString &message, const QString &replyToMessageId) QVariantMap TDLibWrapper::newSendMessageRequest(qlonglong chatId, qlonglong replyToMessageId)
{
QVariantMap request;
request.insert(_TYPE, "sendMessage");
request.insert(CHAT_ID, chatId);
if (replyToMessageId) {
QVariantMap replyTo;
replyTo.insert(_TYPE, TYPE_INPUT_MESSAGE_REPLY_TO_MESSAGE);
replyTo.insert(MESSAGE_ID, replyToMessageId);
request.insert(REPLY_TO, replyTo);
}
return request;
}
void TDLibWrapper::sendTextMessage(qlonglong chatId, const QString &message, qlonglong replyToMessageId)
{ {
LOG("Sending text message" << chatId << message << replyToMessageId); LOG("Sending text message" << chatId << message << replyToMessageId);
QVariantMap requestObject; QVariantMap requestObject(newSendMessageRequest(chatId, replyToMessageId));
requestObject.insert(_TYPE, "sendMessage");
requestObject.insert(CHAT_ID, chatId);
if (replyToMessageId != "0") {
requestObject.insert("reply_to_message_id", replyToMessageId);
}
QVariantMap inputMessageContent; QVariantMap inputMessageContent;
inputMessageContent.insert(_TYPE, "inputMessageText"); inputMessageContent.insert(_TYPE, "inputMessageText");
@ -436,7 +465,7 @@ void TDLibWrapper::sendTextMessage(const QString &chatId, const QString &message
QVariantMap entityType; QVariantMap entityType;
entityType.insert(_TYPE, "textEntityTypeMentionName"); entityType.insert(_TYPE, "textEntityTypeMentionName");
entityType.insert("user_id", nextReplacement.value("userId").toString()); entityType.insert("user_id", nextReplacement.value("userId").toString());
entity.insert("type", entityType); entity.insert(TYPE, entityType);
entities.append(entity); entities.append(entity);
offsetCorrection += replacementLength - replacementPlainText.length(); offsetCorrection += replacementLength - replacementPlainText.length();
} }
@ -450,17 +479,13 @@ void TDLibWrapper::sendTextMessage(const QString &chatId, const QString &message
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::sendPhotoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId) void TDLibWrapper::sendPhotoMessage(qlonglong chatId, const QString &filePath, const QString &message, qlonglong replyToMessageId)
{ {
LOG("Sending photo message" << chatId << filePath << message << replyToMessageId); LOG("Sending photo message" << chatId << filePath << message << replyToMessageId);
QVariantMap requestObject; QVariantMap requestObject(newSendMessageRequest(chatId, replyToMessageId));
requestObject.insert(_TYPE, "sendMessage");
requestObject.insert(CHAT_ID, chatId);
if (replyToMessageId != "0") {
requestObject.insert("reply_to_message_id", replyToMessageId);
}
QVariantMap inputMessageContent; QVariantMap inputMessageContent;
inputMessageContent.insert(_TYPE, "inputMessagePhoto"); inputMessageContent.insert(_TYPE, "inputMessagePhoto");
QVariantMap formattedText; QVariantMap formattedText;
formattedText.insert("text", message); formattedText.insert("text", message);
formattedText.insert(_TYPE, "formattedText"); formattedText.insert(_TYPE, "formattedText");
@ -474,17 +499,13 @@ void TDLibWrapper::sendPhotoMessage(const QString &chatId, const QString &filePa
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::sendVideoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId) void TDLibWrapper::sendVideoMessage(qlonglong chatId, const QString &filePath, const QString &message, qlonglong replyToMessageId)
{ {
LOG("Sending video message" << chatId << filePath << message << replyToMessageId); LOG("Sending video message" << chatId << filePath << message << replyToMessageId);
QVariantMap requestObject; QVariantMap requestObject(newSendMessageRequest(chatId, replyToMessageId));
requestObject.insert(_TYPE, "sendMessage");
requestObject.insert(CHAT_ID, chatId);
if (replyToMessageId != "0") {
requestObject.insert("reply_to_message_id", replyToMessageId);
}
QVariantMap inputMessageContent; QVariantMap inputMessageContent;
inputMessageContent.insert(_TYPE, "inputMessageVideo"); inputMessageContent.insert(_TYPE, "inputMessageVideo");
QVariantMap formattedText; QVariantMap formattedText;
formattedText.insert("text", message); formattedText.insert("text", message);
formattedText.insert(_TYPE, "formattedText"); formattedText.insert(_TYPE, "formattedText");
@ -498,17 +519,13 @@ void TDLibWrapper::sendVideoMessage(const QString &chatId, const QString &filePa
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::sendDocumentMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId) void TDLibWrapper::sendDocumentMessage(qlonglong chatId, const QString &filePath, const QString &message, qlonglong replyToMessageId)
{ {
LOG("Sending document message" << chatId << filePath << message << replyToMessageId); LOG("Sending document message" << chatId << filePath << message << replyToMessageId);
QVariantMap requestObject; QVariantMap requestObject(newSendMessageRequest(chatId, replyToMessageId));
requestObject.insert(_TYPE, "sendMessage");
requestObject.insert(CHAT_ID, chatId);
if (replyToMessageId != "0") {
requestObject.insert("reply_to_message_id", replyToMessageId);
}
QVariantMap inputMessageContent; QVariantMap inputMessageContent;
inputMessageContent.insert(_TYPE, "inputMessageDocument"); inputMessageContent.insert(_TYPE, "inputMessageDocument");
QVariantMap formattedText; QVariantMap formattedText;
formattedText.insert("text", message); formattedText.insert("text", message);
formattedText.insert(_TYPE, "formattedText"); formattedText.insert(_TYPE, "formattedText");
@ -522,17 +539,13 @@ void TDLibWrapper::sendDocumentMessage(const QString &chatId, const QString &fil
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::sendVoiceNoteMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId) void TDLibWrapper::sendVoiceNoteMessage(qlonglong chatId, const QString &filePath, const QString &message, qlonglong replyToMessageId)
{ {
LOG("Sending voice note message" << chatId << filePath << message << replyToMessageId); LOG("Sending voice note message" << chatId << filePath << message << replyToMessageId);
QVariantMap requestObject; QVariantMap requestObject(newSendMessageRequest(chatId, replyToMessageId));
requestObject.insert(_TYPE, "sendMessage");
requestObject.insert(CHAT_ID, chatId);
if (replyToMessageId != "0") {
requestObject.insert("reply_to_message_id", replyToMessageId);
}
QVariantMap inputMessageContent; QVariantMap inputMessageContent;
inputMessageContent.insert(_TYPE, "inputMessageVoiceNote"); inputMessageContent.insert(_TYPE, "inputMessageVoiceNote");
QVariantMap formattedText; QVariantMap formattedText;
formattedText.insert("text", message); formattedText.insert("text", message);
formattedText.insert(_TYPE, "formattedText"); formattedText.insert(_TYPE, "formattedText");
@ -546,24 +559,19 @@ void TDLibWrapper::sendVoiceNoteMessage(const QString &chatId, const QString &fi
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::sendLocationMessage(const QString &chatId, double latitude, double longitude, double horizontalAccuracy, const QString &replyToMessageId) void TDLibWrapper::sendLocationMessage(qlonglong chatId, double latitude, double longitude, double horizontalAccuracy, qlonglong replyToMessageId)
{ {
LOG("Sending location message" << chatId << latitude << longitude << horizontalAccuracy << replyToMessageId); LOG("Sending location message" << chatId << latitude << longitude << horizontalAccuracy << replyToMessageId);
QVariantMap requestObject; QVariantMap requestObject(newSendMessageRequest(chatId, replyToMessageId));
requestObject.insert(_TYPE, "sendMessage");
requestObject.insert(CHAT_ID, chatId);
if (replyToMessageId != "0") {
requestObject.insert("reply_to_message_id", replyToMessageId);
}
QVariantMap inputMessageContent; QVariantMap inputMessageContent;
inputMessageContent.insert(_TYPE, "inputMessageLocation"); inputMessageContent.insert(_TYPE, "inputMessageLocation");
QVariantMap location; QVariantMap location;
location.insert("latitude", latitude); location.insert("latitude", latitude);
location.insert("longitude", longitude); location.insert("longitude", longitude);
location.insert("horizontal_accuracy", horizontalAccuracy); location.insert("horizontal_accuracy", horizontalAccuracy);
location.insert(_TYPE, "location"); location.insert(_TYPE, "location");
inputMessageContent.insert("location", location); inputMessageContent.insert("location", location);
inputMessageContent.insert("live_period", 0); inputMessageContent.insert("live_period", 0);
inputMessageContent.insert("heading", 0); inputMessageContent.insert("heading", 0);
inputMessageContent.insert("proximity_alert_radius", 0); inputMessageContent.insert("proximity_alert_radius", 0);
@ -572,21 +580,16 @@ void TDLibWrapper::sendLocationMessage(const QString &chatId, double latitude, d
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::sendStickerMessage(const QString &chatId, const QString &fileId, const QString &replyToMessageId) void TDLibWrapper::sendStickerMessage(qlonglong chatId, const QString &fileId, qlonglong replyToMessageId)
{ {
LOG("Sending sticker message" << chatId << fileId << replyToMessageId); LOG("Sending sticker message" << chatId << fileId << replyToMessageId);
QVariantMap requestObject; QVariantMap requestObject(newSendMessageRequest(chatId, replyToMessageId));
requestObject.insert(_TYPE, "sendMessage");
requestObject.insert(CHAT_ID, chatId);
if (replyToMessageId != "0") {
requestObject.insert("reply_to_message_id", replyToMessageId);
}
QVariantMap inputMessageContent; QVariantMap inputMessageContent;
inputMessageContent.insert(_TYPE, "inputMessageSticker"); inputMessageContent.insert(_TYPE, "inputMessageSticker");
QVariantMap stickerInputFile; QVariantMap stickerInputFile;
stickerInputFile.insert(_TYPE, "inputFileRemote"); stickerInputFile.insert(_TYPE, "inputFileRemote");
stickerInputFile.insert("id", fileId); stickerInputFile.insert(ID, fileId);
inputMessageContent.insert("sticker", stickerInputFile); inputMessageContent.insert("sticker", stickerInputFile);
@ -594,15 +597,10 @@ void TDLibWrapper::sendStickerMessage(const QString &chatId, const QString &file
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::sendPollMessage(const QString &chatId, const QString &question, const QVariantList &options, bool anonymous, int correctOption, bool multiple, const QString &explanation, const QString &replyToMessageId) void TDLibWrapper::sendPollMessage(qlonglong chatId, const QString &question, const QVariantList &options, bool anonymous, int correctOption, bool multiple, const QString &explanation, qlonglong replyToMessageId)
{ {
LOG("Sending poll message" << chatId << question << replyToMessageId); LOG("Sending poll message" << chatId << question << replyToMessageId);
QVariantMap requestObject; QVariantMap requestObject(newSendMessageRequest(chatId, replyToMessageId));
requestObject.insert(_TYPE, "sendMessage");
requestObject.insert(CHAT_ID, chatId);
if (replyToMessageId != "0") {
requestObject.insert("reply_to_message_id", replyToMessageId);
}
QVariantMap inputMessageContent; QVariantMap inputMessageContent;
inputMessageContent.insert(_TYPE, "inputMessagePoll"); inputMessageContent.insert(_TYPE, "inputMessagePoll");
@ -620,7 +618,7 @@ void TDLibWrapper::sendPollMessage(const QString &chatId, const QString &questio
pollType.insert("allow_multiple_answers", multiple); pollType.insert("allow_multiple_answers", multiple);
} }
inputMessageContent.insert("type", pollType); inputMessageContent.insert(TYPE, pollType);
inputMessageContent.insert("question", question); inputMessageContent.insert("question", question);
inputMessageContent.insert("options", options); inputMessageContent.insert("options", options);
inputMessageContent.insert("is_anonymous", anonymous); inputMessageContent.insert("is_anonymous", anonymous);
@ -709,7 +707,11 @@ void TDLibWrapper::getChatSponsoredMessage(qlonglong chatId)
{ {
LOG("Retrieving sponsored message" << chatId); LOG("Retrieving sponsored message" << chatId);
QVariantMap requestObject; QVariantMap requestObject;
requestObject.insert(_TYPE, "getChatSponsoredMessage"); // getChatSponsoredMessage has been replaced with getChatSponsoredMessages
// between 1.8.7 and 1.8.8
// See https://github.com/tdlib/td/commit/ec1310a
requestObject.insert(_TYPE, QString((versionNumber > VERSION_NUMBER(1,8,7)) ?
"getChatSponsoredMessages" : "getChatSponsoredMessage"));
requestObject.insert(CHAT_ID, chatId); requestObject.insert(CHAT_ID, chatId);
requestObject.insert(_EXTRA, chatId); // see TDLibReceiver::processSponsoredMessage requestObject.insert(_EXTRA, chatId); // see TDLibReceiver::processSponsoredMessage
this->sendRequest(requestObject); this->sendRequest(requestObject);
@ -1171,9 +1173,18 @@ void TDLibWrapper::setChatDraftMessage(qlonglong chatId, qlonglong threadId, qlo
inputMessageContent.insert(_TYPE, "inputMessageText"); inputMessageContent.insert(_TYPE, "inputMessageText");
inputMessageContent.insert("text", formattedText); inputMessageContent.insert("text", formattedText);
draftMessage.insert(_TYPE, "draftMessage"); draftMessage.insert(_TYPE, "draftMessage");
draftMessage.insert("reply_to_message_id", replyToMessageId);
draftMessage.insert("input_message_text", inputMessageContent); draftMessage.insert("input_message_text", inputMessageContent);
if (versionNumber > VERSION_NUMBER(1,8,20)) {
QVariantMap replyTo;
replyTo.insert(_TYPE, TYPE_INPUT_MESSAGE_REPLY_TO_MESSAGE);
replyTo.insert(CHAT_ID, chatId);
replyTo.insert(MESSAGE_ID, replyToMessageId);
draftMessage.insert(REPLY_TO, replyTo);
} else {
draftMessage.insert(REPLY_TO_MESSAGE_ID, replyToMessageId);
}
requestObject.insert("draft_message", draftMessage); requestObject.insert("draft_message", draftMessage);
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
@ -1433,8 +1444,8 @@ void TDLibWrapper::getMessageAvailableReactions(qlonglong chatId, qlonglong mess
QVariantMap requestObject; QVariantMap requestObject;
requestObject.insert(_TYPE, "getMessageAvailableReactions"); requestObject.insert(_TYPE, "getMessageAvailableReactions");
requestObject.insert(_EXTRA, QString::number(messageId)); requestObject.insert(_EXTRA, QString::number(messageId));
requestObject.insert("chat_id", chatId); requestObject.insert(CHAT_ID, chatId);
requestObject.insert("message_id", messageId); requestObject.insert(MESSAGE_ID, messageId);
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
@ -1457,11 +1468,23 @@ void TDLibWrapper::setMessageReaction(qlonglong chatId, qlonglong messageId, con
{ {
LOG("Set message reaction" << chatId << messageId << reaction); LOG("Set message reaction" << chatId << messageId << reaction);
QVariantMap requestObject; QVariantMap requestObject;
requestObject.insert(_TYPE, "setMessageReaction"); requestObject.insert(CHAT_ID, chatId);
requestObject.insert("chat_id", chatId); requestObject.insert(MESSAGE_ID, messageId);
requestObject.insert("message_id", messageId);
requestObject.insert("reaction", reaction);
requestObject.insert("is_big", false); requestObject.insert("is_big", false);
if (versionNumber > VERSION_NUMBER(1,8,5)) {
// "reaction_type": {
// "@type": "reactionTypeEmoji",
// "emoji": "..."
// }
QVariantMap reactionType;
reactionType.insert(_TYPE, REACTION_TYPE_EMOJI);
reactionType.insert(EMOJI, reaction);
requestObject.insert(REACTION_TYPE, reactionType);
requestObject.insert(_TYPE, "addMessageReaction");
} else {
requestObject.insert("reaction", reaction);
requestObject.insert(_TYPE, "setMessageReaction");
}
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
@ -1494,11 +1517,19 @@ void TDLibWrapper::setNetworkType(NetworkType networkType)
break; break;
} }
requestObject.insert("type", networkTypeObject); requestObject.insert(TYPE, networkTypeObject);
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::setInactiveSessionTtl(int days)
{
QVariantMap requestObject;
requestObject.insert(_TYPE, "setInactiveSessionTtl");
requestObject.insert("inactive_session_ttl_days", days);
this->sendRequest(requestObject);
}
void TDLibWrapper::searchEmoji(const QString &queryString) void TDLibWrapper::searchEmoji(const QString &queryString)
{ {
LOG("Searching emoji" << queryString); LOG("Searching emoji" << queryString);
@ -1575,6 +1606,49 @@ QVariantMap TDLibWrapper::getChat(const QString &chatId)
return this->chats.value(chatId).toMap(); return this->chats.value(chatId).toMap();
} }
QStringList TDLibWrapper::getChatReactions(const QString &chatId)
{
LOG("Obtaining chat reactions for chat" << chatId);
const QVariant available_reactions(chats.value(chatId).toMap().value(CHAT_AVAILABLE_REACTIONS));
const QVariantMap map(available_reactions.toMap());
const QString reactions_type(map.value(_TYPE).toString());
if (reactions_type == CHAT_AVAILABLE_REACTIONS_ALL) {
LOG("Chat uses all available reactions, currently available number" << activeEmojiReactions.size());
return activeEmojiReactions;
} else if (reactions_type == CHAT_AVAILABLE_REACTIONS_SOME) {
LOG("Chat uses reduced set of reactions");
const QVariantList reactions(map.value(REACTIONS).toList());
const int n = reactions.count();
QStringList emojis;
// "available_reactions": {
// "@type": "chatAvailableReactionsSome",
// "reactions": [
// {
// "@type": "reactionTypeEmoji",
// "emoji": "..."
// },
emojis.reserve(n);
for (int i = 0; i < n; i++) {
const QVariantMap reaction(reactions.at(i).toMap());
if (reaction.value(_TYPE).toString() == REACTION_TYPE_EMOJI) {
const QString emoji(reaction.value(EMOJI).toString());
if (!emoji.isEmpty()) {
emojis.append(emoji);
}
}
}
LOG("Found emojis for this chat" << emojis.size());
return emojis;
} else if (reactions_type.isEmpty()) {
LOG("No chat reaction type specified, using all reactions");
return available_reactions.toStringList();
} else {
LOG("Unknown chat reaction type" << reactions_type);
return QStringList();
}
}
QVariantMap TDLibWrapper::getSecretChatFromCache(qlonglong secretChatId) QVariantMap TDLibWrapper::getSecretChatFromCache(qlonglong secretChatId)
{ {
return this->secretChats.value(secretChatId); return this->secretChats.value(secretChatId);
@ -1645,7 +1719,16 @@ DBusAdaptor *TDLibWrapper::getDBusAdaptor()
void TDLibWrapper::handleVersionDetected(const QString &version) void TDLibWrapper::handleVersionDetected(const QString &version)
{ {
this->version = version; this->versionString = version;
const QStringList parts(version.split('.'));
uint major, minor, release;
bool ok;
if (parts.count() >= 3 &&
(major = parts.at(0).toInt(&ok), ok) &&
(minor = parts.at(1).toInt(&ok), ok) &&
(release = parts.at(2).toInt(&ok), ok)) {
versionNumber = VERSION_NUMBER(major, minor, release);
}
emit versionDetected(version); emit versionDetected(version);
} }
@ -1758,15 +1841,15 @@ void TDLibWrapper::handleConnectionStateChanged(const QString &connectionState)
void TDLibWrapper::handleUserUpdated(const QVariantMap &userInformation) void TDLibWrapper::handleUserUpdated(const QVariantMap &userInformation)
{ {
QString updatedUserId = userInformation.value("id").toString(); QString updatedUserId = userInformation.value(ID).toString();
if (updatedUserId == this->options.value("my_id").toString()) { if (updatedUserId == this->options.value("my_id").toString()) {
LOG("Own user information updated :)"); LOG("Own user information updated :)");
this->userInformation = userInformation; this->userInformation = userInformation;
emit ownUserUpdated(userInformation); emit ownUserUpdated(userInformation);
} }
LOG("User information updated:" << userInformation.value(USERNAME).toString() << userInformation.value(FIRST_NAME).toString() << userInformation.value(LAST_NAME).toString()); LOG("User information updated:" << userInformation.value(USERNAMES).toMap().value(EDITABLE_USERNAME).toString() << userInformation.value(FIRST_NAME).toString() << userInformation.value(LAST_NAME).toString());
this->allUsers.insert(updatedUserId, userInformation); this->allUsers.insert(updatedUserId, userInformation);
this->allUserNames.insert(userInformation.value(USERNAME).toString(), userInformation); this->allUserNames.insert(userInformation.value(USERNAMES).toMap().value(EDITABLE_USERNAME).toString(), userInformation);
emit userUpdated(updatedUserId, userInformation); emit userUpdated(updatedUserId, userInformation);
} }
@ -1774,24 +1857,24 @@ void TDLibWrapper::handleUserStatusUpdated(const QString &userId, const QVariant
{ {
if (userId == this->options.value("my_id").toString()) { if (userId == this->options.value("my_id").toString()) {
LOG("Own user status information updated :)"); LOG("Own user status information updated :)");
this->userInformation.insert("status", userStatusInformation); this->userInformation.insert(STATUS, userStatusInformation);
} }
LOG("User status information updated:" << userId << userStatusInformation.value(_TYPE).toString()); LOG("User status information updated:" << userId << userStatusInformation.value(_TYPE).toString());
QVariantMap updatedUserInformation = this->allUsers.value(userId).toMap(); QVariantMap updatedUserInformation = this->allUsers.value(userId).toMap();
updatedUserInformation.insert("status", userStatusInformation); updatedUserInformation.insert(STATUS, userStatusInformation);
this->allUsers.insert(userId, updatedUserInformation); this->allUsers.insert(userId, updatedUserInformation);
this->allUserNames.insert(userInformation.value(USERNAME).toString(), userInformation); this->allUserNames.insert(userInformation.value(USERNAMES).toMap().value(EDITABLE_USERNAME).toString(), userInformation);
emit userUpdated(userId, updatedUserInformation); emit userUpdated(userId, updatedUserInformation);
} }
void TDLibWrapper::handleFileUpdated(const QVariantMap &fileInformation) void TDLibWrapper::handleFileUpdated(const QVariantMap &fileInformation)
{ {
emit fileUpdated(fileInformation.value("id").toInt(), fileInformation); emit fileUpdated(fileInformation.value(ID).toInt(), fileInformation);
} }
void TDLibWrapper::handleNewChatDiscovered(const QVariantMap &chatInformation) void TDLibWrapper::handleNewChatDiscovered(const QVariantMap &chatInformation)
{ {
QString chatId = chatInformation.value("id").toString(); QString chatId = chatInformation.value(ID).toString();
this->chats.insert(chatId, chatInformation); this->chats.insert(chatId, chatInformation);
emit newChatDiscovered(chatId, chatInformation); emit newChatDiscovered(chatId, chatInformation);
} }
@ -1831,6 +1914,17 @@ void TDLibWrapper::handleUnreadChatCountUpdated(const QVariantMap &chatCountInfo
} }
} }
void TDLibWrapper::handleAvailableReactionsUpdated(qlonglong chatId, const QVariantMap &availableReactions)
{
LOG("Updating available reactions for chat" << chatId << availableReactions);
QString chatIdString = QString::number(chatId);
QVariantMap chatInformation = this->getChat(chatIdString);
chatInformation.insert(CHAT_AVAILABLE_REACTIONS, availableReactions);
this->chats.insert(chatIdString, chatInformation);
emit chatAvailableReactionsUpdated(chatId, availableReactions);
}
void TDLibWrapper::handleBasicGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation) void TDLibWrapper::handleBasicGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation)
{ {
emit basicGroupUpdated(updateGroup(groupId, groupInformation, &basicGroups)->groupId); emit basicGroupUpdated(updateGroup(groupId, groupInformation, &basicGroups)->groupId);
@ -1856,7 +1950,7 @@ void TDLibWrapper::handleStickerSets(const QVariantList &stickerSets)
QListIterator<QVariant> stickerSetIterator(stickerSets); QListIterator<QVariant> stickerSetIterator(stickerSets);
while (stickerSetIterator.hasNext()) { while (stickerSetIterator.hasNext()) {
QVariantMap stickerSet = stickerSetIterator.next().toMap(); QVariantMap stickerSet = stickerSetIterator.next().toMap();
this->getStickerSet(stickerSet.value("id").toString()); this->getStickerSet(stickerSet.value(ID).toString());
} }
emit this->stickerSetsReceived(stickerSets); emit this->stickerSetsReceived(stickerSets);
} }
@ -1988,6 +2082,13 @@ void TDLibWrapper::handleSponsoredMessage(qlonglong chatId, const QVariantMap &m
} }
} }
void TDLibWrapper::handleActiveEmojiReactionsUpdated(const QStringList& emojis)
{
if (activeEmojiReactions != emojis) {
activeEmojiReactions = emojis;
LOG(emojis.count() << "reaction(s) available");
}
}
void TDLibWrapper::handleNetworkConfigurationChanged(const QNetworkConfiguration &config) void TDLibWrapper::handleNetworkConfigurationChanged(const QNetworkConfiguration &config)
{ {
@ -2077,28 +2178,40 @@ void TDLibWrapper::handleGetPageSourceFinished()
} }
} }
QVariantMap& TDLibWrapper::fillTdlibParameters(QVariantMap& parameters)
{
parameters.insert("api_id", TDLIB_API_ID);
parameters.insert("api_hash", TDLIB_API_HASH);
parameters.insert("database_directory", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tdlib");
bool onlineOnlyMode = this->appSettings->onlineOnlyMode();
parameters.insert("use_file_database", !onlineOnlyMode);
parameters.insert("use_chat_info_database", !onlineOnlyMode);
parameters.insert("use_message_database", !onlineOnlyMode);
parameters.insert("use_secret_chats", true);
parameters.insert("system_language_code", QLocale::system().name());
QSettings hardwareSettings("/etc/hw-release", QSettings::NativeFormat);
parameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString());
parameters.insert("system_version", QSysInfo::prettyProductName());
parameters.insert("application_version", "0.17");
parameters.insert("enable_storage_optimizer", appSettings->storageOptimizer());
// parameters.insert("use_test_dc", true);
return parameters;
}
void TDLibWrapper::setInitialParameters() void TDLibWrapper::setInitialParameters()
{ {
LOG("Sending initial parameters to TD Lib"); LOG("Sending initial parameters to TD Lib");
QVariantMap requestObject; QVariantMap requestObject;
requestObject.insert(_TYPE, "setTdlibParameters"); requestObject.insert(_TYPE, "setTdlibParameters");
QVariantMap initialParameters; // tdlibParameters were inlined between 1.8.5 and 1.8.6
initialParameters.insert("api_id", TDLIB_API_ID); // See https://github.com/tdlib/td/commit/f6a2ecd
initialParameters.insert("api_hash", TDLIB_API_HASH); if (versionNumber > VERSION_NUMBER(1,8,5)) {
initialParameters.insert("database_directory", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tdlib"); fillTdlibParameters(requestObject);
bool onlineOnlyMode = this->appSettings->onlineOnlyMode(); } else {
initialParameters.insert("use_file_database", !onlineOnlyMode); QVariantMap initialParameters;
initialParameters.insert("use_chat_info_database", !onlineOnlyMode); fillTdlibParameters(initialParameters);
initialParameters.insert("use_message_database", !onlineOnlyMode); requestObject.insert("parameters", initialParameters);
initialParameters.insert("use_secret_chats", true); }
initialParameters.insert("system_language_code", QLocale::system().name());
QSettings hardwareSettings("/etc/hw-release", QSettings::NativeFormat);
initialParameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString());
initialParameters.insert("system_version", QSysInfo::prettyProductName());
initialParameters.insert("application_version", "0.17");
initialParameters.insert("enable_storage_optimizer", appSettings->storageOptimizer());
// initialParameters.insert("use_test_dc", true);
requestObject.insert("parameters", initialParameters);
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }

View file

@ -148,6 +148,7 @@ public:
Q_INVOKABLE QVariantMap getSuperGroup(qlonglong groupId) const; Q_INVOKABLE QVariantMap getSuperGroup(qlonglong groupId) const;
Q_INVOKABLE QVariantMap getChat(const QString &chatId); Q_INVOKABLE QVariantMap getChat(const QString &chatId);
Q_INVOKABLE QVariantMap getSecretChatFromCache(qlonglong secretChatId); Q_INVOKABLE QVariantMap getSecretChatFromCache(qlonglong secretChatId);
Q_INVOKABLE QStringList getChatReactions(const QString &chatId);
Q_INVOKABLE QString getOptionString(const QString &optionName); Q_INVOKABLE QString getOptionString(const QString &optionName);
Q_INVOKABLE void copyFileToDownloads(const QString &filePath, bool openAfterCopy = false); Q_INVOKABLE void copyFileToDownloads(const QString &filePath, bool openAfterCopy = false);
Q_INVOKABLE void openFileOnDevice(const QString &filePath); Q_INVOKABLE void openFileOnDevice(const QString &filePath);
@ -175,14 +176,14 @@ public:
Q_INVOKABLE void viewMessage(qlonglong chatId, qlonglong messageId, bool force); Q_INVOKABLE void viewMessage(qlonglong chatId, qlonglong messageId, bool force);
Q_INVOKABLE void pinMessage(const QString &chatId, const QString &messageId, bool disableNotification = false); Q_INVOKABLE void pinMessage(const QString &chatId, const QString &messageId, bool disableNotification = false);
Q_INVOKABLE void unpinMessage(const QString &chatId, const QString &messageId); Q_INVOKABLE void unpinMessage(const QString &chatId, const QString &messageId);
Q_INVOKABLE void sendTextMessage(const QString &chatId, const QString &message, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendTextMessage(qlonglong chatId, const QString &message, qlonglong replyToMessageId = 0);
Q_INVOKABLE void sendPhotoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendPhotoMessage(qlonglong chatId, const QString &filePath, const QString &message, qlonglong replyToMessageId = 0);
Q_INVOKABLE void sendVideoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendVideoMessage(qlonglong chatId, const QString &filePath, const QString &message, qlonglong replyToMessageId = 0);
Q_INVOKABLE void sendDocumentMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendDocumentMessage(qlonglong chatId, const QString &filePath, const QString &message, qlonglong replyToMessageId = 0);
Q_INVOKABLE void sendVoiceNoteMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendVoiceNoteMessage(qlonglong chatId, const QString &filePath, const QString &message, qlonglong replyToMessageId = 0);
Q_INVOKABLE void sendLocationMessage(const QString &chatId, double latitude, double longitude, double horizontalAccuracy, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendLocationMessage(qlonglong chatId, double latitude, double longitude, double horizontalAccuracy, qlonglong replyToMessageId = 0);
Q_INVOKABLE void sendStickerMessage(const QString &chatId, const QString &fileId, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendStickerMessage(qlonglong chatId, const QString &fileId, qlonglong replyToMessageId = 0);
Q_INVOKABLE void sendPollMessage(const QString &chatId, const QString &question, const QVariantList &options, bool anonymous, int correctOption, bool multiple, const QString &explanation, const QString &replyToMessageId = "0"); Q_INVOKABLE void sendPollMessage(qlonglong chatId, const QString &question, const QVariantList &options, bool anonymous, int correctOption, bool multiple, const QString &explanation, qlonglong replyToMessageId = 0);
Q_INVOKABLE void forwardMessages(const QString &chatId, const QString &fromChatId, const QVariantList &messageIds, bool sendCopy, bool removeCaption); Q_INVOKABLE void forwardMessages(const QString &chatId, const QString &fromChatId, const QVariantList &messageIds, bool sendCopy, bool removeCaption);
Q_INVOKABLE void getMessage(qlonglong chatId, qlonglong messageId); Q_INVOKABLE void getMessage(qlonglong chatId, qlonglong messageId);
Q_INVOKABLE void getMessageLinkInfo(const QString &url, const QString &extra = ""); Q_INVOKABLE void getMessageLinkInfo(const QString &url, const QString &extra = "");
@ -250,6 +251,7 @@ public:
Q_INVOKABLE void getPageSource(const QString &address); Q_INVOKABLE void getPageSource(const QString &address);
Q_INVOKABLE void setMessageReaction(qlonglong chatId, qlonglong messageId, const QString &reaction); Q_INVOKABLE void setMessageReaction(qlonglong chatId, qlonglong messageId, const QString &reaction);
Q_INVOKABLE void setNetworkType(NetworkType networkType); Q_INVOKABLE void setNetworkType(NetworkType networkType);
Q_INVOKABLE void setInactiveSessionTtl(int days);
// Others (candidates for extraction ;)) // Others (candidates for extraction ;))
Q_INVOKABLE void searchEmoji(const QString &queryString); Q_INVOKABLE void searchEmoji(const QString &queryString);
@ -277,6 +279,7 @@ signals:
void chatPinnedUpdated(qlonglong chatId, bool isPinned); void chatPinnedUpdated(qlonglong chatId, bool isPinned);
void chatReadInboxUpdated(const QString &chatId, const QString &lastReadInboxMessageId, int unreadCount); void chatReadInboxUpdated(const QString &chatId, const QString &lastReadInboxMessageId, int unreadCount);
void chatReadOutboxUpdated(const QString &chatId, const QString &lastReadOutboxMessageId); void chatReadOutboxUpdated(const QString &chatId, const QString &lastReadOutboxMessageId);
void chatAvailableReactionsUpdated(const qlonglong &chatId, const QVariantMap &availableReactions);
void userUpdated(const QString &userId, const QVariantMap &userInformation); void userUpdated(const QString &userId, const QVariantMap &userInformation);
void ownUserUpdated(const QVariantMap &userInformation); void ownUserUpdated(const QVariantMap &userInformation);
void basicGroupUpdated(qlonglong groupId); void basicGroupUpdated(qlonglong groupId);
@ -320,6 +323,7 @@ signals:
void chatTitleUpdated(const QString &chatId, const QString &title); void chatTitleUpdated(const QString &chatId, const QString &title);
void chatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId); void chatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId);
void usersReceived(const QString &extra, const QVariantList &userIds, int totalUsers); void usersReceived(const QString &extra, const QVariantList &userIds, int totalUsers);
void messageSendersReceived(const QString &extra, const QVariantList &senders, int totalUsers);
void errorReceived(int code, const QString &message, const QString &extra); void errorReceived(int code, const QString &message, const QString &extra);
void contactsImported(const QVariantList &importerCount, const QVariantList &userIds); void contactsImported(const QVariantList &importerCount, const QVariantList &userIds);
void messageNotFound(qlonglong chatId, qlonglong messageId); void messageNotFound(qlonglong chatId, qlonglong messageId);
@ -330,7 +334,7 @@ signals:
void userPrivacySettingUpdated(UserPrivacySetting setting, UserPrivacySettingRule rule); void userPrivacySettingUpdated(UserPrivacySetting setting, UserPrivacySettingRule rule);
void messageInteractionInfoUpdated(qlonglong chatId, qlonglong messageId, const QVariantMap &updatedInfo); void messageInteractionInfoUpdated(qlonglong chatId, qlonglong messageId, const QVariantMap &updatedInfo);
void okReceived(const QString &request); void okReceived(const QString &request);
void sessionsReceived(const QVariantList &sessions); void sessionsReceived(int inactive_session_ttl_days, const QVariantList &sessions);
void openFileExternally(const QString &filePath); void openFileExternally(const QString &filePath);
void availableReactionsReceived(qlonglong messageId, const QStringList &reactions); void availableReactionsReceived(qlonglong messageId, const QStringList &reactions);
void chatUnreadMentionCountUpdated(qlonglong chatId, int unreadMentionCount); void chatUnreadMentionCountUpdated(qlonglong chatId, int unreadMentionCount);
@ -349,6 +353,7 @@ public slots:
void handleChatReceived(const QVariantMap &chatInformation); void handleChatReceived(const QVariantMap &chatInformation);
void handleUnreadMessageCountUpdated(const QVariantMap &messageCountInformation); void handleUnreadMessageCountUpdated(const QVariantMap &messageCountInformation);
void handleUnreadChatCountUpdated(const QVariantMap &chatCountInformation); void handleUnreadChatCountUpdated(const QVariantMap &chatCountInformation);
void handleAvailableReactionsUpdated(qlonglong chatId, const QVariantMap &availableReactions);
void handleBasicGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation); void handleBasicGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation);
void handleSuperGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation); void handleSuperGroupUpdated(qlonglong groupId, const QVariantMap &groupInformation);
void handleStickerSets(const QVariantList &stickerSets); void handleStickerSets(const QVariantList &stickerSets);
@ -364,7 +369,7 @@ public slots:
void handleUpdatedUserPrivacySettingRules(const QVariantMap &updatedRules); void handleUpdatedUserPrivacySettingRules(const QVariantMap &updatedRules);
void handleSponsoredMessage(qlonglong chatId, const QVariantMap &message); void handleSponsoredMessage(qlonglong chatId, const QVariantMap &message);
void handleNetworkConfigurationChanged(const QNetworkConfiguration &config); void handleNetworkConfigurationChanged(const QNetworkConfiguration &config);
void handleActiveEmojiReactionsUpdated(const QStringList& emojis);
void handleGetPageSourceFinished(); void handleGetPageSourceFinished();
private: private:
@ -372,7 +377,9 @@ private:
void setInitialParameters(); void setInitialParameters();
void setEncryptionKey(); void setEncryptionKey();
void setLogVerbosityLevel(); void setLogVerbosityLevel();
QVariantMap &fillTdlibParameters(QVariantMap &parameters);
const Group *updateGroup(qlonglong groupId, const QVariantMap &groupInfo, QHash<qlonglong,Group*> *groups); const Group *updateGroup(qlonglong groupId, const QVariantMap &groupInfo, QHash<qlonglong,Group*> *groups);
QVariantMap newSendMessageRequest(qlonglong chatId, qlonglong replyToMessageId);
void initializeTDLibReceiver(); void initializeTDLibReceiver();
private: private:
@ -383,7 +390,7 @@ private:
MceInterface *mceInterface; MceInterface *mceInterface;
TDLibReceiver *tdLibReceiver; TDLibReceiver *tdLibReceiver;
DBusInterface *dbusInterface; DBusInterface *dbusInterface;
QString version; QString versionString;
TDLibWrapper::AuthorizationState authorizationState; TDLibWrapper::AuthorizationState authorizationState;
QVariantMap authorizationStateData; QVariantMap authorizationStateData;
TDLibWrapper::ConnectionState connectionState; TDLibWrapper::ConnectionState connectionState;
@ -399,7 +406,9 @@ private:
QHash<qlonglong,Group*> basicGroups; QHash<qlonglong,Group*> basicGroups;
QHash<qlonglong,Group*> superGroups; QHash<qlonglong,Group*> superGroups;
EmojiSearchWorker emojiSearchWorker; EmojiSearchWorker emojiSearchWorker;
QStringList activeEmojiReactions;
int versionNumber;
QString activeChatSearchName; QString activeChatSearchName;
bool joinChatRequested; bool joinChatRequested;
bool isLoggingOut; bool isLoggingOut;

View file

@ -1,5 +1,5 @@
// //
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
// //
// Distributed under the Boost Software License, Version 1.0. (See accompanying // Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -23,7 +23,7 @@ namespace td {
* Requests can be sent using the method ClientManager::send from any thread. * Requests can be sent using the method ClientManager::send from any thread.
* New updates and responses to requests can be received using the method ClientManager::receive from any thread after * New updates and responses to requests can be received using the method ClientManager::receive from any thread after
* the first request has been sent to the client instance. ClientManager::receive must not be called simultaneously from * the first request has been sent to the client instance. ClientManager::receive must not be called simultaneously from
* two different threads. Also note that all updates and responses to requests should be applied in the same order as * two different threads. Also, note that all updates and responses to requests should be applied in the same order as
* they were received, to ensure consistency. * they were received, to ensure consistency.
* Some TDLib requests can be executed synchronously from any thread using the method ClientManager::execute. * Some TDLib requests can be executed synchronously from any thread using the method ClientManager::execute.
* *
@ -96,7 +96,7 @@ class ClientManager final {
ClientId client_id; ClientId client_id;
/** /**
* Request identifier, to which the response corresponds, or 0 for incoming updates from TDLib. * Request identifier to which the response corresponds, or 0 for incoming updates from TDLib.
*/ */
RequestId request_id; RequestId request_id;
@ -126,10 +126,10 @@ class ClientManager final {
/** /**
* A type of callback function that will be called when a message is added to the internal TDLib log. * A type of callback function that will be called when a message is added to the internal TDLib log.
* *
* \param verbosity_level Log verbosity level with which the message was added (-1 - 1024). * \param verbosity_level Log verbosity level with which the message was added from -1 up to 1024.
* If 0, then TDLib will crash as soon as the callback returns. * If 0, then TDLib will crash as soon as the callback returns.
* None of the TDLib methods can be called from the callback. * None of the TDLib methods can be called from the callback.
* \param message Null-terminated string with the message added to the log. * \param message Null-terminated UTF-8-encoded string with the message added to the log.
*/ */
using LogMessageCallbackPtr = void (*)(int verbosity_level, const char *message); using LogMessageCallbackPtr = void (*)(int verbosity_level, const char *message);
@ -177,7 +177,7 @@ class ClientManager final {
* The TDLib instance is created for the lifetime of the Client object. * The TDLib instance is created for the lifetime of the Client object.
* Requests to TDLib can be sent using the Client::send method from any thread. * Requests to TDLib can be sent using the Client::send method from any thread.
* New updates and responses to requests can be received using the Client::receive method from any thread, * New updates and responses to requests can be received using the Client::receive method from any thread,
* this function must not be called simultaneously from two different threads. Also note that all updates and * this function must not be called simultaneously from two different threads. Also, note that all updates and
* responses to requests should be applied in the same order as they were received, to ensure consistency. * responses to requests should be applied in the same order as they were received, to ensure consistency.
* Given this information, it's advisable to call this function from a dedicated thread. * Given this information, it's advisable to call this function from a dedicated thread.
* Some service TDLib requests can be executed synchronously from any thread using the Client::execute method. * Some service TDLib requests can be executed synchronously from any thread using the Client::execute method.
@ -199,7 +199,7 @@ class ClientManager final {
* if (response.id == 0) { * if (response.id == 0) {
* // process response.object as an incoming update of type td_api::Update * // process response.object as an incoming update of type td_api::Update
* } else { * } else {
* // process response.object as an answer to a sent request with id response.id * // process response.object as an answer to a sent request with identifier response.id
* } * }
* } * }
* \endcode * \endcode

View file

@ -1,5 +1,5 @@
// //
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
// //
// Distributed under the Boost Software License, Version 1.0. (See accompanying // Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
// //
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
// //
// Distributed under the Boost Software License, Version 1.0. (See accompanying // Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -28,7 +28,7 @@
* Requests can be sent using td_send and the received client identifier. * Requests can be sent using td_send and the received client identifier.
* New updates and responses to requests can be received through td_receive from any thread after the first request * New updates and responses to requests can be received through td_receive from any thread after the first request
* has been sent to the client instance. This function must not be called simultaneously from two different threads. * has been sent to the client instance. This function must not be called simultaneously from two different threads.
* Also note that all updates and responses to requests must be applied in the order they were received for consistency. * Also, note that all updates and responses to requests must be applied in the order they were received for consistency.
* Some TDLib requests can be executed synchronously from any thread using td_execute. * Some TDLib requests can be executed synchronously from any thread using td_execute.
* TDLib client instances are destroyed automatically after they are closed. * TDLib client instances are destroyed automatically after they are closed.
* All TDLib client instances must be closed before application termination to ensure data consistency. * All TDLib client instances must be closed before application termination to ensure data consistency.
@ -88,10 +88,10 @@ TDJSON_EXPORT const char *td_execute(const char *request);
/** /**
* A type of callback function that will be called when a message is added to the internal TDLib log. * A type of callback function that will be called when a message is added to the internal TDLib log.
* *
* \param verbosity_level Log verbosity level with which the message was added (-1 - 1024). * \param verbosity_level Log verbosity level with which the message was added from -1 up to 1024.
* If 0, then TDLib will crash as soon as the callback returns. * If 0, then TDLib will crash as soon as the callback returns.
* None of the TDLib methods can be called from the callback. * None of the TDLib methods can be called from the callback.
* \param message Null-terminated string with the logged message. * \param message Null-terminated UTF-8-encoded string with the message added to the log.
*/ */
typedef void (*td_log_message_callback_ptr)(int verbosity_level, const char *message); typedef void (*td_log_message_callback_ptr)(int verbosity_level, const char *message);
@ -118,7 +118,7 @@ TDJSON_EXPORT void td_set_log_message_callback(int max_verbosity_level, td_log_m
* A TDLib client instance can be created through td_json_client_create. * A TDLib client instance can be created through td_json_client_create.
* Requests then can be sent using td_json_client_send from any thread. * Requests then can be sent using td_json_client_send from any thread.
* New updates and request responses can be received through td_json_client_receive from any thread. This function * New updates and request responses can be received through td_json_client_receive from any thread. This function
* must not be called simultaneously from two different threads. Also note that all updates and request responses * must not be called simultaneously from two different threads. Also, note that all updates and request responses
* must be applied in the order they were received to ensure consistency. * must be applied in the order they were received to ensure consistency.
* Given this information, it's advisable to call this function from a dedicated thread. * Given this information, it's advisable to call this function from a dedicated thread.
* Some service TDLib requests can be executed synchronously from any thread by using td_json_client_execute. * Some service TDLib requests can be executed synchronously from any thread by using td_json_client_execute.

View file

@ -1,5 +1,5 @@
// //
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
// //
// Distributed under the Boost Software License, Version 1.0. (See accompanying // Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View file

@ -1,5 +1,5 @@
// //
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
// //
// Distributed under the Boost Software License, Version 1.0. (See accompanying // Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -97,8 +97,8 @@ class unique_ptr {
using element_type = T; using element_type = T;
unique_ptr() noexcept = default; unique_ptr() noexcept = default;
unique_ptr(const unique_ptr &other) = delete; unique_ptr(const unique_ptr &) = delete;
unique_ptr &operator=(const unique_ptr &other) = delete; unique_ptr &operator=(const unique_ptr &) = delete;
unique_ptr(unique_ptr &&other) noexcept : ptr_(other.release()) { unique_ptr(unique_ptr &&other) noexcept : ptr_(other.release()) {
} }
unique_ptr &operator=(unique_ptr &&other) noexcept { unique_ptr &operator=(unique_ptr &&other) noexcept {
@ -187,11 +187,11 @@ using tl_object_ptr = tl::unique_ptr<Type>;
* A function to create a dynamically allocated TL-object. Can be treated as an analogue of std::make_unique. * A function to create a dynamically allocated TL-object. Can be treated as an analogue of std::make_unique.
* Usage example: * Usage example:
* \code * \code
* auto get_authorization_state_request = td::make_tl_object<td::td_api::getAuthorizationState>(); * auto get_me_request = td::make_tl_object<td::td_api::getMe>();
* auto message_text = td::make_tl_object<td::td_api::formattedText>("Hello, world!!!", * auto message_text = td::make_tl_object<td::td_api::formattedText>("Hello, world!!!",
* td::td_api::array<td::tl_object_ptr<td::td_api::textEntity>>()); * td::td_api::array<td::tl_object_ptr<td::td_api::textEntity>>());
* auto send_message_request = td::make_tl_object<td::td_api::sendMessage>(chat_id, 0, 0, nullptr, nullptr, * auto send_message_request = td::make_tl_object<td::td_api::sendMessage>(chat_id, 0, nullptr, nullptr, nullptr,
* td::make_tl_object<td::td_api::inputMessageText>(std::move(message_text), false, true)); * td::make_tl_object<td::td_api::inputMessageText>(std::move(message_text), nullptr, true));
* \endcode * \endcode
* *
* \tparam Type Type of the TL-object to construct. * \tparam Type Type of the TL-object to construct.

View file

@ -483,6 +483,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation>Gelöschtes Konto</translation> <translation>Gelöschtes Konto</translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation>Drücke zweimal auf eine Nachricht, um eine Reaktion auszuwählen</translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -495,13 +499,6 @@
<translation>Sie haben noch keine Chats.</translation> <translation>Sie haben noch keine Chats.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Konnte Ihre Kontakte nicht mit Telegram synchronisieren.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1582,6 +1579,34 @@
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>Wenn Töne eingeschaltet sind, wird Fernschreiber den aktuellen Sailfish OS-Hinweiston für Chats verwenden, der in den Systemeinstellungen konfiguriert werden kann.</translation> <translation>Wenn Töne eingeschaltet sind, wird Fernschreiber den aktuellen Sailfish OS-Hinweiston für Chats verwenden, der in den Systemeinstellungen konfiguriert werden kann.</translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation>Immer bei Hinweisen die Nachricht ausgeben</translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation>Zusätzlich zur Anzahl der ungelesenen Nachrichten wird immer die neuste Nachricht an Hinweise angefügt.</translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation>Ungelesene Nachrichten hervorheben</translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation>Unterhaltungen mit ungelesenen Nachrichten hervorheben</translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation>Inhalte in Hinweisen verbergen</translation>
</message>
<message>
<source>Go to quoted message</source>
<translation>Zu zitierter Nachricht springen</translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation>Beim Tippen auf eine zitierte Nachricht zu dieser springen anstatt es in einem Overlay anzuzeigen.</translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1679,10 +1704,6 @@
<source>This app</source> <source>This app</source>
<translation>Diese App</translation> <translation>Diese App</translation>
</message> </message>
<message>
<source>IP address: %1, origin: %2</source>
<translation>IP-Adresse: %1, Herkunft: %2</translation>
</message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>Active since: %1, last online: %2</source>
<translation>Aktiv seit: %1, zuletzt online: %2</translation> <translation>Aktiv seit: %1, zuletzt online: %2</translation>
@ -1695,6 +1716,41 @@
<source>Sessions</source> <source>Sessions</source>
<translation>Sitzungen</translation> <translation>Sitzungen</translation>
</message> </message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation>
<numerusform>%1 Tag</numerusform>
<numerusform>%1 Tage</numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation>1 Woche</translation>
</message>
<message>
<source>1 month</source>
<translation>1 Monat</translation>
</message>
<message>
<source>3 months</source>
<translation>3 Monate</translation>
</message>
<message>
<source>6 months</source>
<translation>6 Monate</translation>
</message>
<message>
<source>1 year</source>
<translation>1 Jahr</translation>
</message>
<message>
<source>Session Timeout</source>
<translation>Timeout von Sitzungen</translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation>Inaktive Sitzungen werden nach dieser Zeitdauer abgeschaltet</translation>
</message>
</context> </context>
<context> <context>
<name>SettingsStorage</name> <name>SettingsStorage</name>

View file

@ -483,6 +483,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation>Deleted User</translation> <translation>Deleted User</translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -495,13 +499,6 @@
<translation>You don&apos;t have any chats yet.</translation> <translation>You don&apos;t have any chats yet.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished">Could not synchronize your contacts with Telegram.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1584,6 +1581,34 @@ messages</numerusform>
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</translation> <translation>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation>Always append message preview to notifications</translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Go to quoted message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1689,14 +1714,45 @@ messages</numerusform>
<source>This app</source> <source>This app</source>
<translation>This app</translation> <translation>This app</translation>
</message> </message>
<message>
<source>IP address: %1, origin: %2</source>
<translation>IP address: %1, origin: %2</translation>
</message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>Active since: %1, last online: %2</source>
<translation>Active since: %1, last online: %2</translation> <translation>Active since: %1, last online: %2</translation>
</message> </message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation>
<numerusform>%1 day</numerusform>
<numerusform>%1 days</numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 month</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>3 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>6 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 year</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Session Timeout</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsStorage</name> <name>SettingsStorage</name>

File diff suppressed because it is too large Load diff

View file

@ -483,6 +483,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -495,13 +499,6 @@
<translation>Sinulla ei ole vielä keskusteluja.</translation> <translation>Sinulla ei ole vielä keskusteluja.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished">Yhteystietojasi ei voitu synkronoida Telegramin kanssa.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1583,6 +1580,34 @@
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>Kun äänet ovat käytössä, Fernschreiber käyttää Sailfish OS:n ilmoitusääniä keskusteluille, jotia voit muuttaa järjestelmäasetuksista.</translation> <translation>Kun äänet ovat käytössä, Fernschreiber käyttää Sailfish OS:n ilmoitusääniä keskusteluille, jotia voit muuttaa järjestelmäasetuksista.</translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Go to quoted message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1688,14 +1713,45 @@
<source>This app</source> <source>This app</source>
<translation>Tämä sovellus</translation> <translation>Tämä sovellus</translation>
</message> </message>
<message>
<source>IP address: %1, origin: %2</source>
<translation>IP-osoite: %1, sijainti: %2</translation>
</message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>Active since: %1, last online: %2</source>
<translation>Aktiivinen %1 alkaen, viimeksi paikalla: %2</translation> <translation>Aktiivinen %1 alkaen, viimeksi paikalla: %2</translation>
</message> </message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 month</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>3 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>6 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 year</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Session Timeout</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsStorage</name> <name>SettingsStorage</name>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS> <!DOCTYPE TS>
<TS version="2.1" language="en"> <TS version="2.1" language="fr">
<context> <context>
<name>AboutPage</name> <name>AboutPage</name>
<message> <message>
@ -483,6 +483,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation>Supprimer l&apos;utilisateur</translation> <translation>Supprimer l&apos;utilisateur</translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation>Toucher deux fois sur un message pour réagir</translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -495,13 +499,6 @@
<translation>Vous n&apos;avez aucune conversation.</translation> <translation>Vous n&apos;avez aucune conversation.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Impossible de synchroniser vos contacts avec Telegram.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1166,7 +1163,7 @@
</message> </message>
<message> <message>
<source>No contacts found.</source> <source>No contacts found.</source>
<translation type="unfinished"></translation> <translation>Aucun contact trouvé</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1582,6 +1579,34 @@
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>Lorsque le son est activé, Fernschreiber utilisera le réglage de Sailfish OS. Celui-ci est paramétrable depuis les paramètres du système.</translation> <translation>Lorsque le son est activé, Fernschreiber utilisera le réglage de Sailfish OS. Celui-ci est paramétrable depuis les paramètres du système.</translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation>Toujours visualiser le message dans les notifications</translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation>En plus d&apos;afficher le nombre de messages non-lus, le dernier message sera également ajouté aux notifications.</translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation>Mettre en valeur les messages non-lus</translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation>Mettre en valeur les conversations avec des messages non-lus</translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation>Masquer le contenu dans les notifications</translation>
</message>
<message>
<source>Go to quoted message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1687,14 +1712,45 @@
<source>This app</source> <source>This app</source>
<translation>Cette app</translation> <translation>Cette app</translation>
</message> </message>
<message>
<source>IP address: %1, origin: %2</source>
<translation>Adresse IP : %1, origine : %2</translation>
</message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>Active since: %1, last online: %2</source>
<translation>Actif depuis : %1, en ligne : %2</translation> <translation>Actif depuis : %1, en ligne : %2</translation>
</message> </message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation>
<numerusform>%1 jour(s)</numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation>1 semaine</translation>
</message>
<message>
<source>1 month</source>
<translation>1 mois</translation>
</message>
<message>
<source>3 months</source>
<translation>3 mois</translation>
</message>
<message>
<source>6 months</source>
<translation>6 mois</translation>
</message>
<message>
<source>1 year</source>
<translation>1 année</translation>
</message>
<message>
<source>Session Timeout</source>
<translation>Délai d&apos;inactivité de session</translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation>Sessions inactives seront terminées après ce délai</translation>
</message>
</context> </context>
<context> <context>
<name>SettingsStorage</name> <name>SettingsStorage</name>

View file

@ -473,6 +473,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -485,13 +489,6 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1555,6 +1552,34 @@
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Go to quoted message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1661,11 +1686,41 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>IP address: %1, origin: %2</source> <source>Active since: %1, last online: %2</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>1 month</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>3 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>6 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 year</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Session Timeout</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>

View file

@ -483,6 +483,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -495,13 +499,6 @@
<translation>Nessuna chat presente</translation> <translation>Nessuna chat presente</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished">Sincronizzazione contatti con Telegram non riuscita</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1582,6 +1579,34 @@
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>Quando i suoni di notifica sono attivi, Fernschreiber utilizza l&apos;attuale suono di notifica per i messaggi scelto per Sailfish OS, il quale può essere modificato dalle impostazioni di sistema.</translation> <translation>Quando i suoni di notifica sono attivi, Fernschreiber utilizza l&apos;attuale suono di notifica per i messaggi scelto per Sailfish OS, il quale può essere modificato dalle impostazioni di sistema.</translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Go to quoted message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1688,11 +1713,42 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>IP address: %1, origin: %2</source> <source>Active since: %1, last online: %2</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>1 month</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>3 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>6 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 year</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Session Timeout</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>

View file

@ -493,6 +493,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation>Usunięty użytkownik</translation> <translation>Usunięty użytkownik</translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -505,13 +509,6 @@
<translation>Nie masz jeszcze żadnych czatów.</translation> <translation>Nie masz jeszcze żadnych czatów.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Nie można zsynchonizaować kontaktów z Telegramem.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1609,6 +1606,34 @@
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>Gdy dźwięki włączone, Fernschreiber użyje bieżącego dźwięku powiadomienia Sailfish OS do czatów, które można skonfigurować w ustawieniach systemu.</translation> <translation>Gdy dźwięki włączone, Fernschreiber użyje bieżącego dźwięku powiadomienia Sailfish OS do czatów, które można skonfigurować w ustawieniach systemu.</translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Go to quoted message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1714,14 +1739,46 @@
<source>This app</source> <source>This app</source>
<translation>Ta aplikacja</translation> <translation>Ta aplikacja</translation>
</message> </message>
<message>
<source>IP address: %1, origin: %2</source>
<translation>Adres IP: %1, oryginalny: %2</translation>
</message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>Active since: %1, last online: %2</source>
<translation>Aktywny od: %1, ostatnio aktywny: %2</translation> <translation>Aktywny od: %1, ostatnio aktywny: %2</translation>
</message> </message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 month</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>3 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>6 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 year</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Session Timeout</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsStorage</name> <name>SettingsStorage</name>

View file

@ -348,11 +348,11 @@
</message> </message>
<message> <message>
<source>Leave Chat</source> <source>Leave Chat</source>
<translation>Выйти из Чата</translation> <translation>Выйти из чата</translation>
</message> </message>
<message> <message>
<source>Join Chat</source> <source>Join Chat</source>
<translation>Зайти в Чат</translation> <translation>Зайти в чат</translation>
</message> </message>
<message> <message>
<source>Leaving chat</source> <source>Leaving chat</source>
@ -427,7 +427,7 @@
</message> </message>
<message> <message>
<source>Search in Chat</source> <source>Search in Chat</source>
<translation>Найти в Чате</translation> <translation>Поиск в чате</translation>
</message> </message>
<message> <message>
<source>Search in chat...</source> <source>Search in chat...</source>
@ -493,6 +493,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation>Удалённый пользователь</translation> <translation>Удалённый пользователь</translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -505,13 +509,6 @@
<translation>Тут пока ничего нет</translation> <translation>Тут пока ничего нет</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Невозможно синхронизировать ваши контакты с Телеграм.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1159,7 +1156,7 @@
</message> </message>
<message> <message>
<source>Secret Chat</source> <source>Secret Chat</source>
<translation>Секретный Чат</translation> <translation>Секретный чат</translation>
</message> </message>
<message> <message>
<source>End-to-end-encrypted, accessible on this device only</source> <source>End-to-end-encrypted, accessible on this device only</source>
@ -1241,7 +1238,7 @@
</message> </message>
<message> <message>
<source>New Chat</source> <source>New Chat</source>
<translation>Новый Чат</translation> <translation>Новый чат</translation>
</message> </message>
<message> <message>
<source>Filter your chats...</source> <source>Filter your chats...</source>
@ -1249,7 +1246,7 @@
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation>Найти Чаты</translation> <translation>Поиск чатов</translation>
</message> </message>
<message> <message>
<source>Download of %1 successful.</source> <source>Download of %1 successful.</source>
@ -1460,7 +1457,7 @@
<name>SearchChatsPage</name> <name>SearchChatsPage</name>
<message> <message>
<source>No chats found.</source> <source>No chats found.</source>
<translation>Чаты не найдены</translation> <translation>Ничего не найдено</translation>
</message> </message>
<message> <message>
<source>Searching chats...</source> <source>Searching chats...</source>
@ -1468,7 +1465,7 @@
</message> </message>
<message> <message>
<source>Private Chat</source> <source>Private Chat</source>
<translation>Приватный Чат</translation> <translation>Приватный чат</translation>
</message> </message>
<message> <message>
<source>Group</source> <source>Group</source>
@ -1496,7 +1493,7 @@
</message> </message>
<message> <message>
<source>Search Chats</source> <source>Search Chats</source>
<translation>Найти Чаты</translation> <translation>Поиск чатов</translation>
</message> </message>
<message> <message>
<source>Search a chat...</source> <source>Search a chat...</source>
@ -1612,6 +1609,34 @@
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>Если звуки разрешены, Fernschreiber использует звук, выбранный для чатов в настройках Sailfish OS.</translation> <translation>Если звуки разрешены, Fernschreiber использует звук, выбранный для чатов в настройках Sailfish OS.</translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation>Всегда показывать последнее сообщение на экране событий</translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation>Включать в текст на экране событий не только количество непрочитанных сообщений, но и содержимое последнего сообщения.</translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation>Выделять непрочитанные сообщения</translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation>Помечать чаты и каналы с непрочитанными сообщениями другим шрифтом и цветом.</translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation>Не показывать содержимое сообщений в уведомлениях</translation>
</message>
<message>
<source>Go to quoted message</source>
<translation>Переходить к цитируемому сообщению</translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation>По нажатию на цитируемое сообщение, переходить к нему в чате вместо отображения во всплывающем окне.</translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1717,14 +1742,46 @@
<source>This app</source> <source>This app</source>
<translation>Это приложение</translation> <translation>Это приложение</translation>
</message> </message>
<message>
<source>IP address: %1, origin: %2</source>
<translation>IP-адрес: %1, регион: %2</translation>
</message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>Active since: %1, last online: %2</source>
<translation>Активен с: %1, был онлайн: %2</translation> <translation>Активен с: %1, был онлайн: %2</translation>
</message> </message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation>
<numerusform>%1 день</numerusform>
<numerusform>%1 дня</numerusform>
<numerusform>%1 дней</numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation>1 неделя</translation>
</message>
<message>
<source>1 month</source>
<translation>1 месяц</translation>
</message>
<message>
<source>3 months</source>
<translation>3 месяца</translation>
</message>
<message>
<source>6 months</source>
<translation>6 месяцев</translation>
</message>
<message>
<source>1 year</source>
<translation>1 год</translation>
</message>
<message>
<source>Session Timeout</source>
<translation>Таймаут неактивности</translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation>Неактивные сеансы будут автоматически завершены через указанное время.</translation>
</message>
</context> </context>
<context> <context>
<name>SettingsStorage</name> <name>SettingsStorage</name>

View file

@ -493,6 +493,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation>Odstránený používateľ</translation> <translation>Odstránený používateľ</translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -505,13 +509,6 @@
<translation>Nemáte žiadne čety.</translation> <translation>Nemáte žiadne čety.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Nemožno synchonizovať kontakty s Telegramom.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1609,6 +1606,34 @@
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>Keď povolené zvukové upozornenia, Fernschreiber použije aktuálne zvukové upozornenia Sailfish OS pre čety, ktoré môžu byť upravené v nastaveniach systému.</translation> <translation>Keď povolené zvukové upozornenia, Fernschreiber použije aktuálne zvukové upozornenia Sailfish OS pre čety, ktoré môžu byť upravené v nastaveniach systému.</translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Go to quoted message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1714,14 +1739,46 @@
<source>This app</source> <source>This app</source>
<translation>Táto aplikácia</translation> <translation>Táto aplikácia</translation>
</message> </message>
<message>
<source>IP address: %1, origin: %2</source>
<translation>IP adresa: %1, pôvod: %2</translation>
</message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>Active since: %1, last online: %2</source>
<translation>Aktívna od: %1, naposledy pripojená: %2</translation> <translation>Aktívna od: %1, naposledy pripojená: %2</translation>
</message> </message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 month</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>3 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>6 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 year</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Session Timeout</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsStorage</name> <name>SettingsStorage</name>

View file

@ -483,6 +483,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation>Tog bort användare</translation> <translation>Tog bort användare</translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -495,13 +499,6 @@
<translation>Du har inga chattar än.</translation> <translation>Du har inga chattar än.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Kunde inte synkronisera dina kontakter med Telegram.</translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1582,6 +1579,34 @@
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>När ljud är aktiverat, använder Fernschreiber aktuell Sailfish-signal för chatt-avisering, vilken kan ställas in i systemets ljudinställningar.</translation> <translation>När ljud är aktiverat, använder Fernschreiber aktuell Sailfish-signal för chatt-avisering, vilken kan ställas in i systemets ljudinställningar.</translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Go to quoted message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1687,14 +1712,45 @@
<source>This app</source> <source>This app</source>
<translation>Denna app</translation> <translation>Denna app</translation>
</message> </message>
<message>
<source>IP address: %1, origin: %2</source>
<translation>IP-adress: %1, ursprung: %2</translation>
</message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>Active since: %1, last online: %2</source>
<translation>Aktiv sedan: %1, senast online: %2</translation> <translation>Aktiv sedan: %1, senast online: %2</translation>
</message> </message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 month</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>3 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>6 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 year</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Session Timeout</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsStorage</name> <name>SettingsStorage</name>

View file

@ -473,6 +473,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -485,13 +489,6 @@
<translation></translation> <translation></translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"> Telegram </translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1556,6 +1553,34 @@
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation>Fernschreiber </translation> <translation>Fernschreiber </translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Go to quoted message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1661,14 +1686,44 @@
<source>This app</source> <source>This app</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>IP address: %1, origin: %2</source>
<translation>IP : %1, : %2</translation>
</message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>Active since: %1, last online: %2</source>
<translation>: %1, 线: %2</translation> <translation>: %1, 线: %2</translation>
</message> </message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 month</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>3 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>6 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 year</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Session Timeout</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsStorage</name> <name>SettingsStorage</name>

View file

@ -483,6 +483,10 @@
<source>Deleted User</source> <source>Deleted User</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Double-tap on a message to choose a reaction</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatSelectionPage</name> <name>ChatSelectionPage</name>
@ -495,13 +499,6 @@
<translation>You don&apos;t have any chats yet.</translation> <translation>You don&apos;t have any chats yet.</translation>
</message> </message>
</context> </context>
<context>
<name>ContactSync</name>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>
<message> <message>
@ -1582,6 +1579,34 @@
<source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source> <source>When sounds are enabled, Fernschreiber will use the current Sailfish OS notification sound for chats, which can be configured in the system settings.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Always append message preview to notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>In addition to showing the number of unread messages, the latest message will also be appended to notifications.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Highlight Conversations with unread messages</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide content in notifications</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Go to quoted message</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>When tapping a quoted message, open it in chat instead of showing it in an overlay.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>
@ -1688,11 +1713,42 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>IP address: %1, origin: %2</source> <source>Active since: %1, last online: %2</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<source>%1 day(s)</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>1 week</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Active since: %1, last online: %2</source> <source>1 month</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>3 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>6 months</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>1 year</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Session Timeout</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Inactive sessions will be terminated after this timeframe</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>