diff --git a/harbour-fernschreiber.pro b/harbour-fernschreiber.pro
index dc12009..f751a28 100644
--- a/harbour-fernschreiber.pro
+++ b/harbour-fernschreiber.pro
@@ -84,6 +84,7 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/pages/AboutPage.qml \
qml/pages/PollCreationPage.qml \
qml/pages/PollResultsPage.qml \
+ qml/pages/SearchChatsPage.qml \
qml/pages/SettingsPage.qml \
qml/pages/VideoPage.qml \
rpm/harbour-fernschreiber.changes \
diff --git a/qml/components/AudioPreview.qml b/qml/components/AudioPreview.qml
index c58d69c..e37c8ac 100644
--- a/qml/components/AudioPreview.qml
+++ b/qml/components/AudioPreview.qml
@@ -64,9 +64,9 @@ Item {
audioType = ( audioData['@type'] === "voiceNote" ) ? "voice" : "audio";
audioFileId = audioData[audioType].id;
if (typeof audioData.album_cover_thumbnail !== "undefined") {
- previewFileId = audioData.album_cover_thumbnail.photo.id;
- if (audioData.album_cover_thumbnail.photo.local.is_downloading_completed) {
- placeholderImage.source = audioData.album_cover_thumbnail.photo.local.path;
+ previewFileId = audioData.album_cover_thumbnail.file.id;
+ if (audioData.album_cover_thumbnail.file.local.is_downloading_completed) {
+ placeholderImage.source = audioData.album_cover_thumbnail.file.local.path;
} else {
tdLibWrapper.downloadFile(previewFileId);
}
@@ -94,7 +94,7 @@ Item {
if (typeof audioData === "object") {
if (fileInformation.local.is_downloading_completed) {
if (fileId === previewFileId) {
- audioData.thumbnail.photo = fileInformation;
+ audioData.album_cover_thumbnail.file = fileInformation;
placeholderImage.source = fileInformation.local.path;
}
if (fileId === audioFileId) {
diff --git a/qml/pages/ChatPage.qml b/qml/pages/ChatPage.qml
index a02668b..06b5348 100644
--- a/qml/pages/ChatPage.qml
+++ b/qml/pages/ChatPage.qml
@@ -332,6 +332,14 @@ Page {
return false;
}
+ function resetFocus() {
+ if (searchInChatField.text === "") {
+ chatOverviewItem.visible = true;
+ }
+ searchInChatField.focus = false;
+ chatPage.focus = true;
+ }
+
Timer {
id: forwardMessagesTimer
interval: 200
@@ -348,6 +356,17 @@ Page {
}
}
+ Timer {
+ id: searchInChatTimer
+ interval: 300
+ running: false
+ repeat: false
+ onTriggered: {
+ Debug.log("Searching for '" + searchInChatField.text + "'");
+ chatModel.setSearchQuery(searchInChatField.text);
+ }
+ }
+
Component.onCompleted: {
initializePage();
}
@@ -461,7 +480,7 @@ Page {
chatView.lastReadSentIndex = lastReadSentIndex;
chatView.scrollToIndex(modelIndex);
chatPage.loading = false;
- if (modelIndex >= (chatView.count - 10)) {
+ if (chatOverviewItem.visible && modelIndex >= (chatView.count - 10)) {
chatView.inCooldown = true;
chatModel.triggerLoadMoreFuture();
}
@@ -470,6 +489,8 @@ Page {
Debug.log("[ChatPage] Chat content quite small...");
viewMessageTimer.queueViewMessage(chatView.count - 1);
}
+
+ chatViewCooldownTimer.restart();
}
onNewMessageReceived: {
if (chatView.manuallyScrolledToBottom || message.sender.user_id === chatPage.myUserId) {
@@ -480,7 +501,7 @@ Page {
onUnreadCountUpdated: {
Debug.log("[ChatPage] Unread count updated, new count: ", unreadCount);
chatInformation.unread_count = unreadCount;
- chatUnreadMessagesCountBackground.visible = ( !chatPage.loading && unreadCount > 0 );
+ chatUnreadMessagesItem.visible = ( !chatPage.loading && chatInformation.unread_count > 0 && chatOverviewItem.visible );
chatUnreadMessagesCount.text = unreadCount > 99 ? "99+" : unreadCount;
}
onLastReadSentMessageUpdated: {
@@ -490,7 +511,7 @@ Page {
onMessagesIncrementalUpdate: {
Debug.log("Incremental update received. View now has ", chatView.count, " messages, view is on index ", modelIndex, ", own messages were read before index ", lastReadSentIndex);
chatView.lastReadSentIndex = lastReadSentIndex;
- chatViewCooldownTimer.start();
+ chatViewCooldownTimer.restart();
}
onNotificationSettingsUpdated: {
chatInformation = chatModel.getChatInformation();
@@ -630,6 +651,17 @@ Page {
}
text: chatInformation.notification_settings.mute_for > 0 ? qsTr("Unmute Chat") : qsTr("Mute Chat")
}
+
+ MenuItem {
+ id: searchInChatMenuItem
+ visible: !chatPage.isSecretChat && chatOverviewItem.visible
+ onClicked: {
+ // This automatically shows the search field as well
+ chatOverviewItem.visible = false;
+ searchInChatField.focus = true;
+ }
+ text: qsTr("Search in Chat")
+ }
}
BackgroundItem {
@@ -703,6 +735,8 @@ Page {
Item {
id: chatOverviewItem
+ opacity: visible ? 1 : 0
+ Behavior on opacity { FadeAnimation {} }
width: parent.width - chatPictureThumbnail.width - Theme.paddingMedium
height: chatNameText.height + chatStatusText.height
anchors.bottom: parent.bottom
@@ -735,6 +769,39 @@ Page {
maximumLineCount: 1
}
}
+
+ Item {
+ id: searchInChatItem
+ visible: !chatOverviewItem.visible
+ opacity: visible ? 1 : 0
+ Behavior on opacity { FadeAnimation {} }
+ width: parent.width - chatPictureThumbnail.width - Theme.paddingMedium
+ height: searchInChatField.height
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: chatPage.isPortrait ? Theme.paddingSmall : 0
+
+ SearchField {
+ id: searchInChatField
+ visible: false
+ width: visible ? parent.width : 0
+ placeholderText: qsTr("Search in chat...")
+ active: searchInChatItem.visible
+ canHide: text === ""
+
+ onTextChanged: {
+ searchInChatTimer.restart();
+ }
+
+ onHideClicked: {
+ resetFocus();
+ }
+
+ EnterKey.iconSource: "image://theme/icon-m-enter-close"
+ EnterKey.onClicked: {
+ resetFocus();
+ }
+ }
+ }
}
PinnedMessageItem {
@@ -823,7 +890,7 @@ Page {
function handleScrollPositionChanged() {
Debug.log("Current position: ", chatView.contentY);
- if (chatInformation.unread_count > 0) {
+ if (chatOverviewItem.visible && chatInformation.unread_count > 0) {
var bottomIndex = chatView.indexAt(chatView.contentX, ( chatView.contentY + chatView.height - Theme.horizontalPageMargin ));
if (bottomIndex > -1) {
viewMessageTimer.queueViewMessage(bottomIndex)
@@ -850,7 +917,7 @@ Page {
Debug.log("[ChatPage] Trying to get older history items...");
chatView.inCooldown = true;
chatModel.triggerLoadMoreHistory();
- } else if (chatView.indexAt(chatView.contentX, chatView.contentY) > ( count - 10)) {
+ } else if (chatOverviewItem.visible && chatView.indexAt(chatView.contentX, chatView.contentY) > ( count - 10)) {
Debug.log("[ChatPage] Trying to get newer history items...");
chatView.inCooldown = true;
chatModel.triggerLoadMoreFuture();
@@ -986,12 +1053,13 @@ Page {
anchors.rightMargin: Theme.paddingMedium
anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.paddingMedium
+ visible: !chatPage.loading && chatInformation.unread_count > 0 && chatOverviewItem.visible
Rectangle {
id: chatUnreadMessagesCountBackground
color: Theme.highlightBackgroundColor
anchors.fill: parent
radius: width / 2
- visible: !chatPage.loading && chatInformation.unread_count > 0
+ visible: chatUnreadMessagesItem.visible
}
Text {
@@ -1000,7 +1068,7 @@ Page {
font.bold: true
color: Theme.primaryColor
anchors.centerIn: chatUnreadMessagesCountBackground
- visible: chatUnreadMessagesCountBackground.visible
+ visible: chatUnreadMessagesItem.visible
text: chatInformation.unread_count > 99 ? "99+" : chatInformation.unread_count
}
MouseArea {
diff --git a/qml/pages/OverviewPage.qml b/qml/pages/OverviewPage.qml
index 241fd35..438b8e8 100644
--- a/qml/pages/OverviewPage.qml
+++ b/qml/pages/OverviewPage.qml
@@ -86,6 +86,16 @@ Page {
}
}
+ Timer {
+ id: searchChatTimer
+ interval: 300
+ running: false
+ repeat: false
+ onTriggered: {
+ chatListProxyModel.setFilterWildcard("*" + chatSearchField.text + "*");
+ }
+ }
+
function setPageStatus() {
switch (overviewPage.connectionState) {
case TelegramAPI.WaitingForNetwork:
@@ -145,6 +155,16 @@ Page {
}
}
+ function resetFocus() {
+ if (chatSearchField.text === "") {
+ chatSearchField.visible = false;
+ pageHeader.visible = true;
+ searchChatButton.visible = overviewPage.connectionState === TelegramAPI.ConnectionReady;
+ }
+ chatSearchField.focus = false;
+ overviewPage.focus = true;
+ }
+
Connections {
target: tdLibWrapper
onAuthorizationStateChanged: {
@@ -217,16 +237,19 @@ Page {
text: qsTr("Settings")
onClicked: pageStack.push(Qt.resolvedUrl("../pages/SettingsPage.qml"))
}
+ MenuItem {
+ text: qsTr("Search Chats")
+ onClicked: pageStack.push(Qt.resolvedUrl("../pages/SearchChatsPage.qml"))
+ }
MenuItem {
text: qsTr("New Chat")
onClicked: pageStack.push(Qt.resolvedUrl("../pages/NewChatPage.qml"))
}
}
- PageHeader {
- id: pageHeader
- title: qsTr("Fernschreiber")
- leftMargin: Theme.itemSizeMedium
+ Row {
+ id: headerRow
+ width: parent.width - Theme.horizontalPageMargin
GlassItem {
id: pageStatus
@@ -237,12 +260,64 @@ Page {
radius: 0.2
cache: false
}
+
+ PageHeader {
+ id: pageHeader
+ title: qsTr("Fernschreiber")
+ width: visible ? ( parent.width - pageStatus.width - searchChatButton.width ) : 0
+ opacity: visible ? 1 : 0
+ Behavior on opacity { FadeAnimation {} }
+ }
+
+ IconButton {
+ id: searchChatButton
+ width: visible ? height : 0
+ opacity: visible ? 1 : 0
+ Behavior on opacity { NumberAnimation {} }
+ anchors.verticalCenter: parent.verticalCenter
+ icon {
+ source: "image://theme/icon-m-search?" + Theme.highlightColor
+ asynchronous: true
+ }
+ visible: overviewPage.connectionState === TelegramAPI.ConnectionReady
+ onClicked: {
+ chatSearchField.focus = true;
+ chatSearchField.visible = true;
+ pageHeader.visible = false;
+ searchChatButton.visible = false;
+ }
+ }
+
+ SearchField {
+ id: chatSearchField
+ visible: false
+ opacity: visible ? 1 : 0
+ Behavior on opacity { FadeAnimation {} }
+ width: visible ? ( parent.width - pageStatus.width ) : 0
+ height: pageHeader.height
+ placeholderText: qsTr("Filter your chats...")
+ canHide: text === ""
+
+ onTextChanged: {
+ searchChatTimer.restart();
+ }
+
+ onHideClicked: {
+ resetFocus();
+ }
+
+ EnterKey.iconSource: "image://theme/icon-m-enter-close"
+ EnterKey.onClicked: {
+ resetFocus();
+ }
+ }
+
}
SilicaListView {
id: chatListView
anchors {
- top: pageHeader.bottom
+ top: headerRow.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
@@ -250,7 +325,7 @@ Page {
clip: true
opacity: overviewPage.chatListCreated ? 1 : 0
Behavior on opacity { FadeAnimation {} }
- model: chatListModel
+ model: chatSearchField.text !== "" ? chatListProxyModel : chatListModel
delegate: ChatListViewItem {
ownUserId: overviewPage.ownUserId
isVerified: is_verified
diff --git a/qml/pages/SearchChatsPage.qml b/qml/pages/SearchChatsPage.qml
new file mode 100644
index 0000000..82fe385
--- /dev/null
+++ b/qml/pages/SearchChatsPage.qml
@@ -0,0 +1,262 @@
+/*
+ Copyright (C) 2020 Sebastian J. Wolf and other contributors
+
+ This file is part of Fernschreiber.
+
+ Fernschreiber is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Fernschreiber is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fernschreiber. If not, see .
+*/
+import QtQuick 2.6
+import Sailfish.Silica 1.0
+import WerkWolf.Fernschreiber 1.0
+import "../components"
+import "../js/debug.js" as Debug
+import "../js/twemoji.js" as Emoji
+import "../js/functions.js" as Functions
+
+Page {
+ id: searchChatsPage
+ allowedOrientations: Orientation.All
+
+ function resetFocus() {
+ publicChatsSearchField.focus = false;
+ searchChatsPage.focus = true;
+ }
+
+ Timer {
+ id: searchPublicChatsTimer
+ interval: 800
+ running: false
+ repeat: false
+ onTriggered: {
+ Debug.log("Searching for '" + publicChatsSearchField.text + "'");
+ tdLibWrapper.searchPublicChats(publicChatsSearchField.text);
+ searchChatsPage.isLoading = true;
+ }
+ }
+
+ Connections {
+ target: tdLibWrapper
+ onChatsReceived: {
+ searchChatsPage.isLoading = false;
+ Debug.log(JSON.stringify(chats));
+ chatsFound = chats;
+ }
+ onErrorReceived: {
+ searchChatsPage.isLoading = false;
+ Functions.handleErrorMessage(code, message);
+ }
+ }
+
+ property bool isLoading: false;
+ property var chatsFound;
+ readonly property var ownUserId: tdLibWrapper.getUserInformation().id;
+
+ SilicaFlickable {
+ id: searchChatsContainer
+ contentHeight: searchChatsPage.height
+ anchors.fill: parent
+
+ Column {
+ id: searchChatsPageColumn
+ width: searchChatsPage.width
+ height: searchChatsPage.height
+
+ PageHeader {
+ id: searchChatsPageHeader
+ title: qsTr("Search Chats")
+ }
+
+ Item {
+ id: publicChatsItem
+
+ width: searchChatsPageColumn.width
+ height: searchChatsPageColumn.height - searchChatsPageHeader.height
+
+ Column {
+
+ width: parent.width
+ height: parent.height
+
+ SearchField {
+ id: publicChatsSearchField
+ width: parent.width
+ placeholderText: qsTr("Search a chat...")
+ focus: true
+
+ onTextChanged: {
+ searchPublicChatsTimer.restart();
+ }
+
+ EnterKey.iconSource: "image://theme/icon-m-enter-close"
+ EnterKey.onClicked: {
+ resetFocus();
+ }
+
+ }
+
+ SilicaListView {
+ id: searchChatsListView
+ clip: true
+ width: parent.width
+ height: parent.height - publicChatsSearchField.height
+ visible: !searchChatsPage.isLoading
+ opacity: visible ? 1 : 0
+ Behavior on opacity { FadeAnimation {} }
+ model: searchChatsPage.chatsFound.chat_ids
+
+ ViewPlaceholder {
+ y: Theme.paddingLarge
+ enabled: searchChatsListView.count === 0
+ text: publicChatsSearchField.text.length < 5 ? qsTr("Enter your query to start searching (at least 5 characters needed)") : qsTr("No chats found.")
+ }
+
+ delegate: Item {
+ id: foundChatListDelegate
+ width: parent.width
+ height: foundChatListItem.height
+
+ property var foundChatInformation: tdLibWrapper.getChat(modelData);
+ property var relatedInformation;
+ property bool isPrivateChat: false;
+ property bool isBasicGroup: false;
+ property bool isSupergroup: false;
+
+ Component.onCompleted: {
+ switch (foundChatInformation.type["@type"]) {
+ case "chatTypePrivate":
+ relatedInformation = tdLibWrapper.getUserInformation(foundChatInformation.type.user_id);
+ foundChatListItem.prologSecondaryText.text = qsTr("Private Chat");
+ foundChatListItem.secondaryText.text = "@" + ( relatedInformation.username !== "" ? relatedInformation.username : relatedInformation.user_id );
+ tdLibWrapper.getUserFullInfo(foundChatInformation.type.user_id);
+ isPrivateChat = true;
+ break;
+ case "chatTypeBasicGroup":
+ relatedInformation = tdLibWrapper.getBasicGroup(foundChatInformation.type.basic_group_id);
+ foundChatListItem.prologSecondaryText.text = qsTr("Group");
+ tdLibWrapper.getGroupFullInfo(foundChatInformation.type.basic_group_id, false);
+ isBasicGroup = true;
+ break;
+ case "chatTypeSupergroup":
+ relatedInformation = tdLibWrapper.getSuperGroup(foundChatInformation.type.supergroup_id);
+ if (relatedInformation.is_channel) {
+ foundChatListItem.prologSecondaryText.text = qsTr("Channel");
+ } else {
+ foundChatListItem.prologSecondaryText.text = qsTr("Group");
+ }
+ tdLibWrapper.getGroupFullInfo(foundChatInformation.type.supergroup_id, true);
+ isSupergroup = true;
+ break;
+ }
+ }
+
+ Connections {
+ target: tdLibWrapper
+ onUserFullInfoUpdated: {
+ if (foundChatListDelegate.isPrivateChat && userId.toString() === foundChatListDelegate.foundChatInformation.type.user_id.toString()) {
+ foundChatListItem.tertiaryText.text = Emoji.emojify(userFullInfo.bio, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/");
+ }
+ }
+ onUserFullInfoReceived: {
+ if (foundChatListDelegate.isPrivateChat && userFullInfo["@extra"].toString() === foundChatListDelegate.foundChatInformation.type.user_id.toString()) {
+ foundChatListItem.tertiaryText.text = Emoji.emojify(userFullInfo.bio, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/");
+ }
+ }
+
+ onBasicGroupFullInfoUpdated: {
+ if (foundChatListDelegate.isBasicGroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.basic_group_id.toString()) {
+ foundChatListItem.secondaryText.text = qsTr("%1 members").arg(Number(groupFullInfo.members.length).toLocaleString(Qt.locale(), "f", 0));
+ foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/");
+ }
+ }
+ onBasicGroupFullInfoReceived: {
+ if (foundChatListDelegate.isBasicGroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.basic_group_id.toString()) {
+ foundChatListItem.secondaryText.text = qsTr("%1 members").arg(Number(groupFullInfo.members.length).toLocaleString(Qt.locale(), "f", 0));
+ foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/");
+ }
+ }
+
+ onSupergroupFullInfoUpdated: {
+ if (foundChatListDelegate.isSupergroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.supergroup_id.toString()) {
+ if (foundChatListDelegate.relatedInformation.is_channel) {
+ foundChatListItem.secondaryText.text = qsTr("%1 subscribers").arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0));
+ } else {
+ foundChatListItem.secondaryText.text = qsTr("%1 members").arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0));
+ }
+ foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/");
+ }
+ }
+ onSupergroupFullInfoReceived: {
+ if (foundChatListDelegate.isSupergroup && groupId.toString() === foundChatListDelegate.foundChatInformation.type.supergroup_id.toString()) {
+ if (foundChatListDelegate.relatedInformation.is_channel) {
+ foundChatListItem.secondaryText.text = qsTr("%1 subscribers").arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0));
+ } else {
+ foundChatListItem.secondaryText.text = qsTr("%1 members").arg(Number(groupFullInfo.member_count).toLocaleString(Qt.locale(), "f", 0));
+ }
+ foundChatListItem.tertiaryText.text = Emoji.emojify(groupFullInfo.description, foundChatListItem.tertiaryText.font.pixelSize, "../js/emoji/");
+ }
+ }
+ }
+
+ PhotoTextsListItem {
+ id: foundChatListItem
+
+ pictureThumbnail {
+ photoData: typeof foundChatInformation.photo.small !== "undefined" ? foundChatInformation.photo.small : {}
+ }
+ width: parent.width
+
+ primaryText.text: Emoji.emojify(foundChatInformation.title, primaryText.font.pixelSize, "../js/emoji/")
+ tertiaryText.maximumLineCount: 1
+
+ onClicked: {
+ pageStack.push(Qt.resolvedUrl("../pages/ChatPage.qml"), { "chatInformation" : foundChatInformation });
+ }
+ }
+ }
+
+ VerticalScrollDecorator {}
+ }
+
+ }
+
+ Column {
+
+ opacity: visible ? 1 : 0
+ Behavior on opacity { FadeAnimation {} }
+ visible: searchChatsPage.isLoading
+ width: parent.width
+ height: loadingLabel.height + loadingBusyIndicator.height + Theme.paddingMedium
+
+ spacing: Theme.paddingMedium
+
+ anchors.verticalCenter: parent.verticalCenter
+
+ InfoLabel {
+ id: loadingLabel
+ text: qsTr("Searching chats...")
+ }
+
+ BusyIndicator {
+ id: loadingBusyIndicator
+ anchors.horizontalCenter: parent.horizontalCenter
+ running: searchChatsPage.isLoading
+ size: BusyIndicatorSize.Large
+ }
+ }
+
+ }
+
+ }
+ }
+}
diff --git a/src/chatlistmodel.cpp b/src/chatlistmodel.cpp
index d51fff7..4de375b 100644
--- a/src/chatlistmodel.cpp
+++ b/src/chatlistmodel.cpp
@@ -54,23 +54,6 @@ namespace {
class ChatListModel::ChatData
{
public:
- enum Role {
- RoleDisplay = Qt::DisplayRole,
- RoleChatId,
- RoleChatType,
- RoleTitle,
- RolePhotoSmall,
- RoleUnreadCount,
- RoleLastReadInboxMessageId,
- RoleLastMessageSenderId,
- RoleLastMessageDate,
- RoleLastMessageText,
- RoleLastMessageStatus,
- RoleChatMemberStatus,
- RoleSecretChatState,
- RoleIsVerified,
- RoleIsChannel
- };
ChatData(TDLibWrapper *tdLibWrapper, const QVariantMap &data);
@@ -370,21 +353,22 @@ ChatListModel::~ChatListModel()
QHash ChatListModel::roleNames() const
{
QHash roles;
- roles.insert(ChatData::RoleDisplay, "display");
- roles.insert(ChatData::RoleChatId, "chat_id");
- roles.insert(ChatData::RoleChatType, "chat_type");
- roles.insert(ChatData::RoleTitle, "title");
- roles.insert(ChatData::RolePhotoSmall, "photo_small");
- roles.insert(ChatData::RoleUnreadCount, "unread_count");
- roles.insert(ChatData::RoleLastReadInboxMessageId, "last_read_inbox_message_id");
- roles.insert(ChatData::RoleLastMessageSenderId, "last_message_sender_id");
- roles.insert(ChatData::RoleLastMessageDate, "last_message_date");
- roles.insert(ChatData::RoleLastMessageText, "last_message_text");
- roles.insert(ChatData::RoleLastMessageStatus, "last_message_status");
- roles.insert(ChatData::RoleChatMemberStatus, "chat_member_status");
- roles.insert(ChatData::RoleSecretChatState, "secret_chat_state");
- roles.insert(ChatData::RoleIsVerified, "is_verified");
- roles.insert(ChatData::RoleIsChannel, "is_channel");
+ roles.insert(ChatListModel::RoleDisplay, "display");
+ roles.insert(ChatListModel::RoleChatId, "chat_id");
+ roles.insert(ChatListModel::RoleChatType, "chat_type");
+ roles.insert(ChatListModel::RoleTitle, "title");
+ roles.insert(ChatListModel::RolePhotoSmall, "photo_small");
+ roles.insert(ChatListModel::RoleUnreadCount, "unread_count");
+ roles.insert(ChatListModel::RoleLastReadInboxMessageId, "last_read_inbox_message_id");
+ roles.insert(ChatListModel::RoleLastMessageSenderId, "last_message_sender_id");
+ roles.insert(ChatListModel::RoleLastMessageDate, "last_message_date");
+ roles.insert(ChatListModel::RoleLastMessageText, "last_message_text");
+ roles.insert(ChatListModel::RoleLastMessageStatus, "last_message_status");
+ roles.insert(ChatListModel::RoleChatMemberStatus, "chat_member_status");
+ roles.insert(ChatListModel::RoleSecretChatState, "secret_chat_state");
+ roles.insert(ChatListModel::RoleIsVerified, "is_verified");
+ roles.insert(ChatListModel::RoleIsChannel, "is_channel");
+ roles.insert(ChatListModel::RoleFilter, "filter");
return roles;
}
@@ -398,22 +382,23 @@ QVariant ChatListModel::data(const QModelIndex &index, int role) const
const int row = index.row();
if (row >= 0 && row < chatList.size()) {
const ChatData *data = chatList.at(row);
- switch ((ChatData::Role)role) {
- case ChatData::RoleDisplay: return data->chatData;
- case ChatData::RoleChatId: return data->chatId;
- case ChatData::RoleChatType: return data->chatType;
- case ChatData::RoleTitle: return data->title();
- case ChatData::RolePhotoSmall: return data->photoSmall();
- case ChatData::RoleUnreadCount: return data->unreadCount();
- case ChatData::RoleLastReadInboxMessageId: return data->lastReadInboxMessageId();
- case ChatData::RoleLastMessageSenderId: return data->senderUserId();
- case ChatData::RoleLastMessageText: return data->senderMessageText();
- case ChatData::RoleLastMessageDate: return data->senderMessageDate();
- case ChatData::RoleLastMessageStatus: return data->senderMessageStatus();
- case ChatData::RoleChatMemberStatus: return data->memberStatus;
- case ChatData::RoleSecretChatState: return data->secretChatState;
- case ChatData::RoleIsVerified: return data->verified;
- case ChatData::RoleIsChannel: return data->isChannel();
+ switch ((ChatListModel::Role)role) {
+ case ChatListModel::RoleDisplay: return data->chatData;
+ case ChatListModel::RoleChatId: return data->chatId;
+ case ChatListModel::RoleChatType: return data->chatType;
+ case ChatListModel::RoleTitle: return data->title();
+ case ChatListModel::RolePhotoSmall: return data->photoSmall();
+ case ChatListModel::RoleUnreadCount: return data->unreadCount();
+ case ChatListModel::RoleLastReadInboxMessageId: return data->lastReadInboxMessageId();
+ case ChatListModel::RoleLastMessageSenderId: return data->senderUserId();
+ case ChatListModel::RoleLastMessageText: return data->senderMessageText();
+ case ChatListModel::RoleLastMessageDate: return data->senderMessageDate();
+ case ChatListModel::RoleLastMessageStatus: return data->senderMessageStatus();
+ case ChatListModel::RoleChatMemberStatus: return data->memberStatus;
+ case ChatListModel::RoleSecretChatState: return data->secretChatState;
+ case ChatListModel::RoleIsVerified: return data->verified;
+ case ChatListModel::RoleIsChannel: return data->isChannel();
+ case ChatListModel::RoleFilter: return QString(data->title() + " " + data->senderMessageText()).trimmed();
}
}
return QVariant();
@@ -679,12 +664,12 @@ void ChatListModel::handleChatReadInboxUpdated(const QString &id, const QString
const int chatIndex = chatIndexMap.value(chatId);
ChatData *chat = chatList.at(chatIndex);
QVector changedRoles;
- changedRoles.append(ChatData::RoleDisplay);
+ changedRoles.append(ChatListModel::RoleDisplay);
if (chat->updateUnreadCount(unreadCount)) {
- changedRoles.append(ChatData::RoleUnreadCount);
+ changedRoles.append(ChatListModel::RoleUnreadCount);
}
if (chat->updateLastReadInboxMessageId(messageId)) {
- changedRoles.append(ChatData::RoleLastReadInboxMessageId);
+ changedRoles.append(ChatListModel::RoleLastReadInboxMessageId);
}
const QModelIndex modelIndex(index(chatIndex));
emit dataChanged(modelIndex, modelIndex, changedRoles);
@@ -728,7 +713,7 @@ void ChatListModel::handleChatPhotoUpdated(qlonglong chatId, const QVariantMap &
ChatData *chat = chatList.at(chatIndex);
chat->chatData.insert(PHOTO, photo);
QVector changedRoles;
- changedRoles.append(ChatData::RolePhotoSmall);
+ changedRoles.append(ChatListModel::RolePhotoSmall);
const QModelIndex modelIndex(index(chatIndex));
emit dataChanged(modelIndex, modelIndex, changedRoles);
} else {
@@ -817,7 +802,7 @@ void ChatListModel::handleChatTitleUpdated(const QString &chatId, const QString
ChatData *chat = chatList.at(chatIndex);
chat->chatData.insert(TITLE, title);
QVector changedRoles;
- changedRoles.append(ChatData::RoleTitle);
+ changedRoles.append(ChatListModel::RoleTitle);
const QModelIndex modelIndex(index(chatIndex));
emit dataChanged(modelIndex, modelIndex, changedRoles);
} else {
@@ -833,6 +818,6 @@ void ChatListModel::handleRelativeTimeRefreshTimer()
{
LOG("Refreshing timestamps");
QVector roles;
- roles.append(ChatData::RoleLastMessageDate);
+ roles.append(ChatListModel::RoleLastMessageDate);
emit dataChanged(index(0), index(chatList.size() - 1), roles);
}
diff --git a/src/chatlistmodel.h b/src/chatlistmodel.h
index 07b5523..734d51b 100644
--- a/src/chatlistmodel.h
+++ b/src/chatlistmodel.h
@@ -29,6 +29,26 @@ class ChatListModel : public QAbstractListModel
Q_PROPERTY(bool showAllChats READ showAllChats WRITE setShowAllChats NOTIFY showAllChatsChanged)
public:
+
+ enum Role {
+ RoleDisplay = Qt::DisplayRole,
+ RoleChatId,
+ RoleChatType,
+ RoleTitle,
+ RolePhotoSmall,
+ RoleUnreadCount,
+ RoleLastReadInboxMessageId,
+ RoleLastMessageSenderId,
+ RoleLastMessageDate,
+ RoleLastMessageText,
+ RoleLastMessageStatus,
+ RoleChatMemberStatus,
+ RoleSecretChatState,
+ RoleIsVerified,
+ RoleIsChannel,
+ RoleFilter
+ };
+
ChatListModel(TDLibWrapper *tdLibWrapper);
~ChatListModel() override;
diff --git a/src/chatmodel.cpp b/src/chatmodel.cpp
index 4bdb0fb..72d994b 100644
--- a/src/chatmodel.cpp
+++ b/src/chatmodel.cpp
@@ -156,11 +156,13 @@ QVariant ChatModel::data(const QModelIndex &index, int role) const
return QVariant();
}
-void ChatModel::clear()
+void ChatModel::clear(bool contentOnly)
{
LOG("Clearing chat model");
inReload = false;
inIncrementalUpdate = false;
+ searchModeActive = false;
+ searchQuery.clear();
if (!messages.isEmpty()) {
beginResetModel();
qDeleteAll(messages);
@@ -168,13 +170,16 @@ void ChatModel::clear()
messageIndexMap.clear();
endResetModel();
}
- if (!chatInformation.isEmpty()) {
- chatInformation.clear();
- emit smallPhotoChanged();
- }
- if (chatId) {
- chatId = 0;
- emit chatIdChanged();
+
+ if (!contentOnly) {
+ if (!chatInformation.isEmpty()) {
+ chatInformation.clear();
+ emit smallPhotoChanged();
+ }
+ if (chatId) {
+ chatId = 0;
+ emit chatIdChanged();
+ }
}
}
@@ -188,6 +193,7 @@ void ChatModel::initialize(const QVariantMap &chatInformation)
this->chatId = chatId;
this->messages.clear();
this->messageIndexMap.clear();
+ this->searchQuery.clear();
endResetModel();
emit chatIdChanged();
emit smallPhotoChanged();
@@ -197,15 +203,21 @@ void ChatModel::initialize(const QVariantMap &chatInformation)
void ChatModel::triggerLoadMoreHistory()
{
if (!this->inIncrementalUpdate && !messages.isEmpty()) {
- LOG("Trigger loading older history...");
- this->inIncrementalUpdate = true;
- this->tdLibWrapper->getChatHistory(chatId, messages.first()->messageId);
+ if (searchModeActive) {
+ LOG("Trigger loading older found messages...");
+ this->inIncrementalUpdate = true;
+ this->tdLibWrapper->searchChatMessages(chatId, searchQuery, messages.first()->messageId);
+ } else {
+ LOG("Trigger loading older history...");
+ this->inIncrementalUpdate = true;
+ this->tdLibWrapper->getChatHistory(chatId, messages.first()->messageId);
+ }
}
}
void ChatModel::triggerLoadMoreFuture()
{
- if (!this->inIncrementalUpdate && !messages.isEmpty()) {
+ if (!this->inIncrementalUpdate && !messages.isEmpty() && !searchModeActive) {
LOG("Trigger loading newer future...");
this->inIncrementalUpdate = true;
this->tdLibWrapper->getChatHistory(chatId, messages.last()->messageId, -49);
@@ -221,9 +233,8 @@ QVariantMap ChatModel::getMessage(int index)
{
if (index >= 0 && index < messages.size()) {
return messages.at(index)->messageData;
- } else {
- return QVariantMap();
}
+ return QVariantMap();
}
int ChatModel::getLastReadMessageIndex()
@@ -247,6 +258,20 @@ int ChatModel::getLastReadMessageIndex()
}
}
+void ChatModel::setSearchQuery(const QString newSearchQuery)
+{
+ if (this->searchQuery != newSearchQuery) {
+ this->clear(true);
+ this->searchQuery = newSearchQuery;
+ this->searchModeActive = !this->searchQuery.isEmpty();
+ if (this->searchModeActive) {
+ this->tdLibWrapper->searchChatMessages(this->chatId, this->searchQuery);
+ } else {
+ this->tdLibWrapper->getChatHistory(chatId, this->chatInformation.value(LAST_READ_INBOX_MESSAGE_ID).toLongLong());
+ }
+ }
+}
+
QVariantMap ChatModel::smallPhoto() const
{
return chatInformation.value(PHOTO).toMap().value(SMALL).toMap();
@@ -260,6 +285,7 @@ qlonglong ChatModel::getChatId() const
void ChatModel::handleMessagesReceived(const QVariantList &messages, int totalCount)
{
LOG("Receiving new messages :)" << messages.size());
+ LOG("Received while search mode is" << searchModeActive);
if (messages.size() == 0) {
LOG("No additional messages loaded, notifying chat UI...");
@@ -276,6 +302,7 @@ void ChatModel::handleMessagesReceived(const QVariantList &messages, int totalCo
if (this->isMostRecentMessageLoaded() || this->inIncrementalUpdate) {
QList messagesToBeAdded;
QListIterator messagesIterator(messages);
+
while (messagesIterator.hasNext()) {
const QVariantMap messageData = messagesIterator.next().toMap();
const qlonglong messageId = messageData.value(ID).toLongLong();
@@ -295,7 +322,11 @@ void ChatModel::handleMessagesReceived(const QVariantList &messages, int totalCo
if (!messagesToBeAdded.isEmpty() && (messagesToBeAdded.size() + messages.size()) < 10 && !inReload) {
LOG("Only a few messages received in first call, loading more...");
this->inReload = true;
- this->tdLibWrapper->getChatHistory(chatId, messagesToBeAdded.first()->messageId, 0);
+ if (this->searchModeActive) {
+ this->tdLibWrapper->searchChatMessages(chatId, searchQuery, messagesToBeAdded.first()->messageId);
+ } else {
+ this->tdLibWrapper->getChatHistory(chatId, messagesToBeAdded.first()->messageId, 0);
+ }
} else {
LOG("Messages loaded, notifying chat UI...");
this->inReload = false;
@@ -322,7 +353,7 @@ void ChatModel::handleNewMessageReceived(qlonglong chatId, const QVariantMap &me
{
const qlonglong messageId = message.value(ID).toLongLong();
if (chatId == this->chatId && !messageIndexMap.contains(messageId)) {
- if (this->isMostRecentMessageLoaded()) {
+ if (this->isMostRecentMessageLoaded() && !this->searchModeActive) {
LOG("New message received for this chat");
QList messagesToBeAdded;
messagesToBeAdded.append(new MessageData(message, messageId));
@@ -449,27 +480,40 @@ void ChatModel::handleMessagesDeleted(qlonglong chatId, const QList &
if (chatId == this->chatId) {
const int count = messageIds.size();
LOG(count << "messages in this chat were deleted...");
- int firstDeleted = -1, lastDeleted = -2;
- for (int i = 0; i < count; i++) {
- const int pos = messageIndexMap.value(messageIds.at(i), -1);
- if (pos >= 0) {
- if (pos == lastDeleted + 1) {
- lastDeleted = pos; // Extend the current range
+
+ int firstPosition = count, lastPosition = count;
+ for (int i = (count - 1); i > -1; i--) {
+ const int position = messageIndexMap.value(messageIds.at(i), -1);
+ if (position >= 0) {
+ // We found at least one message in our list that needs to be deleted
+ if (lastPosition == count) {
+ lastPosition = position;
+ }
+ if (firstPosition == count) {
+ firstPosition = position;
+ }
+ if (position < (firstPosition - 1)) {
+ // Some gap in between, can remove previous range and reset positions
+ removeRange(firstPosition, lastPosition);
+ firstPosition = lastPosition = position;
} else {
- removeRange(firstDeleted, lastDeleted);
- firstDeleted = lastDeleted = pos; // Start new range
+ // No gap in between, extend the range and continue loop
+ firstPosition = position;
}
}
}
- // Handle the last (and likely the only) range
- removeRange(firstDeleted, lastDeleted);
+ // After all elements have been processed, there may be one last range to remove
+ // But only if we found at least one item to remove
+ if (firstPosition != count && lastPosition != count) {
+ removeRange(firstPosition, lastPosition);
+ }
}
}
void ChatModel::removeRange(int firstDeleted, int lastDeleted)
{
if (firstDeleted >= 0 && firstDeleted <= lastDeleted) {
- LOG("Removing range" << firstDeleted << "..." << lastDeleted);
+ LOG("Removing range" << firstDeleted << "..." << lastDeleted << "| current messages size" << messages.size());
beginRemoveRows(QModelIndex(), firstDeleted, lastDeleted);
for (int i = firstDeleted; i <= lastDeleted; i++) {
MessageData *message = messages.at(i);
diff --git a/src/chatmodel.h b/src/chatmodel.h
index bd45b55..6895902 100644
--- a/src/chatmodel.h
+++ b/src/chatmodel.h
@@ -37,13 +37,14 @@ public:
virtual int rowCount(const QModelIndex&) const override;
virtual QVariant data(const QModelIndex &index, int role) const override;
- Q_INVOKABLE void clear();
+ Q_INVOKABLE void clear(bool contentOnly = false);
Q_INVOKABLE void initialize(const QVariantMap &chatInformation);
Q_INVOKABLE void triggerLoadMoreHistory();
Q_INVOKABLE void triggerLoadMoreFuture();
Q_INVOKABLE QVariantMap getChatInformation();
Q_INVOKABLE QVariantMap getMessage(int index);
Q_INVOKABLE int getLastReadMessageIndex();
+ Q_INVOKABLE void setSearchQuery(const QString newSearchQuery);
QVariantMap smallPhoto() const;
qlonglong getChatId() const;
@@ -93,6 +94,8 @@ private:
qlonglong chatId;
bool inReload;
bool inIncrementalUpdate;
+ bool searchModeActive;
+ QString searchQuery;
};
#endif // CHATMODEL_H
diff --git a/src/harbour-fernschreiber.cpp b/src/harbour-fernschreiber.cpp
index a2efd4f..6efdf5d 100644
--- a/src/harbour-fernschreiber.cpp
+++ b/src/harbour-fernschreiber.cpp
@@ -88,6 +88,11 @@ int main(int argc, char *argv[])
ChatListModel chatListModel(tdLibWrapper);
context->setContextProperty("chatListModel", &chatListModel);
+ QSortFilterProxyModel chatListProxyModel(view.data());
+ chatListProxyModel.setSourceModel(&chatListModel);
+ chatListProxyModel.setFilterRole(ChatListModel::RoleFilter);
+ chatListProxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
+ context->setContextProperty("chatListProxyModel", &chatListProxyModel);
ChatModel chatModel(tdLibWrapper);
context->setContextProperty("chatModel", &chatModel);
diff --git a/src/tdlibreceiver.h b/src/tdlibreceiver.h
index aae67bc..6a3068d 100644
--- a/src/tdlibreceiver.h
+++ b/src/tdlibreceiver.h
@@ -74,7 +74,7 @@ signals:
void stickerSet(const QVariantMap &stickerSet);
void chatMembers(const QString &extra, const QVariantList &members, int totalMembers);
void userFullInfo(const QVariantMap &userFullInfo);
- void userFullInfoUpdated(const QString &userId,const QVariantMap &userFullInfo);
+ void userFullInfoUpdated(const QString &userId, const QVariantMap &userFullInfo);
void basicGroupFullInfo(const QString &groupId, const QVariantMap &groupFullInfo);
void basicGroupFullInfoUpdated(const QString &groupId, const QVariantMap &groupFullInfo);
void supergroupFullInfo(const QString &groupId, const QVariantMap &groupFullInfo);
diff --git a/src/tdlibwrapper.cpp b/src/tdlibwrapper.cpp
index 5a43916..883658e 100644
--- a/src/tdlibwrapper.cpp
+++ b/src/tdlibwrapper.cpp
@@ -937,6 +937,30 @@ void TDLibWrapper::importContacts(const QVariantList &contacts)
this->sendRequest(requestObject);
}
+void TDLibWrapper::searchChatMessages(const qlonglong &chatId, const QString &query, const qlonglong fromMessageId)
+{
+ LOG("Searching for messages" << chatId << query << fromMessageId);
+ QVariantMap requestObject;
+ requestObject.insert(_TYPE, "searchChatMessages");
+ requestObject.insert("chat_id", chatId);
+ requestObject.insert("query", query);
+ requestObject.insert("from_message_id", fromMessageId);
+ requestObject.insert("offset", 0);
+ requestObject.insert("limit", 50);
+ requestObject.insert(_EXTRA, "searchChatMessages");
+ this->sendRequest(requestObject);
+}
+
+void TDLibWrapper::searchPublicChats(const QString &query)
+{
+ LOG("Searching public chats" << query);
+ QVariantMap requestObject;
+ requestObject.insert(_TYPE, "searchPublicChats");
+ requestObject.insert("query", query);
+ requestObject.insert(_EXTRA, "searchPublicChats");
+ this->sendRequest(requestObject);
+}
+
void TDLibWrapper::readAllChatMentions(qlonglong chatId)
{
LOG("Read all chat mentions" << chatId);
diff --git a/src/tdlibwrapper.h b/src/tdlibwrapper.h
index ed3ae1a..f51462f 100644
--- a/src/tdlibwrapper.h
+++ b/src/tdlibwrapper.h
@@ -179,6 +179,8 @@ public:
Q_INVOKABLE void getSecretChat(qlonglong secretChatId);
Q_INVOKABLE void closeSecretChat(qlonglong secretChatId);
Q_INVOKABLE void importContacts(const QVariantList &contacts);
+ Q_INVOKABLE void searchChatMessages(const qlonglong &chatId, const QString &query, const qlonglong fromMessageId = 0);
+ Q_INVOKABLE void searchPublicChats(const QString &query);
Q_INVOKABLE void readAllChatMentions(qlonglong chatId);
// Others (candidates for extraction ;))
diff --git a/translations/harbour-fernschreiber-de.ts b/translations/harbour-fernschreiber-de.ts
index 29f8c68..b0645d9 100644
--- a/translations/harbour-fernschreiber-de.ts
+++ b/translations/harbour-fernschreiber-de.ts
@@ -391,6 +391,14 @@
Chat schließen
+
+
+ Im Chat suchen
+
+
+
+ Im Chat suchen...
+
ChatSelectionPage
@@ -1044,6 +1052,14 @@
Neuer Chat
+
+
+ Ihre Chats filtern...
+
+
+
+ Chats suchen
+
Download von %1 erfolgreich.
@@ -1246,6 +1262,49 @@
+
+ SearchChatsPage
+
+
+ Keine Chats gefunden.
+
+
+
+ Suche Chats...
+
+
+
+ Privater Chat
+
+
+
+ Gruppe
+
+
+
+ Kanal
+
+
+
+ %1 Mitglied
+
+
+
+ %1 Abonnent
+
+
+
+ Chats suchen
+
+
+
+ Einen Chat suchen...
+
+
+
+ Geben Sie Ihre Anfrage ein, um die Suche zu starten (mindestens 5 Zeichen benötigt)
+
+
SettingsPage
diff --git a/translations/harbour-fernschreiber-en.ts b/translations/harbour-fernschreiber-en.ts
index 9db2c96..4345302 100644
--- a/translations/harbour-fernschreiber-en.ts
+++ b/translations/harbour-fernschreiber-en.ts
@@ -391,6 +391,14 @@
Close Chat
+
+
+ Search in Chat
+
+
+
+ Search in chat...
+
ChatSelectionPage
@@ -1044,6 +1052,14 @@
New Chat
+
+
+ Filter your chats...
+
+
+
+ Search Chats
+
Download of %1 successful.
@@ -1246,6 +1262,49 @@
+
+ SearchChatsPage
+
+
+ No chats found.
+
+
+
+ Searching chats...
+
+
+
+ Private Chat
+
+
+
+ Group
+
+
+
+ Channel
+
+
+
+ %1 member
+
+
+
+ %1 subscriber
+
+
+
+ Search Chats
+
+
+
+ Search a chat...
+
+
+
+ Enter your query to start searching (at least 5 characters needed)
+
+
SettingsPage
diff --git a/translations/harbour-fernschreiber-es.ts b/translations/harbour-fernschreiber-es.ts
index af19a0f..7efb477 100644
--- a/translations/harbour-fernschreiber-es.ts
+++ b/translations/harbour-fernschreiber-es.ts
@@ -381,6 +381,14 @@
Cerrar charla
+
+
+
+
+
+
+
+
ChatSelectionPage
@@ -1033,6 +1041,14 @@
Nueva charla
+
+
+
+
+
+
+
+
Bajada de %1 exitosa.
@@ -1227,6 +1243,49 @@
+
+ SearchChatsPage
+
+
+
+
+
+
+
+
+
+
+ Privado
+
+
+
+
+
+
+
+
+
+
+
+ %1 miembros
+
+
+
+ %1 suscriptores
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SettingsPage
diff --git a/translations/harbour-fernschreiber-fi.ts b/translations/harbour-fernschreiber-fi.ts
index d940597..eb9f6b6 100644
--- a/translations/harbour-fernschreiber-fi.ts
+++ b/translations/harbour-fernschreiber-fi.ts
@@ -391,6 +391,14 @@
Sulje keskustelu
+
+
+
+
+
+
+
+
ChatSelectionPage
@@ -1045,6 +1053,14 @@
Uusi keskustelu
+
+
+
+
+
+
+
+
@@ -1247,6 +1263,49 @@
+
+ SearchChatsPage
+
+
+
+
+
+
+
+
+
+
+ Yksityinen keskustelu
+
+
+
+
+
+
+
+
+
+
+
+ %1 jäsen
+
+
+
+ %1 tilaaja
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SettingsPage
diff --git a/translations/harbour-fernschreiber-hu.ts b/translations/harbour-fernschreiber-hu.ts
index 1d6a27d..63448a4 100644
--- a/translations/harbour-fernschreiber-hu.ts
+++ b/translations/harbour-fernschreiber-hu.ts
@@ -381,6 +381,14 @@
+
+
+
+
+
+
+
+
ChatSelectionPage
@@ -1033,6 +1041,14 @@
+
+
+
+
+
+
+
+
A %1 letöltése sikerült.
@@ -1227,6 +1243,49 @@
+
+ SearchChatsPage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %1 tag
+
+
+
+ %1 feliratkozott
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SettingsPage
diff --git a/translations/harbour-fernschreiber-it.ts b/translations/harbour-fernschreiber-it.ts
index 3f6c8e0..4fcac81 100644
--- a/translations/harbour-fernschreiber-it.ts
+++ b/translations/harbour-fernschreiber-it.ts
@@ -391,6 +391,14 @@
Chiudi chat
+
+
+
+
+
+
+
+
ChatSelectionPage
@@ -1044,6 +1052,14 @@
Nuova chat
+
+
+
+
+
+
+
+
Download di %1 completato.
@@ -1246,6 +1262,49 @@
+
+ SearchChatsPage
+
+
+
+
+
+
+
+
+
+
+ Chat privata
+
+
+
+
+
+
+
+
+
+
+
+ %1 membro
+
+
+
+ %1 abbonato
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SettingsPage
diff --git a/translations/harbour-fernschreiber-pl.ts b/translations/harbour-fernschreiber-pl.ts
index e8ad7cf..789ae0c 100644
--- a/translations/harbour-fernschreiber-pl.ts
+++ b/translations/harbour-fernschreiber-pl.ts
@@ -401,6 +401,14 @@
Zamknij czat
+
+
+
+
+
+
+
+
ChatSelectionPage
@@ -1055,6 +1063,14 @@
Nowy czat
+
+
+
+
+
+
+
+
@@ -1265,6 +1281,49 @@
+
+ SearchChatsPage
+
+
+
+
+
+
+
+
+
+
+ Prywatny czat
+
+
+
+
+
+
+
+
+
+
+
+ %1 członek
+
+
+
+ %1 subskrybent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SettingsPage
diff --git a/translations/harbour-fernschreiber-ru.ts b/translations/harbour-fernschreiber-ru.ts
index ce981da..b14f5f3 100644
--- a/translations/harbour-fernschreiber-ru.ts
+++ b/translations/harbour-fernschreiber-ru.ts
@@ -401,6 +401,14 @@
Закрыть чат
+
+
+
+
+
+
+
+
ChatSelectionPage
@@ -1055,6 +1063,14 @@
Новый Чат
+
+
+
+
+
+
+
+
Успешно скачано %1.
@@ -1265,6 +1281,49 @@
+
+ SearchChatsPage
+
+
+
+
+
+
+
+
+
+
+ Приватный Чат
+
+
+
+
+
+
+
+
+
+
+
+ %1 участников
+
+
+
+ %1 подписчиков
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SettingsPage
diff --git a/translations/harbour-fernschreiber-sv.ts b/translations/harbour-fernschreiber-sv.ts
index 0aa1fbc..bf6045f 100644
--- a/translations/harbour-fernschreiber-sv.ts
+++ b/translations/harbour-fernschreiber-sv.ts
@@ -391,6 +391,14 @@
Stäng chatten
+
+
+
+
+
+
+
+
ChatSelectionPage
@@ -1044,6 +1052,14 @@
Ny chatt
+
+
+
+
+
+
+
+
Nerladdning av %1 slutförd.
@@ -1246,6 +1262,49 @@
Valt av:
+
+ SearchChatsPage
+
+
+
+
+
+
+
+
+
+
+ Privat chatt
+
+
+
+
+
+
+
+
+
+
+
+ %1 medlem
+
+
+
+ %1 prenumerant
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SettingsPage
diff --git a/translations/harbour-fernschreiber-zh_CN.ts b/translations/harbour-fernschreiber-zh_CN.ts
index 3557b00..fad6d26 100644
--- a/translations/harbour-fernschreiber-zh_CN.ts
+++ b/translations/harbour-fernschreiber-zh_CN.ts
@@ -381,6 +381,14 @@
关闭对话
+
+
+
+
+
+
+
+
ChatSelectionPage
@@ -1033,6 +1041,14 @@
新对话
+
+
+
+
+
+
+
+
已成功下载 %1 。
@@ -1227,6 +1243,49 @@
+
+ SearchChatsPage
+
+
+
+
+
+
+
+
+
+
+ 个人对话
+
+
+
+
+
+
+
+
+
+
+
+ %1 位成员
+
+
+
+ %1 位订阅者
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SettingsPage
diff --git a/translations/harbour-fernschreiber.ts b/translations/harbour-fernschreiber.ts
index b3969bf..21a1b2f 100644
--- a/translations/harbour-fernschreiber.ts
+++ b/translations/harbour-fernschreiber.ts
@@ -391,6 +391,14 @@
+
+
+
+
+
+
+
+
ChatSelectionPage
@@ -1044,6 +1052,14 @@
+
+
+
+
+
+
+
+
Download of %1 successful.
@@ -1246,6 +1262,49 @@
+
+ SearchChatsPage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %1 member
+
+
+
+ %1 subscriber
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SettingsPage