Merge pull request #205 from Wunderfitz/new-chat-from-contacts-7
New chat from contacts, initial secret chat support
This commit is contained in:
commit
965f6db84e
33 changed files with 1949 additions and 69 deletions
|
@ -24,6 +24,7 @@ SOURCES += src/harbour-fernschreiber.cpp \
|
||||||
src/appsettings.cpp \
|
src/appsettings.cpp \
|
||||||
src/chatlistmodel.cpp \
|
src/chatlistmodel.cpp \
|
||||||
src/chatmodel.cpp \
|
src/chatmodel.cpp \
|
||||||
|
src/contactsmodel.cpp \
|
||||||
src/dbusadaptor.cpp \
|
src/dbusadaptor.cpp \
|
||||||
src/dbusinterface.cpp \
|
src/dbusinterface.cpp \
|
||||||
src/emojisearchworker.cpp \
|
src/emojisearchworker.cpp \
|
||||||
|
@ -72,6 +73,7 @@ DISTFILES += qml/harbour-fernschreiber.qml \
|
||||||
qml/pages/ChatSelectionPage.qml \
|
qml/pages/ChatSelectionPage.qml \
|
||||||
qml/pages/CoverPage.qml \
|
qml/pages/CoverPage.qml \
|
||||||
qml/pages/InitializationPage.qml \
|
qml/pages/InitializationPage.qml \
|
||||||
|
qml/pages/NewChatPage.qml \
|
||||||
qml/pages/OverviewPage.qml \
|
qml/pages/OverviewPage.qml \
|
||||||
qml/pages/AboutPage.qml \
|
qml/pages/AboutPage.qml \
|
||||||
qml/pages/PollCreationPage.qml \
|
qml/pages/PollCreationPage.qml \
|
||||||
|
@ -145,6 +147,7 @@ HEADERS += \
|
||||||
src/appsettings.h \
|
src/appsettings.h \
|
||||||
src/chatlistmodel.h \
|
src/chatlistmodel.h \
|
||||||
src/chatmodel.h \
|
src/chatmodel.h \
|
||||||
|
src/contactsmodel.h \
|
||||||
src/dbusadaptor.h \
|
src/dbusadaptor.h \
|
||||||
src/dbusinterface.h \
|
src/dbusinterface.h \
|
||||||
src/debuglog.h \
|
src/debuglog.h \
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.6
|
import QtQuick 2.6
|
||||||
import Sailfish.Silica 1.0
|
import Sailfish.Silica 1.0
|
||||||
|
import WerkWolf.Fernschreiber 1.0
|
||||||
|
|
||||||
import "../js/twemoji.js" as Emoji
|
import "../js/twemoji.js" as Emoji
|
||||||
import "../js/functions.js" as Functions
|
import "../js/functions.js" as Functions
|
||||||
|
@ -14,12 +15,13 @@ PhotoTextsListItem {
|
||||||
// chat title
|
// chat title
|
||||||
primaryText.text: title ? Emoji.emojify(title + ( display.notification_settings.mute_for > 0 ? " 🔇" : "" ), Theme.fontSizeMedium) : qsTr("Unknown")
|
primaryText.text: title ? Emoji.emojify(title + ( display.notification_settings.mute_for > 0 ? " 🔇" : "" ), Theme.fontSizeMedium) : qsTr("Unknown")
|
||||||
// last user
|
// last user
|
||||||
prologSecondaryText.text: is_channel ? "" : ( last_message_sender_id ? ( last_message_sender_id !== ownUserId ? Emoji.emojify(Functions.getUserName(tdLibWrapper.getUserInformation(last_message_sender_id)), primaryText.font.pixelSize) : qsTr("You") ) : qsTr("Unknown") )
|
prologSecondaryText.text: is_channel ? "" : ( last_message_sender_id ? ( last_message_sender_id !== ownUserId ? Emoji.emojify(Functions.getUserName(tdLibWrapper.getUserInformation(last_message_sender_id)), primaryText.font.pixelSize) : qsTr("You") ) : "" )
|
||||||
// last message
|
// last message
|
||||||
secondaryText.text: last_message_text ? Emoji.emojify(Functions.enhanceHtmlEntities(last_message_text), Theme.fontSizeExtraSmall) : qsTr("Unknown")
|
secondaryText.text: last_message_text ? Emoji.emojify(Functions.enhanceHtmlEntities(last_message_text), Theme.fontSizeExtraSmall) : "<i>" + qsTr("No message in this chat.") + "</i>"
|
||||||
// message date
|
// message date
|
||||||
tertiaryText.text: ( last_message_date ? Functions.getDateTimeElapsed(last_message_date) : qsTr("Unknown") ) + Emoji.emojify(last_message_status, tertiaryText.font.pixelSize)
|
tertiaryText.text: ( last_message_date ? ( last_message_date.length === 0 ? "" : Functions.getDateTimeElapsed(last_message_date) + Emoji.emojify(last_message_status, tertiaryText.font.pixelSize) ) : "" )
|
||||||
unreadCount: unread_count
|
unreadCount: unread_count
|
||||||
|
isSecret: ( chat_type === TelegramAPI.ChatTypeSecret )
|
||||||
|
|
||||||
openMenuOnPressAndHold: true//chat_id != overviewPage.ownUserId
|
openMenuOnPressAndHold: true//chat_id != overviewPage.ownUserId
|
||||||
menu: ContextMenu {
|
menu: ContextMenu {
|
||||||
|
|
|
@ -1,5 +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
|
||||||
|
|
||||||
ListItem {
|
ListItem {
|
||||||
|
|
||||||
id: chatListViewItem
|
id: chatListViewItem
|
||||||
|
@ -9,7 +11,8 @@ ListItem {
|
||||||
property alias secondaryText: secondaryText //usually last message
|
property alias secondaryText: secondaryText //usually last message
|
||||||
property alias tertiaryText: tertiaryText //usually last message date
|
property alias tertiaryText: tertiaryText //usually last message date
|
||||||
|
|
||||||
property int unreadCount
|
property int unreadCount: 0
|
||||||
|
property bool isSecret: false
|
||||||
property alias pictureThumbnail: pictureThumbnail
|
property alias pictureThumbnail: pictureThumbnail
|
||||||
|
|
||||||
contentHeight: mainRow.height + separator.height + 2 * Theme.paddingMedium
|
contentHeight: mainRow.height + separator.height + 2 * Theme.paddingMedium
|
||||||
|
@ -50,6 +53,24 @@ ListItem {
|
||||||
height: parent.width
|
height: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: chatSecretBackground
|
||||||
|
color: Theme.overlayBackgroundColor
|
||||||
|
width: Theme.fontSizeExtraLarge
|
||||||
|
height: Theme.fontSizeExtraLarge
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
radius: parent.width / 2
|
||||||
|
visible: chatListViewItem.isSecret
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: "image://theme/icon-s-secure"
|
||||||
|
height: Theme.fontSizeMedium
|
||||||
|
width: Theme.fontSizeMedium
|
||||||
|
anchors.centerIn: chatSecretBackground
|
||||||
|
visible: chatListViewItem.isSecret
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: chatUnreadMessagesCountBackground
|
id: chatUnreadMessagesCountBackground
|
||||||
color: Theme.highlightBackgroundColor
|
color: Theme.highlightBackgroundColor
|
||||||
|
@ -104,6 +125,7 @@ ListItem {
|
||||||
width: parent.width - Theme.paddingMedium - prologSecondaryText.width
|
width: parent.width - Theme.paddingMedium - prologSecondaryText.width
|
||||||
truncationMode: TruncationMode.Fade
|
truncationMode: TruncationMode.Fade
|
||||||
textFormat: Text.StyledText
|
textFormat: Text.StyledText
|
||||||
|
visible: prologSecondaryText.width < ( parent.width - Theme.paddingLarge )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,15 @@ SilicaFlickable {
|
||||||
tdLibWrapper.getUserFullInfo(chatInformationPage.chatPartnerGroupId);
|
tdLibWrapper.getUserFullInfo(chatInformationPage.chatPartnerGroupId);
|
||||||
tdLibWrapper.getUserProfilePhotos(chatInformationPage.chatPartnerGroupId, 100, 0);
|
tdLibWrapper.getUserProfilePhotos(chatInformationPage.chatPartnerGroupId, 100, 0);
|
||||||
break;
|
break;
|
||||||
|
case "chatTypeSecret":
|
||||||
|
chatInformationPage.isSecretChat = true;
|
||||||
|
chatInformationPage.chatPartnerGroupId = chatInformationPage.chatInformation.type.user_id.toString();
|
||||||
|
if(!chatInformationPage.privateChatUserInformation.id) {
|
||||||
|
chatInformationPage.privateChatUserInformation = tdLibWrapper.getUserInformation(chatInformationPage.chatPartnerGroupId);
|
||||||
|
}
|
||||||
|
tdLibWrapper.getUserFullInfo(chatInformationPage.chatPartnerGroupId);
|
||||||
|
tdLibWrapper.getUserProfilePhotos(chatInformationPage.chatPartnerGroupId, 100, 0);
|
||||||
|
break;
|
||||||
case "chatTypeBasicGroup":
|
case "chatTypeBasicGroup":
|
||||||
chatInformationPage.isBasicGroup = true;
|
chatInformationPage.isBasicGroup = true;
|
||||||
chatInformationPage.chatPartnerGroupId = chatInformation.type.basic_group_id.toString();
|
chatInformationPage.chatPartnerGroupId = chatInformation.type.basic_group_id.toString();
|
||||||
|
@ -60,8 +69,8 @@ SilicaFlickable {
|
||||||
chatInformationPage.isChannel = chatInformationPage.groupInformation.is_channel;
|
chatInformationPage.isChannel = chatInformationPage.groupInformation.is_channel;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Debug.log("is set up", chatInformationPage.isPrivateChat, chatInformationPage.isBasicGroup, chatInformationPage.isSuperGroup, chatInformationPage.chatPartnerGroupId)
|
Debug.log("is set up", chatInformationPage.isPrivateChat, chatInformationPage.isSecretChat, chatInformationPage.isBasicGroup, chatInformationPage.isSuperGroup, chatInformationPage.chatPartnerGroupId)
|
||||||
if(!chatInformationPage.isPrivateChat) {
|
if(!(chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat)) {
|
||||||
updateGroupStatusText();
|
updateGroupStatusText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,18 +156,18 @@ SilicaFlickable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onUserFullInfoReceived: {
|
onUserFullInfoReceived: {
|
||||||
if(chatInformationPage.isPrivateChat && userFullInfo["@extra"] === chatInformationPage.chatPartnerGroupId) {
|
if((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && userFullInfo["@extra"] === chatInformationPage.chatPartnerGroupId) {
|
||||||
chatInformationPage.chatPartnerFullInformation = userFullInfo;
|
chatInformationPage.chatPartnerFullInformation = userFullInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onUserFullInfoUpdated: {
|
onUserFullInfoUpdated: {
|
||||||
if(chatInformationPage.isPrivateChat && userId === chatInformationPage.chatPartnerGroupId) {
|
if((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && userId === chatInformationPage.chatPartnerGroupId) {
|
||||||
chatInformationPage.chatPartnerFullInformation = userFullInfo;
|
chatInformationPage.chatPartnerFullInformation = userFullInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onUserProfilePhotosReceived: {
|
onUserProfilePhotosReceived: {
|
||||||
if(chatInformationPage.isPrivateChat && extra === chatInformationPage.chatPartnerGroupId) {
|
if((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && extra === chatInformationPage.chatPartnerGroupId) {
|
||||||
chatInformationPage.chatPartnerProfilePhotos = photos;
|
chatInformationPage.chatPartnerProfilePhotos = photos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,14 +189,10 @@ SilicaFlickable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
|
||||||
Component.onCompleted: {7
|
|
||||||
initializePage();
|
initializePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ListModel {
|
ListModel {
|
||||||
id: membersList
|
id: membersList
|
||||||
}
|
}
|
||||||
|
@ -227,13 +232,13 @@ SilicaFlickable {
|
||||||
}
|
}
|
||||||
text: chatInformation.notification_settings.mute_for > 0 ? qsTr("Unmute Chat") : qsTr("Mute Chat")
|
text: chatInformation.notification_settings.mute_for > 0 ? qsTr("Unmute Chat") : qsTr("Mute Chat")
|
||||||
}
|
}
|
||||||
// MenuItem { //TODO Implement
|
MenuItem {
|
||||||
// visible: !userIsMember
|
visible: chatInformationPage.isPrivateChat
|
||||||
// onClicked: {
|
onClicked: {
|
||||||
// tdLibWrapper.joinChat(chatInformationPage.chatInformation.id);
|
tdLibWrapper.createNewSecretChat(chatInformationPage.chatPartnerGroupId);
|
||||||
// }
|
}
|
||||||
// text: qsTr("Join Chat")
|
text: qsTr("New Secret Chat")
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
// header
|
// header
|
||||||
PageHeader {
|
PageHeader {
|
||||||
|
@ -278,14 +283,14 @@ SilicaFlickable {
|
||||||
active: imageContainer.hasImage
|
active: imageContainer.hasImage
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
anchors.fill: chatPictureThumbnail
|
anchors.fill: chatPictureThumbnail
|
||||||
source: chatInformationPage.isPrivateChat
|
source: ( chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat)
|
||||||
? "ChatInformationProfilePictureList.qml"
|
? "ChatInformationProfilePictureList.qml"
|
||||||
: "ChatInformationProfilePicture.qml"
|
: "ChatInformationProfilePicture.qml"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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.privateChatUserInformation.username || chatInformationPage.chatPartnerGroupId)) : ""
|
description: (chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) ? ("@"+(chatInformationPage.privateChatUserInformation.username || chatInformationPage.chatPartnerGroupId)) : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
SilicaFlickable {
|
SilicaFlickable {
|
||||||
|
@ -350,7 +355,7 @@ SilicaFlickable {
|
||||||
|
|
||||||
ChatInformationEditArea {
|
ChatInformationEditArea {
|
||||||
visible: canEdit
|
visible: canEdit
|
||||||
canEdit: !chatInformationPage.isPrivateChat && chatInformationPage.groupInformation.status && (chatInformationPage.groupInformation.status.can_change_info || chatInformationPage.groupInformation.status["@type"] === "chatMemberStatusCreator")
|
canEdit: !(chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && chatInformationPage.groupInformation.status && (chatInformationPage.groupInformation.status.can_change_info || chatInformationPage.groupInformation.status["@type"] === "chatMemberStatusCreator")
|
||||||
headerText: qsTr("Chat Title", "group title header")
|
headerText: qsTr("Chat Title", "group title header")
|
||||||
text: chatInformationPage.chatInformation.title
|
text: chatInformationPage.chatInformation.title
|
||||||
|
|
||||||
|
@ -375,13 +380,13 @@ SilicaFlickable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ChatInformationEditArea {
|
ChatInformationEditArea {
|
||||||
canEdit: (chatInformationPage.isPrivateChat && chatInformationPage.privateChatUserInformation.id === chatInformationPage.myUserId) || ((chatInformationPage.isBasicGroup || chatInformationPage.isSuperGroup) && chatInformationPage.groupInformation && (chatInformationPage.groupInformation.status.can_change_info || chatInformationPage.groupInformation.status["@type"] === "chatMemberStatusCreator"))
|
canEdit: ((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && chatInformationPage.privateChatUserInformation.id === chatInformationPage.myUserId) || ((chatInformationPage.isBasicGroup || chatInformationPage.isSuperGroup) && chatInformationPage.groupInformation && (chatInformationPage.groupInformation.status.can_change_info || chatInformationPage.groupInformation.status["@type"] === "chatMemberStatusCreator"))
|
||||||
emptyPlaceholderText: qsTr("There is no information text available, yet.")
|
emptyPlaceholderText: qsTr("There is no information text available, yet.")
|
||||||
headerText: qsTr("Info", "group or user infotext header")
|
headerText: qsTr("Info", "group or user infotext header")
|
||||||
multiLine: true
|
multiLine: true
|
||||||
text: (chatInformationPage.isPrivateChat ? chatInformationPage.chatPartnerFullInformation.bio : chatInformationPage.groupFullInformation.description) || ""
|
text: ((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) ? chatInformationPage.chatPartnerFullInformation.bio : chatInformationPage.groupFullInformation.description) || ""
|
||||||
onSaveButtonClicked: {
|
onSaveButtonClicked: {
|
||||||
if(chatInformationPage.isPrivateChat) { // own bio
|
if ((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat)) { // own bio
|
||||||
tdLibWrapper.setBio(textValue);
|
tdLibWrapper.setBio(textValue);
|
||||||
} else { // group info
|
} else { // group info
|
||||||
tdLibWrapper.setChatDescription(chatInformationPage.chatInformation.id, textValue);
|
tdLibWrapper.setChatDescription(chatInformationPage.chatInformation.id, textValue);
|
||||||
|
@ -391,7 +396,7 @@ SilicaFlickable {
|
||||||
|
|
||||||
ChatInformationTextItem {
|
ChatInformationTextItem {
|
||||||
headerText: qsTr("Phone Number", "user phone number header")
|
headerText: qsTr("Phone Number", "user phone number header")
|
||||||
text: (chatInformationPage.isPrivateChat && chatInformationPage.privateChatUserInformation.phone_number ? "+"+chatInformationPage.privateChatUserInformation.phone_number : "") || ""
|
text: ((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && chatInformationPage.privateChatUserInformation.phone_number ? "+"+chatInformationPage.privateChatUserInformation.phone_number : "") || ""
|
||||||
isLinkedLabel: true
|
isLinkedLabel: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,7 +413,7 @@ SilicaFlickable {
|
||||||
visible: !!inviteLinkItem.text
|
visible: !!inviteLinkItem.text
|
||||||
ChatInformationTextItem {
|
ChatInformationTextItem {
|
||||||
id: inviteLinkItem
|
id: inviteLinkItem
|
||||||
text: !isPrivateChat ? chatInformationPage.groupFullInformation.invite_link : ""
|
text: !(chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) ? chatInformationPage.groupFullInformation.invite_link : ""
|
||||||
width: parent.width - inviteLinkButton.width
|
width: parent.width - inviteLinkButton.width
|
||||||
}
|
}
|
||||||
IconButton {
|
IconButton {
|
||||||
|
|
|
@ -27,15 +27,15 @@ import "../../js/functions.js" as Functions
|
||||||
|
|
||||||
ChatInformationTabItemBase {
|
ChatInformationTabItemBase {
|
||||||
id: tabBase
|
id: tabBase
|
||||||
loadingText: isPrivateChat ? qsTr("Loading common chats…", "chats you have in common with a user") : qsTr("Loading group members…")
|
loadingText: (isPrivateChat || isSecretChat) ? qsTr("Loading common chats…", "chats you have in common with a user") : qsTr("Loading group members…")
|
||||||
loading: ( chatInformationPage.isSuperGroup || chatInformationPage.isPrivateChat) && !chatInformationPage.isChannel
|
loading: ( chatInformationPage.isSuperGroup || chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && !chatInformationPage.isChannel
|
||||||
loadingVisible: loading && membersView.count === 0
|
loadingVisible: loading && membersView.count === 0
|
||||||
|
|
||||||
property var chatPartnerCommonGroupsIds: ([]);
|
property var chatPartnerCommonGroupsIds: ([]);
|
||||||
|
|
||||||
SilicaListView {
|
SilicaListView {
|
||||||
id: membersView
|
id: membersView
|
||||||
model: chatInformationPage.isPrivateChat ? (chatPartnerCommonGroupsIds.length > 0 ? delegateModel : null) : pageContent.membersList
|
model: (chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) ? (chatPartnerCommonGroupsIds.length > 0 ? delegateModel : null) : pageContent.membersList
|
||||||
clip: true
|
clip: true
|
||||||
height: tabBase.height
|
height: tabBase.height
|
||||||
width: tabBase.width
|
width: tabBase.width
|
||||||
|
@ -68,7 +68,7 @@ ChatInformationTabItemBase {
|
||||||
ViewPlaceholder {
|
ViewPlaceholder {
|
||||||
y: Theme.paddingLarge
|
y: Theme.paddingLarge
|
||||||
enabled: membersView.count === 0
|
enabled: membersView.count === 0
|
||||||
text: chatInformationPage.isPrivateChat ? qsTr("You don't have any groups in common with this user.") : ( chatInformationPage.isChannel ? qsTr("Channel members are anonymous.") : qsTr("This group is empty.") )
|
text: (chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) ? qsTr("You don't have any groups in common with this user.") : ( chatInformationPage.isChannel ? qsTr("Channel members are anonymous.") : qsTr("This group is empty.") )
|
||||||
}
|
}
|
||||||
delegate: PhotoTextsListItem {
|
delegate: PhotoTextsListItem {
|
||||||
pictureThumbnail {
|
pictureThumbnail {
|
||||||
|
@ -204,7 +204,7 @@ ChatInformationTabItemBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onChatsReceived: {// common chats with user
|
onChatsReceived: {// common chats with user
|
||||||
if(isPrivateChat && chats["@extra"] === chatInformationPage.chatPartnerGroupId) {
|
if((isPrivateChat || isSecretChat) && chats["@extra"] === chatInformationPage.chatPartnerGroupId) {
|
||||||
tabBase.chatPartnerCommonGroupsIds = chats.chat_ids;
|
tabBase.chatPartnerCommonGroupsIds = chats.chat_ids;
|
||||||
delegateModel.applyFilter();
|
delegateModel.applyFilter();
|
||||||
// if we set it directly, the views start scrolling
|
// if we set it directly, the views start scrolling
|
||||||
|
@ -221,7 +221,7 @@ ChatInformationTabItemBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if(chatInformationPage.isPrivateChat) {
|
if(chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) {
|
||||||
tdLibWrapper.getGroupsInCommon(chatInformationPage.chatPartnerGroupId, 200, 0); // we only use the first 200
|
tdLibWrapper.getGroupsInCommon(chatInformationPage.chatPartnerGroupId, 200, 0); // we only use the first 200
|
||||||
} else if(chatInformationPage.isSuperGroup) {
|
} else if(chatInformationPage.isSuperGroup) {
|
||||||
fetchMoreMembersTimer.start();
|
fetchMoreMembersTimer.start();
|
||||||
|
|
|
@ -124,14 +124,14 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if(!(isPrivateChat && chatPartnerGroupId === myUserId.toString())) {
|
if(!((isPrivateChat || isSecretChat) && chatPartnerGroupId === myUserId.toString())) {
|
||||||
tabModel.append({
|
tabModel.append({
|
||||||
tab:"ChatInformationTabItemMembersGroups",
|
tab:"ChatInformationTabItemMembersGroups",
|
||||||
title: chatInformationPage.isPrivateChat ? qsTr("Groups", "Button: groups in common (short)") : qsTr("Members", "Button: Group Members"),
|
title: ( chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat ) ? qsTr("Groups", "Button: groups in common (short)") : qsTr("Members", "Button: Group Members"),
|
||||||
image: "image://theme/icon-m-people"
|
image: "image://theme/icon-m-people"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if(!isPrivateChat && (groupInformation.status.can_restrict_members || groupInformation.status["@type"] === "chatMemberStatusCreator")) {
|
if(!(isPrivateChat || isSecretChat) && (groupInformation.status.can_restrict_members || groupInformation.status["@type"] === "chatMemberStatusCreator")) {
|
||||||
tabModel.append({
|
tabModel.append({
|
||||||
tab:"ChatInformationTabItemSettings",
|
tab:"ChatInformationTabItemSettings",
|
||||||
title: qsTr("Settings", "Button: Chat Settings"),
|
title: qsTr("Settings", "Button: Chat Settings"),
|
||||||
|
|
|
@ -160,6 +160,7 @@ function getMessageText(message, simple, myself) {
|
||||||
|
|
||||||
return myself ? qsTr("sent an unsupported message: %1", "myself; %1 is message type").arg(message.content['@type'].substring(7)) : qsTr("sent an unsupported message: %1", "%1 is message type").arg(message.content['@type'].substring(7));
|
return myself ? qsTr("sent an unsupported message: %1", "myself; %1 is message type").arg(message.content['@type'].substring(7)) : qsTr("sent an unsupported message: %1", "%1 is message type").arg(message.content['@type'].substring(7));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getChatPartnerStatusText(statusType, was_online) {
|
function getChatPartnerStatusText(statusType, was_online) {
|
||||||
switch(statusType) {
|
switch(statusType) {
|
||||||
case "userStatusEmpty":
|
case "userStatusEmpty":
|
||||||
|
@ -176,6 +177,18 @@ function getChatPartnerStatusText(statusType, was_online) {
|
||||||
return qsTr("offline, was recently online");
|
return qsTr("offline, was recently online");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSecretChatStatus(secretChatDetails) {
|
||||||
|
switch (secretChatDetails.state["@type"]) {
|
||||||
|
case "secretChatStateClosed":
|
||||||
|
return "<b>" + qsTr("Closed!") + "</b>";
|
||||||
|
case "secretChatStatePending":
|
||||||
|
return qsTr("Pending acknowledgement");
|
||||||
|
case "secretChatStateReady":
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getChatMemberStatusText(statusType) {
|
function getChatMemberStatusText(statusType) {
|
||||||
// chatMemberStatusAdministrator, chatMemberStatusBanned, chatMemberStatusCreator, chatMemberStatusLeft, chatMemberStatusMember, and chatMemberStatusRestricted.
|
// chatMemberStatusAdministrator, chatMemberStatusBanned, chatMemberStatusCreator, chatMemberStatusLeft, chatMemberStatusMember, and chatMemberStatusRestricted.
|
||||||
switch(statusType) {
|
switch(statusType) {
|
||||||
|
|
|
@ -54,13 +54,13 @@ function emojify(str, emojiSize) {
|
||||||
basePath,
|
basePath,
|
||||||
iconId,
|
iconId,
|
||||||
'.svg',
|
'.svg',
|
||||||
'" align="middle" width="',
|
'" align="bottom" width="',
|
||||||
emojiSize,
|
Math.round(emojiSize * 6 / 5 ),
|
||||||
'" height="',
|
'" height="',
|
||||||
emojiSize,
|
Math.round(emojiSize * 6 / 5 ),
|
||||||
'"/>'
|
'"/>'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return ret.replace(ampersandRe, "&");
|
return ret.replace(ampersandRe, "&");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,13 +34,14 @@ Page {
|
||||||
property int myUserId: tdLibWrapper.getUserInformation().id;
|
property int myUserId: tdLibWrapper.getUserInformation().id;
|
||||||
|
|
||||||
property bool isPrivateChat: false
|
property bool isPrivateChat: false
|
||||||
|
property bool isSecretChat: false
|
||||||
property bool isBasicGroup: false
|
property bool isBasicGroup: false
|
||||||
property bool isSuperGroup: false
|
property bool isSuperGroup: false
|
||||||
property bool isChannel: false
|
property bool isChannel: false
|
||||||
|
|
||||||
property string chatPartnerGroupId
|
property string chatPartnerGroupId
|
||||||
|
|
||||||
property bool userIsMember: (isPrivateChat && chatInformation["@type"]) || // should be optimized
|
property bool userIsMember: ((isPrivateChat || isSecretChat ) && chatInformation["@type"]) || // should be optimized
|
||||||
(isBasicGroup || isSuperGroup) && (
|
(isBasicGroup || isSuperGroup) && (
|
||||||
(groupInformation.status["@type"] === "chatMemberStatusMember")
|
(groupInformation.status["@type"] === "chatMemberStatusMember")
|
||||||
|| (groupInformation.status["@type"] === "chatMemberStatusAdministrator")
|
|| (groupInformation.status["@type"] === "chatMemberStatusAdministrator")
|
||||||
|
|
|
@ -36,8 +36,11 @@ Page {
|
||||||
property bool isInitialized: false;
|
property bool isInitialized: false;
|
||||||
readonly property int myUserId: tdLibWrapper.getUserInformation().id;
|
readonly property int myUserId: tdLibWrapper.getUserInformation().id;
|
||||||
property var chatInformation;
|
property var chatInformation;
|
||||||
|
property var secretChatDetails;
|
||||||
property alias chatPicture: chatPictureThumbnail.photoData
|
property alias chatPicture: chatPictureThumbnail.photoData
|
||||||
property bool isPrivateChat: false;
|
property bool isPrivateChat: false;
|
||||||
|
property bool isSecretChat: false;
|
||||||
|
property bool isSecretChatReady: false;
|
||||||
property bool isBasicGroup: false;
|
property bool isBasicGroup: false;
|
||||||
property bool isSuperGroup: false;
|
property bool isSuperGroup: false;
|
||||||
property bool isChannel: false;
|
property bool isChannel: false;
|
||||||
|
@ -46,7 +49,7 @@ Page {
|
||||||
property int chatOnlineMemberCount: 0;
|
property int chatOnlineMemberCount: 0;
|
||||||
property var emojiProposals;
|
property var emojiProposals;
|
||||||
property bool iterativeInitialization: false;
|
property bool iterativeInitialization: false;
|
||||||
readonly property bool userIsMember: (isPrivateChat && 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")
|
||||||
|| (chatGroupInformation.status["@type"] === "chatMemberStatusAdministrator")
|
|| (chatGroupInformation.status["@type"] === "chatMemberStatusAdministrator")
|
||||||
|
@ -104,11 +107,21 @@ Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateChatPartnerStatusText() {
|
function updateChatPartnerStatusText() {
|
||||||
if(chatPage.state === "selectMessages") {
|
if (chatPage.state === "selectMessages") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var statusText = Functions.getChatPartnerStatusText(chatPartnerInformation.status['@type'], chatPartnerInformation.status.was_online);
|
var statusText = Functions.getChatPartnerStatusText(chatPartnerInformation.status['@type'], chatPartnerInformation.status.was_online);
|
||||||
if(statusText) {
|
if (chatPage.secretChatDetails) {
|
||||||
|
var secretChatStatus = Functions.getSecretChatStatus(chatPage.secretChatDetails);
|
||||||
|
if (statusText && secretChatStatus) {
|
||||||
|
statusText += " - ";
|
||||||
|
}
|
||||||
|
if (secretChatStatus) {
|
||||||
|
statusText += secretChatStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusText) {
|
||||||
chatStatusText.text = statusText;
|
chatStatusText.text = statusText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,12 +151,16 @@ Page {
|
||||||
chatView.currentIndex = -1;
|
chatView.currentIndex = -1;
|
||||||
chatView.lastReadSentIndex = 0;
|
chatView.lastReadSentIndex = 0;
|
||||||
var chatType = chatInformation.type['@type'];
|
var chatType = chatInformation.type['@type'];
|
||||||
isPrivateChat = ( chatType === "chatTypePrivate" );
|
isPrivateChat = chatType === "chatTypePrivate";
|
||||||
|
isSecretChat = chatType === "chatTypeSecret";
|
||||||
isBasicGroup = ( chatType === "chatTypeBasicGroup" );
|
isBasicGroup = ( chatType === "chatTypeBasicGroup" );
|
||||||
isSuperGroup = ( chatType === "chatTypeSupergroup" );
|
isSuperGroup = ( chatType === "chatTypeSupergroup" );
|
||||||
if (isPrivateChat) {
|
if (isPrivateChat || isSecretChat) {
|
||||||
chatPartnerInformation = tdLibWrapper.getUserInformation(chatInformation.type.user_id);
|
chatPartnerInformation = tdLibWrapper.getUserInformation(chatInformation.type.user_id);
|
||||||
updateChatPartnerStatusText();
|
updateChatPartnerStatusText();
|
||||||
|
if (isSecretChat) {
|
||||||
|
tdLibWrapper.getSecretChat(chatInformation.type.secret_chat_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (isBasicGroup) {
|
else if (isBasicGroup) {
|
||||||
chatGroupInformation = tdLibWrapper.getBasicGroup(chatInformation.type.basic_group_id);
|
chatGroupInformation = tdLibWrapper.getBasicGroup(chatInformation.type.basic_group_id);
|
||||||
|
@ -291,11 +308,12 @@ Page {
|
||||||
|| groupStatusType === "chatMemberStatusAdministrator"
|
|| groupStatusType === "chatMemberStatusAdministrator"
|
||||||
|| groupStatusType === "chatMemberStatusCreator"
|
|| groupStatusType === "chatMemberStatusCreator"
|
||||||
|| (groupStatusType === "chatMemberStatusRestricted" && groupStatus.permissions[privilege])
|
|| (groupStatusType === "chatMemberStatusRestricted" && groupStatus.permissions[privilege])
|
||||||
|
|| (chatPage.isSecretChat && chatPage.isSecretChatReady)
|
||||||
}
|
}
|
||||||
function canPinMessages() {
|
function canPinMessages() {
|
||||||
Debug.log("Can we pin messages?");
|
Debug.log("Can we pin messages?");
|
||||||
if (chatPage.isPrivateChat) {
|
if (chatPage.isPrivateChat || chatPage.isSecretChat) {
|
||||||
Debug.log("Private Chat: No!");
|
Debug.log("Private/Secret Chat: No!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (chatPage.chatGroupInformation.status["@type"] === "chatMemberStatusCreator") {
|
if (chatPage.chatGroupInformation.status["@type"] === "chatMemberStatusCreator") {
|
||||||
|
@ -367,7 +385,7 @@ Page {
|
||||||
Connections {
|
Connections {
|
||||||
target: tdLibWrapper
|
target: tdLibWrapper
|
||||||
onUserUpdated: {
|
onUserUpdated: {
|
||||||
if (isPrivateChat && chatPartnerInformation.id.toString() === userId ) {
|
if ((isPrivateChat || isSecretChat) && chatPartnerInformation.id.toString() === userId ) {
|
||||||
chatPartnerInformation = userInformation;
|
chatPartnerInformation = userInformation;
|
||||||
updateChatPartnerStatusText();
|
updateChatPartnerStatusText();
|
||||||
}
|
}
|
||||||
|
@ -410,6 +428,22 @@ Page {
|
||||||
pinnedMessageItem.pinnedMessage = message;
|
pinnedMessageItem.pinnedMessage = message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onSecretChatReceived: {
|
||||||
|
if (secretChatId === chatInformation.type.secret_chat_id) {
|
||||||
|
Debug.log("[ChatPage] Received detailed information about this secret chat");
|
||||||
|
chatPage.secretChatDetails = secretChat;
|
||||||
|
updateChatPartnerStatusText();
|
||||||
|
chatPage.isSecretChatReady = chatPage.secretChatDetails.state["@type"] === "secretChatStateReady";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onSecretChatUpdated: {
|
||||||
|
if (secretChatId.toString() === chatInformation.type.secret_chat_id.toString()) {
|
||||||
|
Debug.log("[ChatPage] Detailed information about this secret chat was updated");
|
||||||
|
chatPage.secretChatDetails = secretChat;
|
||||||
|
updateChatPartnerStatusText();
|
||||||
|
chatPage.isSecretChatReady = chatPage.secretChatDetails.state["@type"] === "secretChatStateReady";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
@ -507,7 +541,7 @@ Page {
|
||||||
Timer {
|
Timer {
|
||||||
id: chatContactTimeUpdater
|
id: chatContactTimeUpdater
|
||||||
interval: 60000
|
interval: 60000
|
||||||
running: isPrivateChat
|
running: isPrivateChat || isSecretChat
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
updateChatPartnerStatusText();
|
updateChatPartnerStatusText();
|
||||||
|
@ -543,6 +577,19 @@ Page {
|
||||||
|
|
||||||
PullDownMenu {
|
PullDownMenu {
|
||||||
visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active && !messageOverlayLoader.active
|
visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active && !messageOverlayLoader.active
|
||||||
|
MenuItem {
|
||||||
|
id: closeSecretChatMenuItem
|
||||||
|
visible: chatPage.isSecretChat && chatPage.secretChatDetails.state["@type"] !== "secretChatStateClosed"
|
||||||
|
onClicked: {
|
||||||
|
var remorse = Remorse.popupAction(appWindow, qsTr("Closing chat"), (function(secretChatId) {
|
||||||
|
return function() {
|
||||||
|
tdLibWrapper.closeSecretChat(secretChatId);
|
||||||
|
};
|
||||||
|
}(chatPage.secretChatDetails.id)))
|
||||||
|
}
|
||||||
|
text: qsTr("Close Chat")
|
||||||
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
id: joinLeaveChatMenuItem
|
id: joinLeaveChatMenuItem
|
||||||
visible: (chatPage.isSuperGroup || chatPage.isBasicGroup) && chatGroupInformation && chatGroupInformation.status["@type"] !== "chatMemberStatusBanned"
|
visible: (chatPage.isSuperGroup || chatPage.isBasicGroup) && chatGroupInformation && chatGroupInformation.status["@type"] !== "chatMemberStatusBanned"
|
||||||
|
@ -604,23 +651,49 @@ Page {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
spacing: Theme.paddingMedium
|
spacing: Theme.paddingMedium
|
||||||
|
|
||||||
ProfileThumbnail {
|
Item {
|
||||||
id: chatPictureThumbnail
|
|
||||||
replacementStringHint: chatNameText.text
|
|
||||||
width: chatOverviewItem.height
|
width: chatOverviewItem.height
|
||||||
height: chatOverviewItem.height
|
height: chatOverviewItem.height
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.bottomMargin: chatPage.isPortrait ? Theme.paddingMedium : Theme.paddingSmall
|
anchors.bottomMargin: chatPage.isPortrait ? Theme.paddingMedium : Theme.paddingSmall
|
||||||
|
|
||||||
// Setting it directly may cause an stale state for the thumbnail in case the chat page
|
ProfileThumbnail {
|
||||||
// was previously loaded with a picture and now it doesn't have one. Instead setting it
|
id: chatPictureThumbnail
|
||||||
// when the ChatModel indicates a change. This also avoids flickering when the page is loaded...
|
replacementStringHint: chatNameText.text
|
||||||
Connections {
|
width: parent.height
|
||||||
target: chatModel
|
height: parent.height
|
||||||
onSmallPhotoChanged: {
|
|
||||||
chatPictureThumbnail.photoData = chatModel.smallPhoto;
|
// Setting it directly may cause an stale state for the thumbnail in case the chat page
|
||||||
|
// was previously loaded with a picture and now it doesn't have one. Instead setting it
|
||||||
|
// when the ChatModel indicates a change. This also avoids flickering when the page is loaded...
|
||||||
|
Connections {
|
||||||
|
target: chatModel
|
||||||
|
onSmallPhotoChanged: {
|
||||||
|
chatPictureThumbnail.photoData = chatModel.smallPhoto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: chatSecretBackground
|
||||||
|
color: Theme.overlayBackgroundColor
|
||||||
|
width: chatPage.isPortrait ? Theme.fontSizeLarge : Theme.fontSizeMedium
|
||||||
|
height: width
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
radius: parent.width / 2
|
||||||
|
visible: chatPage.isSecretChat
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: chatSecretImage
|
||||||
|
source: "image://theme/icon-s-secure"
|
||||||
|
width: chatPage.isPortrait ? Theme.fontSizeSmall : Theme.fontSizeExtraSmall
|
||||||
|
height: width
|
||||||
|
anchors.centerIn: chatSecretBackground
|
||||||
|
visible: chatPage.isSecretChat
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -855,8 +928,9 @@ Page {
|
||||||
VerticalScrollDecorator {}
|
VerticalScrollDecorator {}
|
||||||
|
|
||||||
ViewPlaceholder {
|
ViewPlaceholder {
|
||||||
|
id: chatViewPlaceholder
|
||||||
enabled: chatView.count === 0
|
enabled: chatView.count === 0
|
||||||
text: qsTr("This chat is empty.")
|
text: (chatPage.isSecretChat && !chatPage.isSecretChatReady) ? qsTr("This secret chat is not yet ready. Your chat partner needs to go online first.") : qsTr("This chat is empty.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,7 +1135,7 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IconButton {
|
IconButton {
|
||||||
visible: !chatPage.isPrivateChat && chatPage.hasSendPrivilege("can_send_polls")
|
visible: !(chatPage.isPrivateChat || chatPage.isSecretChat) && chatPage.hasSendPrivilege("can_send_polls")
|
||||||
icon.source: "image://theme/icon-m-question"
|
icon.source: "image://theme/icon-m-question"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
pageStack.push(Qt.resolvedUrl("../pages/PollCreationPage.qml"), { "chatId" : chatInformation.id, groupName: chatInformation.title});
|
pageStack.push(Qt.resolvedUrl("../pages/PollCreationPage.qml"), { "chatId" : chatInformation.id, groupName: chatInformation.title});
|
||||||
|
@ -1340,7 +1414,7 @@ Page {
|
||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
visible: selectedMessages.every(function(message){
|
visible: selectedMessages.every(function(message){
|
||||||
return message.can_be_forwarded
|
return message.can_be_forwarded && !chatPage.isSecretChat
|
||||||
})
|
})
|
||||||
width: visible ? Theme.itemSizeMedium : 0
|
width: visible ? Theme.itemSizeMedium : 0
|
||||||
icon.source: "image://theme/icon-m-forward"
|
icon.source: "image://theme/icon-m-forward"
|
||||||
|
|
392
qml/pages/NewChatPage.qml
Normal file
392
qml/pages/NewChatPage.qml
Normal file
|
@ -0,0 +1,392 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2020 Sebastian J. Wolf and other contributors
|
||||||
|
|
||||||
|
This file is part of Fernschreiber.
|
||||||
|
|
||||||
|
Fernschreiber is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Fernschreiber is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import QtQuick 2.6
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
import "../components"
|
||||||
|
import "../js/twemoji.js" as Emoji
|
||||||
|
import "../js/functions.js" as Functions
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: newChatPage
|
||||||
|
allowedOrientations: Orientation.All
|
||||||
|
|
||||||
|
property bool isLoading: true;
|
||||||
|
|
||||||
|
function resetFocus() {
|
||||||
|
contactsSearchField.focus = false;
|
||||||
|
newChatPage.focus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadContacts() {
|
||||||
|
contactsModel.hydrateContacts();
|
||||||
|
contactsListView.model = contactsProxyModel;
|
||||||
|
newChatPage.isLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === PageStatus.Active) {
|
||||||
|
reloadContacts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: tdLibWrapper
|
||||||
|
onContactsImported: {
|
||||||
|
reloadContacts();
|
||||||
|
appNotification.show(qsTr("Contacts successfully synchronized with Telegram."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SilicaFlickable {
|
||||||
|
id: newChatContainer
|
||||||
|
contentHeight: newChatPage.height
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
PullDownMenu {
|
||||||
|
visible: contactsModel.canSynchronizeContacts()
|
||||||
|
MenuItem {
|
||||||
|
onClicked: {
|
||||||
|
newChatPage.isLoading = true;
|
||||||
|
if (!contactsModel.synchronizeContacts()) {
|
||||||
|
reloadContacts();
|
||||||
|
appNotification.show(qsTr("Could not synchronize your contacts with Telegram."));
|
||||||
|
}
|
||||||
|
// Success message is not fired before TDLib returned "Contacts imported" (see above)
|
||||||
|
}
|
||||||
|
text: qsTr("Synchronize Contacts with Telegram")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: newChatPageColumn
|
||||||
|
width: newChatPage.width
|
||||||
|
height: newChatPage.height
|
||||||
|
|
||||||
|
PageHeader {
|
||||||
|
id: newChatPageHeader
|
||||||
|
title: qsTr("Your Contacts")
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contactsItem
|
||||||
|
|
||||||
|
width: newChatPageColumn.width
|
||||||
|
height: newChatPageColumn.height - newChatPageHeader.height
|
||||||
|
|
||||||
|
Column {
|
||||||
|
visible: !newChatPage.isLoading
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
SearchField {
|
||||||
|
id: contactsSearchField
|
||||||
|
width: parent.width
|
||||||
|
placeholderText: qsTr("Search a contact...")
|
||||||
|
active: !newChatPage.isLoading
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
contactsProxyModel.setFilterWildcard("*" + text + "*");
|
||||||
|
}
|
||||||
|
|
||||||
|
EnterKey.iconSource: "image://theme/icon-m-enter-close"
|
||||||
|
EnterKey.onClicked: {
|
||||||
|
resetFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SilicaListView {
|
||||||
|
id: contactsListView
|
||||||
|
clip: true
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - contactsSearchField.height
|
||||||
|
visible: !newChatPage.isLoading
|
||||||
|
opacity: visible ? 1 : 0
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
|
||||||
|
signal newChatInitiated ( int currentIndex )
|
||||||
|
|
||||||
|
ViewPlaceholder {
|
||||||
|
y: Theme.paddingLarge
|
||||||
|
enabled: contactsListView.count === 0
|
||||||
|
text: qsTr("You don't have any contacts.")
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: newChatListItem
|
||||||
|
width: parent.width
|
||||||
|
height: contactListItem.height
|
||||||
|
|
||||||
|
PhotoTextsListItem {
|
||||||
|
id: contactListItem
|
||||||
|
|
||||||
|
opacity: visible ? 1 : 0
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
|
||||||
|
pictureThumbnail {
|
||||||
|
photoData: typeof photo_small !== "undefined" ? photo_small : {}
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
primaryText.text: Emoji.emojify(title, primaryText.font.pixelSize, "../js/emoji/")
|
||||||
|
prologSecondaryText.text: "@" + ( username !== "" ? username : user_id )
|
||||||
|
tertiaryText {
|
||||||
|
maximumLineCount: 1
|
||||||
|
text: Functions.getChatPartnerStatusText(user_status, user_last_online);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
contactsListView.newChatInitiated(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: contactsListView
|
||||||
|
|
||||||
|
onNewChatInitiated: {
|
||||||
|
if (index === currentIndex) {
|
||||||
|
contactListItem.visible = false;
|
||||||
|
} else {
|
||||||
|
contactListItem.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: contactsSearchField
|
||||||
|
onFocusChanged: {
|
||||||
|
if (contactsSearchField.focus) {
|
||||||
|
contactListItem.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: selectChatTypeColumn
|
||||||
|
visible: !contactListItem.visible
|
||||||
|
opacity: visible ? 1 : 0
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
width: parent.width
|
||||||
|
height: contactListItem.height
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - chatTypeSeparator.height
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
opacity: Theme.opacityLow
|
||||||
|
color: Theme.overlayBackgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: privateChatItem
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width / 2
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: privateChatHighlightBackground
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Theme.highlightBackgroundColor
|
||||||
|
opacity: Theme.opacityHigh
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - ( 2 * Theme.paddingSmall )
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
id: privateChatButton
|
||||||
|
width: Theme.itemSizeLarge
|
||||||
|
height: Theme.itemSizeLarge
|
||||||
|
icon.source: "image://theme/icon-m-chat"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClicked: {
|
||||||
|
tdLibWrapper.createPrivateChat(display.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width - privateChatButton.width - Theme.horizontalPageMargin
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.paddingSmall
|
||||||
|
Text {
|
||||||
|
id: privateChatHeader
|
||||||
|
width: parent.width
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.ExtraBold
|
||||||
|
color: Theme.primaryColor
|
||||||
|
maximumLineCount: 1
|
||||||
|
elide: Text.ElideRight
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
text: qsTr("Private Chat")
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - privateChatHeader.height - Theme.paddingSmall
|
||||||
|
font.pixelSize: Theme.fontSizeTiny
|
||||||
|
color: Theme.secondaryColor
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
text: qsTr("Transport-encrypted, uses Telegram Cloud, sharable across devices")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
tdLibWrapper.createPrivateChat(display.id);
|
||||||
|
}
|
||||||
|
onPressed: {
|
||||||
|
privateChatHighlightBackground.visible = true;
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
privateChatHighlightBackground.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: secretChatItem
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width / 2
|
||||||
|
anchors.left: privateChatItem.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: secretChatHighlightBackground
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Theme.highlightBackgroundColor
|
||||||
|
opacity: Theme.opacityHigh
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - ( 2 * Theme.paddingSmall )
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
id: secretChatButton
|
||||||
|
width: Theme.itemSizeLarge
|
||||||
|
height: Theme.itemSizeLarge
|
||||||
|
icon.source: "image://theme/icon-m-device-lock"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClicked: {
|
||||||
|
tdLibWrapper.createNewSecretChat(display.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width - secretChatButton.width - Theme.horizontalPageMargin
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.paddingSmall
|
||||||
|
Text {
|
||||||
|
width: parent.width
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.ExtraBold
|
||||||
|
color: Theme.primaryColor
|
||||||
|
maximumLineCount: 1
|
||||||
|
elide: Text.ElideRight
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
text: qsTr("Secret Chat")
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - privateChatHeader.height - Theme.paddingSmall
|
||||||
|
font.pixelSize: Theme.fontSizeTiny
|
||||||
|
color: Theme.secondaryColor
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
text: qsTr("End-to-end-encrypted, accessible on this device only")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
tdLibWrapper.createNewSecretChat(display.id);
|
||||||
|
}
|
||||||
|
onPressed: {
|
||||||
|
secretChatHighlightBackground.visible = true;
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
secretChatHighlightBackground.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
id: chatTypeSeparator
|
||||||
|
width: parent.width
|
||||||
|
color: Theme.primaryColor
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalScrollDecorator {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
|
||||||
|
opacity: visible ? 1 : 0
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
visible: newChatPage.isLoading
|
||||||
|
width: parent.width
|
||||||
|
height: loadingLabel.height + loadingBusyIndicator.height + Theme.paddingMedium
|
||||||
|
|
||||||
|
spacing: Theme.paddingMedium
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
InfoLabel {
|
||||||
|
id: loadingLabel
|
||||||
|
text: qsTr("Loading contacts...")
|
||||||
|
}
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
id: loadingBusyIndicator
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
running: newChatPage.isLoading
|
||||||
|
size: BusyIndicatorSize.Large
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -105,6 +105,7 @@ Page {
|
||||||
tdLibWrapper.getChats();
|
tdLibWrapper.getChats();
|
||||||
tdLibWrapper.getRecentStickers();
|
tdLibWrapper.getRecentStickers();
|
||||||
tdLibWrapper.getInstalledStickerSets();
|
tdLibWrapper.getInstalledStickerSets();
|
||||||
|
tdLibWrapper.getContacts();
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializePage() {
|
function initializePage() {
|
||||||
|
@ -197,6 +198,10 @@ Page {
|
||||||
text: qsTr("Settings")
|
text: qsTr("Settings")
|
||||||
onClicked: pageStack.push(Qt.resolvedUrl("../pages/SettingsPage.qml"))
|
onClicked: pageStack.push(Qt.resolvedUrl("../pages/SettingsPage.qml"))
|
||||||
}
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: qsTr("New Chat")
|
||||||
|
onClicked: pageStack.push(Qt.resolvedUrl("../pages/NewChatPage.qml"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
|
|
@ -46,6 +46,7 @@ namespace {
|
||||||
const QString IS_CHANNEL("is_channel");
|
const QString IS_CHANNEL("is_channel");
|
||||||
const QString PINNED_MESSAGE_ID("pinned_message_id");
|
const QString PINNED_MESSAGE_ID("pinned_message_id");
|
||||||
const QString _TYPE("@type");
|
const QString _TYPE("@type");
|
||||||
|
const QString SECRET_CHAT_ID("secret_chat_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatListModel::ChatData
|
class ChatListModel::ChatData
|
||||||
|
@ -64,6 +65,7 @@ public:
|
||||||
RoleLastMessageText,
|
RoleLastMessageText,
|
||||||
RoleLastMessageStatus,
|
RoleLastMessageStatus,
|
||||||
RoleChatMemberStatus,
|
RoleChatMemberStatus,
|
||||||
|
RoleSecretChatState,
|
||||||
RoleIsChannel
|
RoleIsChannel
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,6 +88,7 @@ public:
|
||||||
bool updateLastReadInboxMessageId(qlonglong messageId);
|
bool updateLastReadInboxMessageId(qlonglong messageId);
|
||||||
QVector<int> updateLastMessage(const QVariantMap &message);
|
QVector<int> updateLastMessage(const QVariantMap &message);
|
||||||
QVector<int> updateGroup(const TDLibWrapper::Group *group);
|
QVector<int> updateGroup(const TDLibWrapper::Group *group);
|
||||||
|
QVector<int> updateSecretChat(const QVariantMap &secretChatDetails);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QVariantMap chatData;
|
QVariantMap chatData;
|
||||||
|
@ -94,6 +97,7 @@ public:
|
||||||
qlonglong groupId;
|
qlonglong groupId;
|
||||||
TDLibWrapper::ChatType chatType;
|
TDLibWrapper::ChatType chatType;
|
||||||
TDLibWrapper::ChatMemberStatus memberStatus;
|
TDLibWrapper::ChatMemberStatus memberStatus;
|
||||||
|
TDLibWrapper::SecretChatState secretChatState;
|
||||||
QVariantMap userInformation;
|
QVariantMap userInformation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -103,6 +107,7 @@ ChatListModel::ChatData::ChatData(const QVariantMap &data, const QVariantMap &us
|
||||||
order(data.value(ORDER).toLongLong()),
|
order(data.value(ORDER).toLongLong()),
|
||||||
groupId(0),
|
groupId(0),
|
||||||
memberStatus(TDLibWrapper::ChatMemberStatusUnknown),
|
memberStatus(TDLibWrapper::ChatMemberStatusUnknown),
|
||||||
|
secretChatState(TDLibWrapper::SecretChatStateUnknown),
|
||||||
userInformation(userInfo)
|
userInformation(userInfo)
|
||||||
{
|
{
|
||||||
const QVariantMap type(data.value(TYPE).toMap());
|
const QVariantMap type(data.value(TYPE).toMap());
|
||||||
|
@ -227,7 +232,11 @@ bool ChatListModel::ChatData::isHidden() const
|
||||||
break;
|
break;
|
||||||
case TDLibWrapper::ChatTypeUnknown:
|
case TDLibWrapper::ChatTypeUnknown:
|
||||||
case TDLibWrapper::ChatTypePrivate:
|
case TDLibWrapper::ChatTypePrivate:
|
||||||
|
break;
|
||||||
case TDLibWrapper::ChatTypeSecret:
|
case TDLibWrapper::ChatTypeSecret:
|
||||||
|
if (secretChatState == TDLibWrapper::SecretChatStateClosed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -288,6 +297,18 @@ QVector<int> ChatListModel::ChatData::updateGroup(const TDLibWrapper::Group *gro
|
||||||
return changedRoles;
|
return changedRoles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<int> ChatListModel::ChatData::updateSecretChat(const QVariantMap &secretChatDetails)
|
||||||
|
{
|
||||||
|
QVector<int> changedRoles;
|
||||||
|
|
||||||
|
TDLibWrapper::SecretChatState newSecretChatState = TDLibWrapper::secretChatStateFromString(secretChatDetails.value("state").toMap().value(_TYPE).toString());
|
||||||
|
if (newSecretChatState != secretChatState) {
|
||||||
|
secretChatState = newSecretChatState;
|
||||||
|
changedRoles.append(RoleSecretChatState);
|
||||||
|
}
|
||||||
|
return changedRoles;
|
||||||
|
}
|
||||||
|
|
||||||
ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper) : showHiddenChats(false)
|
ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper) : showHiddenChats(false)
|
||||||
{
|
{
|
||||||
this->tdLibWrapper = tdLibWrapper;
|
this->tdLibWrapper = tdLibWrapper;
|
||||||
|
@ -302,6 +323,9 @@ ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper) : showHiddenChats(false
|
||||||
connect(tdLibWrapper, SIGNAL(chatNotificationSettingsUpdated(QString, QVariantMap)), this, SLOT(handleChatNotificationSettingsUpdated(QString, QVariantMap)));
|
connect(tdLibWrapper, SIGNAL(chatNotificationSettingsUpdated(QString, QVariantMap)), this, SLOT(handleChatNotificationSettingsUpdated(QString, QVariantMap)));
|
||||||
connect(tdLibWrapper, SIGNAL(superGroupUpdated(qlonglong)), this, SLOT(handleGroupUpdated(qlonglong)));
|
connect(tdLibWrapper, SIGNAL(superGroupUpdated(qlonglong)), this, SLOT(handleGroupUpdated(qlonglong)));
|
||||||
connect(tdLibWrapper, SIGNAL(basicGroupUpdated(qlonglong)), this, SLOT(handleGroupUpdated(qlonglong)));
|
connect(tdLibWrapper, SIGNAL(basicGroupUpdated(qlonglong)), this, SLOT(handleGroupUpdated(qlonglong)));
|
||||||
|
connect(tdLibWrapper, SIGNAL(secretChatUpdated(qlonglong, QVariantMap)), this, SLOT(handleSecretChatUpdated(qlonglong, QVariantMap)));
|
||||||
|
connect(tdLibWrapper, SIGNAL(secretChatReceived(qlonglong, QVariantMap)), this, SLOT(handleSecretChatUpdated(qlonglong, QVariantMap)));
|
||||||
|
connect(tdLibWrapper, SIGNAL(chatTitleUpdated(QString, QString)), this, SLOT(handleChatTitleUpdated(QString, QString)));
|
||||||
|
|
||||||
// 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);
|
||||||
|
@ -332,6 +356,7 @@ QHash<int,QByteArray> ChatListModel::roleNames() const
|
||||||
roles.insert(ChatData::RoleLastMessageText, "last_message_text");
|
roles.insert(ChatData::RoleLastMessageText, "last_message_text");
|
||||||
roles.insert(ChatData::RoleLastMessageStatus, "last_message_status");
|
roles.insert(ChatData::RoleLastMessageStatus, "last_message_status");
|
||||||
roles.insert(ChatData::RoleChatMemberStatus, "chat_member_status");
|
roles.insert(ChatData::RoleChatMemberStatus, "chat_member_status");
|
||||||
|
roles.insert(ChatData::RoleSecretChatState, "secret_chat_state");
|
||||||
roles.insert(ChatData::RoleIsChannel, "is_channel");
|
roles.insert(ChatData::RoleIsChannel, "is_channel");
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
@ -359,6 +384,7 @@ QVariant ChatListModel::data(const QModelIndex &index, int role) const
|
||||||
case ChatData::RoleLastMessageDate: return data->senderMessageDate();
|
case ChatData::RoleLastMessageDate: return data->senderMessageDate();
|
||||||
case ChatData::RoleLastMessageStatus: return data->senderMessageStatus();
|
case ChatData::RoleLastMessageStatus: return data->senderMessageStatus();
|
||||||
case ChatData::RoleChatMemberStatus: return data->memberStatus;
|
case ChatData::RoleChatMemberStatus: return data->memberStatus;
|
||||||
|
case ChatData::RoleSecretChatState: return data->secretChatState;
|
||||||
case ChatData::RoleIsChannel: return data->isChannel();
|
case ChatData::RoleIsChannel: return data->isChannel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -492,6 +518,38 @@ void ChatListModel::updateChatVisibility(const TDLibWrapper::Group *group)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatListModel::updateSecretChatVisibility(const QVariantMap secretChatDetails)
|
||||||
|
{
|
||||||
|
LOG("Updating secret chat visibility" << secretChatDetails.value(ID).toString());
|
||||||
|
// See if any secret chat has been closed
|
||||||
|
for (int i = 0; i < chatList.size(); i++) {
|
||||||
|
ChatData *chat = chatList.at(i);
|
||||||
|
if (chat->chatType != TDLibWrapper::ChatTypeSecret) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (chat->chatData.value(TYPE).toMap().value(SECRET_CHAT_ID).toLongLong() != secretChatDetails.value(ID).toLongLong()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QVector<int> changedRoles(chat->updateSecretChat(secretChatDetails));
|
||||||
|
if (chat->isHidden() && !showHiddenChats) {
|
||||||
|
LOG("Hiding chat" << chat->chatId << "at" << i);
|
||||||
|
beginRemoveRows(QModelIndex(), i, i);
|
||||||
|
chatList.removeAt(i);
|
||||||
|
// Update damaged part of the map
|
||||||
|
const int n = chatList.size();
|
||||||
|
for (int pos = i; pos < n; pos++) {
|
||||||
|
chatIndexMap.insert(chatList.at(pos)->chatId, pos);
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
hiddenChats.insert(chat->chatId, chat);
|
||||||
|
endRemoveRows();
|
||||||
|
} else if (!changedRoles.isEmpty()) {
|
||||||
|
const QModelIndex modelIndex(index(i));
|
||||||
|
emit dataChanged(modelIndex, modelIndex, changedRoles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ChatListModel::showAllChats() const
|
bool ChatListModel::showAllChats() const
|
||||||
{
|
{
|
||||||
return showHiddenChats;
|
return showHiddenChats;
|
||||||
|
@ -509,10 +567,19 @@ void ChatListModel::setShowAllChats(bool showAll)
|
||||||
void ChatListModel::handleChatDiscovered(const QString &, const QVariantMap &chatToBeAdded)
|
void ChatListModel::handleChatDiscovered(const QString &, const QVariantMap &chatToBeAdded)
|
||||||
{
|
{
|
||||||
ChatData *chat = new ChatData(chatToBeAdded, tdLibWrapper->getUserInformation());
|
ChatData *chat = new ChatData(chatToBeAdded, tdLibWrapper->getUserInformation());
|
||||||
|
|
||||||
const TDLibWrapper::Group *group = tdLibWrapper->getGroup(chat->groupId);
|
const TDLibWrapper::Group *group = tdLibWrapper->getGroup(chat->groupId);
|
||||||
if (group) {
|
if (group) {
|
||||||
chat->updateGroup(group);
|
chat->updateGroup(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chat->chatType == TDLibWrapper::ChatTypeSecret) {
|
||||||
|
QVariantMap secretChatDetails = tdLibWrapper->getSecretChatFromCache(chatToBeAdded.value(TYPE).toMap().value(SECRET_CHAT_ID).toLongLong());
|
||||||
|
if (!secretChatDetails.isEmpty()) {
|
||||||
|
chat->updateSecretChat(secretChatDetails);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (chat->isHidden()) {
|
if (chat->isHidden()) {
|
||||||
LOG("Hidden chat" << chat->chatId);
|
LOG("Hidden chat" << chat->chatId);
|
||||||
hiddenChats.insert(chat->chatId, chat);
|
hiddenChats.insert(chat->chatId, chat);
|
||||||
|
@ -707,6 +774,33 @@ void ChatListModel::handleGroupUpdated(qlonglong groupId)
|
||||||
updateChatVisibility(tdLibWrapper->getGroup(groupId));
|
updateChatVisibility(tdLibWrapper->getGroup(groupId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatListModel::handleSecretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat)
|
||||||
|
{
|
||||||
|
LOG("Updating visibility of secret chat " << secretChatId);
|
||||||
|
updateSecretChatVisibility(secretChat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatListModel::handleChatTitleUpdated(const QString &chatId, const QString &title)
|
||||||
|
{
|
||||||
|
qlonglong chatIdLongLong = chatId.toLongLong();
|
||||||
|
if (chatIndexMap.contains(chatIdLongLong)) {
|
||||||
|
LOG("Updating title for" << chatId);
|
||||||
|
const int chatIndex = chatIndexMap.value(chatIdLongLong);
|
||||||
|
ChatData *chat = chatList.at(chatIndex);
|
||||||
|
chat->chatData.insert(TITLE, title);
|
||||||
|
QVector<int> changedRoles;
|
||||||
|
changedRoles.append(ChatData::RoleTitle);
|
||||||
|
const QModelIndex modelIndex(index(chatIndex));
|
||||||
|
emit dataChanged(modelIndex, modelIndex, changedRoles);
|
||||||
|
} else {
|
||||||
|
ChatData *chat = hiddenChats.value(chatId.toLongLong());
|
||||||
|
if (chat) {
|
||||||
|
LOG("Updating title for hidden chat" << chatId);
|
||||||
|
chat->chatData.insert(TITLE, title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ChatListModel::handleRelativeTimeRefreshTimer()
|
void ChatListModel::handleRelativeTimeRefreshTimer()
|
||||||
{
|
{
|
||||||
LOG("Refreshing timestamps");
|
LOG("Refreshing timestamps");
|
||||||
|
|
|
@ -54,6 +54,8 @@ private slots:
|
||||||
void handleMessageSendSucceeded(const QString &messageId, const QString &oldMessageId, const QVariantMap &message);
|
void handleMessageSendSucceeded(const QString &messageId, const QString &oldMessageId, const QVariantMap &message);
|
||||||
void handleChatNotificationSettingsUpdated(const QString &chatId, const QVariantMap &chatNotificationSettings);
|
void handleChatNotificationSettingsUpdated(const QString &chatId, const QVariantMap &chatNotificationSettings);
|
||||||
void handleGroupUpdated(qlonglong groupId);
|
void handleGroupUpdated(qlonglong groupId);
|
||||||
|
void handleSecretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat);
|
||||||
|
void handleChatTitleUpdated(const QString &chatId, const QString &title);
|
||||||
void handleRelativeTimeRefreshTimer();
|
void handleRelativeTimeRefreshTimer();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -65,6 +67,7 @@ private:
|
||||||
class ChatData;
|
class ChatData;
|
||||||
void addVisibleChat(ChatData *chat);
|
void addVisibleChat(ChatData *chat);
|
||||||
void updateChatVisibility(const TDLibWrapper::Group *group);
|
void updateChatVisibility(const TDLibWrapper::Group *group);
|
||||||
|
void updateSecretChatVisibility(const QVariantMap secretChatDetails);
|
||||||
int updateChatOrder(int chatIndex);
|
int updateChatOrder(int chatIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
184
src/contactsmodel.cpp
Normal file
184
src/contactsmodel.cpp
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2020 Sebastian J. Wolf and other contributors
|
||||||
|
|
||||||
|
This file is part of Fernschreiber.
|
||||||
|
|
||||||
|
Fernschreiber is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Fernschreiber is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "contactsmodel.h"
|
||||||
|
#include <QListIterator>
|
||||||
|
|
||||||
|
#define DEBUG_MODULE ContactsModel
|
||||||
|
#include "debuglog.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const QString STATUS("status");
|
||||||
|
const QString ID("id");
|
||||||
|
const QString TYPE("type");
|
||||||
|
const QString LAST_NAME("last_name");
|
||||||
|
const QString FIRST_NAME("first_name");
|
||||||
|
const QString USERNAME("username");
|
||||||
|
const QString _TYPE("@type");
|
||||||
|
const QString _EXTRA("@extra");
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactsModel::ContactsModel(TDLibWrapper *tdLibWrapper, QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
this->tdLibWrapper = tdLibWrapper;
|
||||||
|
connect(this->tdLibWrapper, SIGNAL(usersReceived(QString, QVariantList, int)), this, SLOT(handleUsersReceived(QString, QVariantList, int)));
|
||||||
|
|
||||||
|
this->deviceContactsDatabase = QSqlDatabase::addDatabase("QSQLITE", "contacts");
|
||||||
|
this->deviceContactsDatabase.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.local/share/system/Contacts/qtcontacts-sqlite/contacts.db");
|
||||||
|
if (this->deviceContactsDatabase.open()) {
|
||||||
|
LOG("Device's contacts database successfully opened :)");
|
||||||
|
this->canUseDeviceContacts = true;
|
||||||
|
} else {
|
||||||
|
LOG("Error opening device's contacts database :(");
|
||||||
|
this->canUseDeviceContacts = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> ContactsModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int,QByteArray> roles;
|
||||||
|
roles.insert(ContactRole::RoleDisplay, "display");
|
||||||
|
roles.insert(ContactRole::RoleTitle, "title");
|
||||||
|
roles.insert(ContactRole::RoleUserId, "user_id");
|
||||||
|
roles.insert(ContactRole::RoleUsername, "username");
|
||||||
|
roles.insert(ContactRole::RolePhotoSmall, "photo_small");
|
||||||
|
roles.insert(ContactRole::RoleUserStatus, "user_status");
|
||||||
|
roles.insert(ContactRole::RoleUserLastOnline, "user_last_online");
|
||||||
|
roles.insert(ContactRole::RoleFilter, "filter");
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContactsModel::rowCount(const QModelIndex &) const
|
||||||
|
{
|
||||||
|
return this->contacts.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ContactsModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (index.isValid()) {
|
||||||
|
QVariantMap requestedContact = contacts.value(index.row()).toMap();
|
||||||
|
switch (static_cast<ContactRole>(role)) {
|
||||||
|
case ContactRole::RoleDisplay: return requestedContact;
|
||||||
|
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::RoleUsername: return requestedContact.value("username");
|
||||||
|
case ContactRole::RolePhotoSmall: return requestedContact.value("profile_photo").toMap().value("small");
|
||||||
|
case ContactRole::RoleUserStatus: return requestedContact.value("status").toMap().value("@type");
|
||||||
|
case ContactRole::RoleUserLastOnline: return requestedContact.value("status").toMap().value("was_online");
|
||||||
|
case ContactRole::RoleFilter: return QString(requestedContact.value("first_name").toString() + " " + requestedContact.value("last_name").toString() + " " + requestedContact.value("username").toString()).trimmed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContactsModel::handleUsersReceived(const QString &extra, const QVariantList &userIds, int totalUsers)
|
||||||
|
{
|
||||||
|
if (extra == "contactsRequested") {
|
||||||
|
LOG("Received contacts list..." << totalUsers);
|
||||||
|
this->contactIds.clear();
|
||||||
|
QListIterator<QVariant> userIdIterator(userIds);
|
||||||
|
while (userIdIterator.hasNext()) {
|
||||||
|
QString nextUserId = userIdIterator.next().toString();
|
||||||
|
if (!this->tdLibWrapper->hasUserInformation(nextUserId)) {
|
||||||
|
this->tdLibWrapper->getUserFullInfo(nextUserId);
|
||||||
|
}
|
||||||
|
this->contactIds.append(nextUserId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool compareUsers(const QVariant &user1, const QVariant &user2)
|
||||||
|
{
|
||||||
|
const QVariantMap userMap1 = user1.toMap();
|
||||||
|
const QVariantMap userMap2 = user2.toMap();
|
||||||
|
|
||||||
|
const QString lastName1 = userMap1.value(LAST_NAME).toString();
|
||||||
|
const QString lastName2 = userMap2.value(LAST_NAME).toString();
|
||||||
|
if (!lastName1.isEmpty()) {
|
||||||
|
if (lastName1 < lastName2) {
|
||||||
|
return true;
|
||||||
|
} else if (lastName1 > lastName2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString firstName1 = userMap1.value(FIRST_NAME).toString();
|
||||||
|
const QString firstName2 = userMap2.value(FIRST_NAME).toString();
|
||||||
|
if (firstName1 < firstName2) {
|
||||||
|
return true;
|
||||||
|
} else if (firstName1 > firstName2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const QString userName1 = userMap1.value(USERNAME).toString();
|
||||||
|
const QString userName2 = userMap2.value(USERNAME).toString();
|
||||||
|
if (userName1 < userName2) {
|
||||||
|
return true;
|
||||||
|
} else if (userName1 > userName2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return userMap1.value(ID).toLongLong() < userMap2.value(ID).toLongLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContactsModel::hydrateContacts()
|
||||||
|
{
|
||||||
|
LOG("Hydrating contacts...");
|
||||||
|
this->contacts.clear();
|
||||||
|
QListIterator<QString> userIdIterator(contactIds);
|
||||||
|
while (userIdIterator.hasNext()) {
|
||||||
|
QString nextUserId = userIdIterator.next();
|
||||||
|
LOG("Hydrating contact:" << nextUserId);
|
||||||
|
this->contacts.append(this->tdLibWrapper->getUserInformation(nextUserId));
|
||||||
|
}
|
||||||
|
LOG("Hydrated contacts:" << this->contacts.size());
|
||||||
|
std::sort(this->contacts.begin(), this->contacts.end(), compareUsers);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContactsModel::synchronizeContacts()
|
||||||
|
{
|
||||||
|
LOG("Synchronizing device contacts");
|
||||||
|
QVariantList deviceContacts;
|
||||||
|
QSqlQuery databaseQuery(this->deviceContactsDatabase);
|
||||||
|
databaseQuery.prepare("select distinct c.contactId, c.firstName, c.lastName, n.phoneNumber from Contacts as c inner join PhoneNumbers as n on c.contactId = n.contactId where n.phoneNumber is not null and ( c.firstName is not null or c.lastName is not null );");
|
||||||
|
if (databaseQuery.exec()) {
|
||||||
|
LOG("Device contacts successfully selected from database!");
|
||||||
|
while (databaseQuery.next()) {
|
||||||
|
QVariantMap singleContact;
|
||||||
|
singleContact.insert("first_name", databaseQuery.value(1).toString());
|
||||||
|
singleContact.insert("last_name", databaseQuery.value(2).toString());
|
||||||
|
singleContact.insert("phone_number", databaseQuery.value(3).toString());
|
||||||
|
deviceContacts.append(singleContact);
|
||||||
|
LOG("Found contact" << singleContact.value("first_name").toString() << singleContact.value("last_name").toString() << singleContact.value("phone_number").toString());
|
||||||
|
}
|
||||||
|
if (!deviceContacts.isEmpty()) {
|
||||||
|
LOG("Importing found contacts" << deviceContacts.size());
|
||||||
|
this->tdLibWrapper->importContacts(deviceContacts);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
LOG("Error selecting contacts from database!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContactsModel::canSynchronizeContacts()
|
||||||
|
{
|
||||||
|
return this->canUseDeviceContacts;
|
||||||
|
}
|
68
src/contactsmodel.h
Normal file
68
src/contactsmodel.h
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2020 Sebastian J. Wolf and other contributors
|
||||||
|
|
||||||
|
This file is part of Fernschreiber.
|
||||||
|
|
||||||
|
Fernschreiber is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Fernschreiber is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONTACTSMODEL_H
|
||||||
|
#define CONTACTSMODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QVariantList>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
|
||||||
|
#include "tdlibwrapper.h"
|
||||||
|
|
||||||
|
class ContactsModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum ContactRole {
|
||||||
|
RoleDisplay = Qt::DisplayRole,
|
||||||
|
RolePhotoSmall,
|
||||||
|
RoleTitle,
|
||||||
|
RoleUserId,
|
||||||
|
RoleUsername,
|
||||||
|
RoleUserStatus,
|
||||||
|
RoleUserLastOnline,
|
||||||
|
RoleFilter
|
||||||
|
};
|
||||||
|
|
||||||
|
ContactsModel(TDLibWrapper *tdLibWrapper, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
virtual QHash<int,QByteArray> roleNames() const override;
|
||||||
|
virtual int rowCount(const QModelIndex &) const override;
|
||||||
|
virtual QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
|
||||||
|
Q_INVOKABLE void hydrateContacts();
|
||||||
|
Q_INVOKABLE bool synchronizeContacts();
|
||||||
|
Q_INVOKABLE bool canSynchronizeContacts();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void handleUsersReceived(const QString &extra, const QVariantList &userIds, int totalUsers);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TDLibWrapper *tdLibWrapper;
|
||||||
|
QVariantList contacts;
|
||||||
|
QList<QString> contactIds;
|
||||||
|
QString filter;
|
||||||
|
QSqlDatabase deviceContactsDatabase;
|
||||||
|
bool canUseDeviceContacts;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONTACTSMODEL_H
|
|
@ -10,6 +10,10 @@ FernschreiberUtils::FernschreiberUtils(QObject *parent) : QObject(parent)
|
||||||
|
|
||||||
QString FernschreiberUtils::getMessageShortText(const QVariantMap &messageContent, const bool &myself)
|
QString FernschreiberUtils::getMessageShortText(const QVariantMap &messageContent, const bool &myself)
|
||||||
{
|
{
|
||||||
|
if (messageContent.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
QString contentType = messageContent.value("@type").toString();
|
QString contentType = messageContent.value("@type").toString();
|
||||||
|
|
||||||
if (contentType == "messageText") {
|
if (contentType == "messageText") {
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "stickermanager.h"
|
#include "stickermanager.h"
|
||||||
#include "tgsplugin.h"
|
#include "tgsplugin.h"
|
||||||
#include "fernschreiberutils.h"
|
#include "fernschreiberutils.h"
|
||||||
|
#include "contactsmodel.h"
|
||||||
|
|
||||||
// The default filter can be overridden by QT_LOGGING_RULES envinronment variable, e.g.
|
// The default filter can be overridden by QT_LOGGING_RULES envinronment variable, e.g.
|
||||||
// QT_LOGGING_RULES="fernschreiber.*=true" harbour-fernschreiber
|
// QT_LOGGING_RULES="fernschreiber.*=true" harbour-fernschreiber
|
||||||
|
@ -96,6 +97,14 @@ int main(int argc, char *argv[])
|
||||||
StickerManager stickerManager(tdLibWrapper);
|
StickerManager stickerManager(tdLibWrapper);
|
||||||
context->setContextProperty("stickerManager", &stickerManager);
|
context->setContextProperty("stickerManager", &stickerManager);
|
||||||
|
|
||||||
|
ContactsModel contactsModel(tdLibWrapper, view.data());
|
||||||
|
context->setContextProperty("contactsModel", &contactsModel);
|
||||||
|
QSortFilterProxyModel contactsProxyModel(view.data());
|
||||||
|
contactsProxyModel.setSourceModel(&contactsModel);
|
||||||
|
contactsProxyModel.setFilterRole(ContactsModel::RoleFilter);
|
||||||
|
contactsProxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||||
|
context->setContextProperty("contactsProxyModel", &contactsProxyModel);
|
||||||
|
|
||||||
view->setSource(SailfishApp::pathTo("qml/harbour-fernschreiber.qml"));
|
view->setSource(SailfishApp::pathTo("qml/harbour-fernschreiber.qml"));
|
||||||
view->show();
|
view->show();
|
||||||
return app->exec();
|
return app->exec();
|
||||||
|
|
|
@ -44,6 +44,7 @@ namespace {
|
||||||
const QString UNREAD_COUNT("unread_count");
|
const QString UNREAD_COUNT("unread_count");
|
||||||
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");
|
||||||
|
const QString SECRET_CHAT("secret_chat");
|
||||||
|
|
||||||
const QString TYPE("@type");
|
const QString TYPE("@type");
|
||||||
const QString EXTRA("@extra");
|
const QString EXTRA("@extra");
|
||||||
|
@ -126,6 +127,9 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren
|
||||||
handlers.insert("users", &TDLibReceiver::processUsers);
|
handlers.insert("users", &TDLibReceiver::processUsers);
|
||||||
handlers.insert("error", &TDLibReceiver::processError);
|
handlers.insert("error", &TDLibReceiver::processError);
|
||||||
handlers.insert("ok", &TDLibReceiver::nop);
|
handlers.insert("ok", &TDLibReceiver::nop);
|
||||||
|
handlers.insert("secretChat", &TDLibReceiver::processSecretChat);
|
||||||
|
handlers.insert("updateSecretChat", &TDLibReceiver::processUpdateSecretChat);
|
||||||
|
handlers.insert("importedContacts", &TDLibReceiver::processImportedContacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TDLibReceiver::setActive(bool active)
|
void TDLibReceiver::setActive(bool active)
|
||||||
|
@ -523,3 +527,22 @@ void TDLibReceiver::processError(const QVariantMap &receivedInformation)
|
||||||
void TDLibReceiver::nop(const QVariantMap &)
|
void TDLibReceiver::nop(const QVariantMap &)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TDLibReceiver::processSecretChat(const QVariantMap &receivedInformation)
|
||||||
|
{
|
||||||
|
LOG("Received a secret chat");
|
||||||
|
emit secretChat(receivedInformation.value(ID).toLongLong(), receivedInformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TDLibReceiver::processUpdateSecretChat(const QVariantMap &receivedInformation)
|
||||||
|
{
|
||||||
|
LOG("A secret chat was updated");
|
||||||
|
QVariantMap updatedSecretChat = receivedInformation.value(SECRET_CHAT).toMap();
|
||||||
|
emit secretChatUpdated(updatedSecretChat.value(ID).toLongLong(), updatedSecretChat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TDLibReceiver::processImportedContacts(const QVariantMap &receivedInformation)
|
||||||
|
{
|
||||||
|
LOG("Contacts were imported");
|
||||||
|
emit contactsImported(receivedInformation.value("importer_count").toList(), receivedInformation.value("user_ids").toList());
|
||||||
|
}
|
||||||
|
|
|
@ -85,6 +85,9 @@ signals:
|
||||||
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 errorReceived(const int code, const QString &message);
|
void errorReceived(const int code, const QString &message);
|
||||||
|
void secretChat(qlonglong secretChatId, const QVariantMap &secretChat);
|
||||||
|
void secretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat);
|
||||||
|
void contactsImported(const QVariantList &importerCount, const QVariantList &userIds);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef void (TDLibReceiver::*Handler)(const QVariantMap &);
|
typedef void (TDLibReceiver::*Handler)(const QVariantMap &);
|
||||||
|
@ -145,6 +148,9 @@ private:
|
||||||
void processUsers(const QVariantMap &receivedInformation);
|
void processUsers(const QVariantMap &receivedInformation);
|
||||||
void processError(const QVariantMap &receivedInformation);
|
void processError(const QVariantMap &receivedInformation);
|
||||||
void nop(const QVariantMap &receivedInformation);
|
void nop(const QVariantMap &receivedInformation);
|
||||||
|
void processSecretChat(const QVariantMap &receivedInformation);
|
||||||
|
void processUpdateSecretChat(const QVariantMap &receivedInformation);
|
||||||
|
void processImportedContacts(const QVariantMap &receivedInformation);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TDLIBRECEIVER_H
|
#endif // TDLIBRECEIVER_H
|
||||||
|
|
|
@ -37,8 +37,10 @@ namespace {
|
||||||
const QString STATUS("status");
|
const QString STATUS("status");
|
||||||
const QString ID("id");
|
const QString ID("id");
|
||||||
const QString TYPE("type");
|
const QString TYPE("type");
|
||||||
|
const QString LAST_NAME("last_name");
|
||||||
|
const QString FIRST_NAME("first_name");
|
||||||
|
const QString USERNAME("username");
|
||||||
const QString VALUE("value");
|
const QString VALUE("value");
|
||||||
|
|
||||||
const QString _TYPE("@type");
|
const QString _TYPE("@type");
|
||||||
const QString _EXTRA("@extra");
|
const QString _EXTRA("@extra");
|
||||||
}
|
}
|
||||||
|
@ -94,6 +96,8 @@ TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface,
|
||||||
connect(this->tdLibReceiver, SIGNAL(messagesDeleted(QString, QVariantList)), this, SIGNAL(messagesDeleted(QString, QVariantList)));
|
connect(this->tdLibReceiver, SIGNAL(messagesDeleted(QString, QVariantList)), this, SIGNAL(messagesDeleted(QString, QVariantList)));
|
||||||
connect(this->tdLibReceiver, SIGNAL(chats(QVariantMap)), this, SIGNAL(chatsReceived(QVariantMap)));
|
connect(this->tdLibReceiver, SIGNAL(chats(QVariantMap)), this, SIGNAL(chatsReceived(QVariantMap)));
|
||||||
connect(this->tdLibReceiver, SIGNAL(chat(QVariantMap)), this, SLOT(handleChatReceived(QVariantMap)));
|
connect(this->tdLibReceiver, SIGNAL(chat(QVariantMap)), this, SLOT(handleChatReceived(QVariantMap)));
|
||||||
|
connect(this->tdLibReceiver, SIGNAL(secretChat(qlonglong, QVariantMap)), this, SLOT(handleSecretChatReceived(qlonglong, QVariantMap)));
|
||||||
|
connect(this->tdLibReceiver, SIGNAL(secretChatUpdated(qlonglong, QVariantMap)), this, SLOT(handleSecretChatUpdated(qlonglong, QVariantMap)));
|
||||||
connect(this->tdLibReceiver, SIGNAL(recentStickersUpdated(QVariantList)), this, SIGNAL(recentStickersUpdated(QVariantList)));
|
connect(this->tdLibReceiver, SIGNAL(recentStickersUpdated(QVariantList)), this, SIGNAL(recentStickersUpdated(QVariantList)));
|
||||||
connect(this->tdLibReceiver, SIGNAL(stickers(QVariantList)), this, SIGNAL(stickersReceived(QVariantList)));
|
connect(this->tdLibReceiver, SIGNAL(stickers(QVariantList)), this, SIGNAL(stickersReceived(QVariantList)));
|
||||||
connect(this->tdLibReceiver, SIGNAL(installedStickerSetsUpdated(QVariantList)), this, SIGNAL(installedStickerSetsUpdated(QVariantList)));
|
connect(this->tdLibReceiver, SIGNAL(installedStickerSetsUpdated(QVariantList)), this, SIGNAL(installedStickerSetsUpdated(QVariantList)));
|
||||||
|
@ -113,6 +117,7 @@ TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface,
|
||||||
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(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(errorReceived(int, QString)), this, SIGNAL(errorReceived(int, QString)));
|
connect(this->tdLibReceiver, SIGNAL(errorReceived(int, QString)), this, SIGNAL(errorReceived(int, QString)));
|
||||||
|
connect(this->tdLibReceiver, SIGNAL(contactsImported(QVariantList, QVariantList)), this, SIGNAL(contactsImported(QVariantList, QVariantList)));
|
||||||
|
|
||||||
connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList)));
|
connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList)));
|
||||||
|
|
||||||
|
@ -615,7 +620,7 @@ void TDLibWrapper::getGroupFullInfo(const QString &groupId, bool isSuperGroup)
|
||||||
|
|
||||||
void TDLibWrapper::getUserFullInfo(const QString &userId)
|
void TDLibWrapper::getUserFullInfo(const QString &userId)
|
||||||
{
|
{
|
||||||
LOG("Retrieving UserFullInfo");
|
LOG("Retrieving UserFullInfo" << userId);
|
||||||
QVariantMap requestObject;
|
QVariantMap requestObject;
|
||||||
requestObject.insert(_TYPE, "getUserFullInfo");
|
requestObject.insert(_TYPE, "getUserFullInfo");
|
||||||
requestObject.insert(_EXTRA, userId);
|
requestObject.insert(_EXTRA, userId);
|
||||||
|
@ -633,6 +638,16 @@ void TDLibWrapper::createPrivateChat(const QString &userId)
|
||||||
this->sendRequest(requestObject);
|
this->sendRequest(requestObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::createNewSecretChat(const QString &userId)
|
||||||
|
{
|
||||||
|
LOG("Creating new secret chat");
|
||||||
|
QVariantMap requestObject;
|
||||||
|
requestObject.insert(_TYPE, "createNewSecretChat");
|
||||||
|
requestObject.insert("user_id", userId);
|
||||||
|
requestObject.insert(_EXTRA, "openDirectly"); //gets matched in qml
|
||||||
|
this->sendRequest(requestObject);
|
||||||
|
}
|
||||||
|
|
||||||
void TDLibWrapper::createSupergroupChat(const QString &supergroupId)
|
void TDLibWrapper::createSupergroupChat(const QString &supergroupId)
|
||||||
{
|
{
|
||||||
LOG("Creating Supergroup Chat");
|
LOG("Creating Supergroup Chat");
|
||||||
|
@ -802,6 +817,42 @@ void TDLibWrapper::getDeepLinkInfo(const QString &link)
|
||||||
this->sendRequest(requestObject);
|
this->sendRequest(requestObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::getContacts()
|
||||||
|
{
|
||||||
|
LOG("Retrieving contacts");
|
||||||
|
QVariantMap requestObject;
|
||||||
|
requestObject.insert(_TYPE, "getContacts");
|
||||||
|
requestObject.insert(_EXTRA, "contactsRequested");
|
||||||
|
this->sendRequest(requestObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::getSecretChat(qlonglong secretChatId)
|
||||||
|
{
|
||||||
|
LOG("Getting detailed information about secret chat" << secretChatId);
|
||||||
|
QVariantMap requestObject;
|
||||||
|
requestObject.insert(_TYPE, "getSecretChat");
|
||||||
|
requestObject.insert("secret_chat_id", secretChatId);
|
||||||
|
this->sendRequest(requestObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::closeSecretChat(qlonglong secretChatId)
|
||||||
|
{
|
||||||
|
LOG("Closing secret chat" << secretChatId);
|
||||||
|
QVariantMap requestObject;
|
||||||
|
requestObject.insert(_TYPE, "closeSecretChat");
|
||||||
|
requestObject.insert("secret_chat_id", secretChatId);
|
||||||
|
this->sendRequest(requestObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::importContacts(const QVariantList &contacts)
|
||||||
|
{
|
||||||
|
LOG("Importing contacts");
|
||||||
|
QVariantMap requestObject;
|
||||||
|
requestObject.insert(_TYPE, "importContacts");
|
||||||
|
requestObject.insert("contacts", contacts);
|
||||||
|
this->sendRequest(requestObject);
|
||||||
|
}
|
||||||
|
|
||||||
void TDLibWrapper::searchEmoji(const QString &queryString)
|
void TDLibWrapper::searchEmoji(const QString &queryString)
|
||||||
{
|
{
|
||||||
LOG("Searching emoji" << queryString);
|
LOG("Searching emoji" << queryString);
|
||||||
|
@ -823,6 +874,11 @@ QVariantMap TDLibWrapper::getUserInformation(const QString &userId)
|
||||||
return this->allUsers.value(userId).toMap();
|
return this->allUsers.value(userId).toMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TDLibWrapper::hasUserInformation(const QString &userId)
|
||||||
|
{
|
||||||
|
return this->allUsers.contains(userId);
|
||||||
|
}
|
||||||
|
|
||||||
QVariantMap TDLibWrapper::getUserInformationByName(const QString &userName)
|
QVariantMap TDLibWrapper::getUserInformationByName(const QString &userName)
|
||||||
{
|
{
|
||||||
return this->allUserNames.value(userName).toMap();
|
return this->allUserNames.value(userName).toMap();
|
||||||
|
@ -868,6 +924,11 @@ QVariantMap TDLibWrapper::getChat(const QString &chatId)
|
||||||
return this->chats.value(chatId).toMap();
|
return this->chats.value(chatId).toMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantMap TDLibWrapper::getSecretChatFromCache(qlonglong secretChatId)
|
||||||
|
{
|
||||||
|
return this->secretChats.value(secretChatId);
|
||||||
|
}
|
||||||
|
|
||||||
QString TDLibWrapper::getOptionString(const QString &optionName)
|
QString TDLibWrapper::getOptionString(const QString &optionName)
|
||||||
{
|
{
|
||||||
return this->options.value(optionName).toString();
|
return this->options.value(optionName).toString();
|
||||||
|
@ -1137,6 +1198,18 @@ void TDLibWrapper::handleOpenWithChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::handleSecretChatReceived(qlonglong secretChatId, const QVariantMap &secretChat)
|
||||||
|
{
|
||||||
|
this->secretChats.insert(secretChatId, secretChat);
|
||||||
|
emit secretChatReceived(secretChatId, secretChat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::handleSecretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat)
|
||||||
|
{
|
||||||
|
this->secretChats.insert(secretChatId, secretChat);
|
||||||
|
emit secretChatUpdated(secretChatId, secretChat);
|
||||||
|
}
|
||||||
|
|
||||||
void TDLibWrapper::handleStorageOptimizerChanged()
|
void TDLibWrapper::handleStorageOptimizerChanged()
|
||||||
{
|
{
|
||||||
setOptionBoolean("use_storage_optimizer", appSettings->storageOptimizer());
|
setOptionBoolean("use_storage_optimizer", appSettings->storageOptimizer());
|
||||||
|
@ -1154,7 +1227,7 @@ void TDLibWrapper::setInitialParameters()
|
||||||
initialParameters.insert("use_file_database", true);
|
initialParameters.insert("use_file_database", true);
|
||||||
initialParameters.insert("use_chat_info_database", true);
|
initialParameters.insert("use_chat_info_database", true);
|
||||||
initialParameters.insert("use_message_database", true);
|
initialParameters.insert("use_message_database", true);
|
||||||
initialParameters.insert("use_secret_chats", false);
|
initialParameters.insert("use_secret_chats", true);
|
||||||
initialParameters.insert("system_language_code", QLocale::system().name());
|
initialParameters.insert("system_language_code", QLocale::system().name());
|
||||||
QSettings hardwareSettings("/etc/hw-release", QSettings::NativeFormat);
|
QSettings hardwareSettings("/etc/hw-release", QSettings::NativeFormat);
|
||||||
initialParameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString());
|
initialParameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString());
|
||||||
|
@ -1289,7 +1362,15 @@ TDLibWrapper::ChatMemberStatus TDLibWrapper::chatMemberStatusFromString(const QS
|
||||||
(status == QStringLiteral("chatMemberStatusAdministrator")) ? ChatMemberStatusAdministrator :
|
(status == QStringLiteral("chatMemberStatusAdministrator")) ? ChatMemberStatusAdministrator :
|
||||||
(status == QStringLiteral("chatMemberStatusRestricted")) ? ChatMemberStatusRestricted :
|
(status == QStringLiteral("chatMemberStatusRestricted")) ? ChatMemberStatusRestricted :
|
||||||
(status == QStringLiteral("chatMemberStatusBanned")) ? ChatMemberStatusBanned :
|
(status == QStringLiteral("chatMemberStatusBanned")) ? ChatMemberStatusBanned :
|
||||||
ChatMemberStatusUnknown;
|
ChatMemberStatusUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
TDLibWrapper::SecretChatState TDLibWrapper::secretChatStateFromString(const QString &state)
|
||||||
|
{
|
||||||
|
return (state == QStringLiteral("secretChatStateClosed")) ? SecretChatStateClosed :
|
||||||
|
(state == QStringLiteral("secretChatStatePending")) ? SecretChatStatePending :
|
||||||
|
(state == QStringLiteral("secretChatStateReady")) ? SecretChatStateReady :
|
||||||
|
SecretChatStateUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
TDLibWrapper::ChatMemberStatus TDLibWrapper::Group::chatMemberStatus() const
|
TDLibWrapper::ChatMemberStatus TDLibWrapper::Group::chatMemberStatus() const
|
||||||
|
|
|
@ -79,6 +79,14 @@ public:
|
||||||
};
|
};
|
||||||
Q_ENUM(ChatMemberStatus)
|
Q_ENUM(ChatMemberStatus)
|
||||||
|
|
||||||
|
enum SecretChatState {
|
||||||
|
SecretChatStateUnknown,
|
||||||
|
SecretChatStateClosed,
|
||||||
|
SecretChatStatePending,
|
||||||
|
SecretChatStateReady,
|
||||||
|
};
|
||||||
|
Q_ENUM(SecretChatState)
|
||||||
|
|
||||||
class Group {
|
class Group {
|
||||||
public:
|
public:
|
||||||
Group(qlonglong id) : groupId(id) { }
|
Group(qlonglong id) : groupId(id) { }
|
||||||
|
@ -94,12 +102,14 @@ public:
|
||||||
Q_INVOKABLE TDLibWrapper::ConnectionState getConnectionState();
|
Q_INVOKABLE TDLibWrapper::ConnectionState getConnectionState();
|
||||||
Q_INVOKABLE QVariantMap getUserInformation();
|
Q_INVOKABLE QVariantMap getUserInformation();
|
||||||
Q_INVOKABLE QVariantMap getUserInformation(const QString &userId);
|
Q_INVOKABLE QVariantMap getUserInformation(const QString &userId);
|
||||||
|
Q_INVOKABLE bool hasUserInformation(const QString &userId);
|
||||||
Q_INVOKABLE QVariantMap getUserInformationByName(const QString &userName);
|
Q_INVOKABLE QVariantMap getUserInformationByName(const QString &userName);
|
||||||
Q_INVOKABLE QVariantMap getUnreadMessageInformation();
|
Q_INVOKABLE QVariantMap getUnreadMessageInformation();
|
||||||
Q_INVOKABLE QVariantMap getUnreadChatInformation();
|
Q_INVOKABLE QVariantMap getUnreadChatInformation();
|
||||||
Q_INVOKABLE QVariantMap getBasicGroup(qlonglong groupId) const;
|
Q_INVOKABLE QVariantMap getBasicGroup(qlonglong groupId) const;
|
||||||
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 QString getOptionString(const QString &optionName);
|
Q_INVOKABLE QString getOptionString(const QString &optionName);
|
||||||
Q_INVOKABLE void copyFileToDownloads(const QString &filePath);
|
Q_INVOKABLE void copyFileToDownloads(const QString &filePath);
|
||||||
Q_INVOKABLE void openFileOnDevice(const QString &filePath);
|
Q_INVOKABLE void openFileOnDevice(const QString &filePath);
|
||||||
|
@ -146,6 +156,7 @@ public:
|
||||||
Q_INVOKABLE void getGroupFullInfo(const QString &groupId, bool isSuperGroup);
|
Q_INVOKABLE void getGroupFullInfo(const QString &groupId, bool isSuperGroup);
|
||||||
Q_INVOKABLE void getUserFullInfo(const QString &userId);
|
Q_INVOKABLE void getUserFullInfo(const QString &userId);
|
||||||
Q_INVOKABLE void createPrivateChat(const QString &userId);
|
Q_INVOKABLE void createPrivateChat(const QString &userId);
|
||||||
|
Q_INVOKABLE void createNewSecretChat(const QString &userId);
|
||||||
Q_INVOKABLE void createSupergroupChat(const QString &supergroupId);
|
Q_INVOKABLE void createSupergroupChat(const QString &supergroupId);
|
||||||
Q_INVOKABLE void createBasicGroupChat(const QString &basicGroupId);
|
Q_INVOKABLE void createBasicGroupChat(const QString &basicGroupId);
|
||||||
Q_INVOKABLE void getGroupsInCommon(const QString &userId, int limit, int offset);
|
Q_INVOKABLE void getGroupsInCommon(const QString &userId, int limit, int offset);
|
||||||
|
@ -162,6 +173,10 @@ public:
|
||||||
Q_INVOKABLE void searchPublicChat(const QString &userName);
|
Q_INVOKABLE void searchPublicChat(const QString &userName);
|
||||||
Q_INVOKABLE void joinChatByInviteLink(const QString &inviteLink);
|
Q_INVOKABLE void joinChatByInviteLink(const QString &inviteLink);
|
||||||
Q_INVOKABLE void getDeepLinkInfo(const QString &link);
|
Q_INVOKABLE void getDeepLinkInfo(const QString &link);
|
||||||
|
Q_INVOKABLE void getContacts();
|
||||||
|
Q_INVOKABLE void getSecretChat(qlonglong secretChatId);
|
||||||
|
Q_INVOKABLE void closeSecretChat(qlonglong secretChatId);
|
||||||
|
Q_INVOKABLE void importContacts(const QVariantList &contacts);
|
||||||
|
|
||||||
// Others (candidates for extraction ;))
|
// Others (candidates for extraction ;))
|
||||||
Q_INVOKABLE void searchEmoji(const QString &queryString);
|
Q_INVOKABLE void searchEmoji(const QString &queryString);
|
||||||
|
@ -172,6 +187,7 @@ public:
|
||||||
const Group* getGroup(qlonglong groupId) const;
|
const Group* getGroup(qlonglong groupId) const;
|
||||||
static ChatType chatTypeFromString(const QString &type);
|
static ChatType chatTypeFromString(const QString &type);
|
||||||
static ChatMemberStatus chatMemberStatusFromString(const QString &status);
|
static ChatMemberStatus chatMemberStatusFromString(const QString &status);
|
||||||
|
static SecretChatState secretChatStateFromString(const QString &state);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void versionDetected(const QString &version);
|
void versionDetected(const QString &version);
|
||||||
|
@ -205,6 +221,8 @@ signals:
|
||||||
void messagesDeleted(const QString &chatId, const QVariantList &messageIds);
|
void messagesDeleted(const QString &chatId, const QVariantList &messageIds);
|
||||||
void chatsReceived(const QVariantMap &chats);
|
void chatsReceived(const QVariantMap &chats);
|
||||||
void chatReceived(const QVariantMap &chat);
|
void chatReceived(const QVariantMap &chat);
|
||||||
|
void secretChatReceived(qlonglong secretChatId, const QVariantMap &secretChat);
|
||||||
|
void secretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat);
|
||||||
void recentStickersUpdated(const QVariantList &stickerIds);
|
void recentStickersUpdated(const QVariantList &stickerIds);
|
||||||
void stickersReceived(const QVariantList &stickers);
|
void stickersReceived(const QVariantList &stickers);
|
||||||
void installedStickerSetsUpdated(const QVariantList &stickerSetIds);
|
void installedStickerSetsUpdated(const QVariantList &stickerSetIds);
|
||||||
|
@ -225,6 +243,7 @@ signals:
|
||||||
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 errorReceived(const int code, const QString &message);
|
void errorReceived(const int code, const QString &message);
|
||||||
|
void contactsImported(const QVariantList &importerCount, const QVariantList &userIds);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void handleVersionDetected(const QString &version);
|
void handleVersionDetected(const QString &version);
|
||||||
|
@ -243,6 +262,8 @@ public slots:
|
||||||
void handleStickerSets(const QVariantList &stickerSets);
|
void handleStickerSets(const QVariantList &stickerSets);
|
||||||
void handleEmojiSearchCompleted(const QString &queryString, const QVariantList &resultList);
|
void handleEmojiSearchCompleted(const QString &queryString, const QVariantList &resultList);
|
||||||
void handleOpenWithChanged();
|
void handleOpenWithChanged();
|
||||||
|
void handleSecretChatReceived(qlonglong secretChatId, const QVariantMap &secretChat);
|
||||||
|
void handleSecretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat);
|
||||||
void handleStorageOptimizerChanged();
|
void handleStorageOptimizerChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -267,6 +288,7 @@ private:
|
||||||
QVariantMap allUsers;
|
QVariantMap allUsers;
|
||||||
QVariantMap allUserNames;
|
QVariantMap allUserNames;
|
||||||
QVariantMap chats;
|
QVariantMap chats;
|
||||||
|
QMap<qlonglong, QVariantMap> secretChats;
|
||||||
QVariantMap unreadMessageInformation;
|
QVariantMap unreadMessageInformation;
|
||||||
QVariantMap unreadChatInformation;
|
QVariantMap unreadChatInformation;
|
||||||
QHash<qlonglong,Group*> basicGroups;
|
QHash<qlonglong,Group*> basicGroups;
|
||||||
|
|
|
@ -179,6 +179,10 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Secret Chat</source>
|
||||||
|
<translation>Neuer geheimer Chat</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatInformationTabItemMembersGroups</name>
|
<name>ChatInformationTabItemMembersGroups</name>
|
||||||
|
@ -260,6 +264,10 @@
|
||||||
<source>Mark all messages as read</source>
|
<source>Mark all messages as read</source>
|
||||||
<translation>Nachrichten als gelesen markieren</translation>
|
<translation>Nachrichten als gelesen markieren</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No message in this chat.</source>
|
||||||
|
<translation>Keine Nachricht in diesem Chat</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatPage</name>
|
<name>ChatPage</name>
|
||||||
|
@ -375,6 +383,18 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<translation>Dieser geheime Chat ist noch nicht bereit. Ihr Chatpartner muss erst noch online gehen.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closing chat</source>
|
||||||
|
<translation>Schließe Chat</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Close Chat</source>
|
||||||
|
<translation>Chat schließen</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -903,6 +923,53 @@
|
||||||
<translation>Diese Nachricht wurde weitergeleitet. Ursprünglicher Autor: %1</translation>
|
<translation>Diese Nachricht wurde weitergeleitet. Ursprünglicher Autor: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>NewChatPage</name>
|
||||||
|
<message>
|
||||||
|
<source>Your Contacts</source>
|
||||||
|
<translation>Ihre Kontakte</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You don't have any contacts.</source>
|
||||||
|
<translation>Sie haben keine Kontakte</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Private Chat</source>
|
||||||
|
<translation>Privater Chat</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
|
||||||
|
<translation>Transportverschlüsselt, nutzt Telegram-Cloud, teilbar zwischen Geräten</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Secret Chat</source>
|
||||||
|
<translation>Geheimer Chat</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>End-to-end-encrypted, accessible on this device only</source>
|
||||||
|
<translation>Ende-zu-Ende-verschlüsselt, nur auf diesem Gerät zugreifbar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Search a contact...</source>
|
||||||
|
<translation>Einen Kontakt suchen...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading contacts...</source>
|
||||||
|
<translation>Lade Kontakte...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<translation>Kontakte mit Telegram synchronisieren</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation>Konnte Ihre Kontakte nicht mit Telegram synchronisieren.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Contacts successfully synchronized with Telegram.</source>
|
||||||
|
<translation>Die Kontakte wurden erfolgreich mit Telegram synchronisiert.</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NotificationManager</name>
|
<name>NotificationManager</name>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
|
@ -955,6 +1022,10 @@
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
<translation>Sie haben noch keine Chats.</translation>
|
<translation>Sie haben noch keine Chats.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Chat</source>
|
||||||
|
<translation>Neuer Chat</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PinnedMessageItem</name>
|
<name>PinnedMessageItem</name>
|
||||||
|
@ -1625,5 +1696,13 @@
|
||||||
<source>You are already a member of this chat.</source>
|
<source>You are already a member of this chat.</source>
|
||||||
<translation>Sie sind bereits Mitglied dieses Chats.</translation>
|
<translation>Sie sind bereits Mitglied dieses Chats.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closed!</source>
|
||||||
|
<translation>Geschlossen!</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pending acknowledgement</source>
|
||||||
|
<translation>Ausstehende Bestätigung</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -179,6 +179,10 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Secret Chat</source>
|
||||||
|
<translation>New Secret Chat</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatInformationTabItemMembersGroups</name>
|
<name>ChatInformationTabItemMembersGroups</name>
|
||||||
|
@ -260,6 +264,10 @@
|
||||||
<source>Mark all messages as read</source>
|
<source>Mark all messages as read</source>
|
||||||
<translation>Mark all messages as read</translation>
|
<translation>Mark all messages as read</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No message in this chat.</source>
|
||||||
|
<translation>No message in this chat.</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatPage</name>
|
<name>ChatPage</name>
|
||||||
|
@ -375,6 +383,18 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<translation>This secret chat is not yet ready. Your chat partner needs to go online first.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closing chat</source>
|
||||||
|
<translation>Closing chat</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Close Chat</source>
|
||||||
|
<translation>Close Chat</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -903,6 +923,53 @@
|
||||||
<translation>This message was forwarded. Original author: %1</translation>
|
<translation>This message was forwarded. Original author: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>NewChatPage</name>
|
||||||
|
<message>
|
||||||
|
<source>Your Contacts</source>
|
||||||
|
<translation>Your Contacts</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You don't have any contacts.</source>
|
||||||
|
<translation>You don't have any contacts.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Private Chat</source>
|
||||||
|
<translation>Private Chat</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Secret Chat</source>
|
||||||
|
<translation>Secret Chat</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>End-to-end-encrypted, accessible on this device only</source>
|
||||||
|
<translation>End-to-end-encrypted, accessible on this device only</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Search a contact...</source>
|
||||||
|
<translation>Search a contact...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading contacts...</source>
|
||||||
|
<translation>Loading contacts...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
|
||||||
|
<translation>Transport-encrypted, uses Telegram Cloud, sharable across devices</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<translation>Synchronize Contacts with Telegram</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation>Could not synchronize your contacts with Telegram.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Contacts successfully synchronized with Telegram.</source>
|
||||||
|
<translation>Contacts successfully synchronized with Telegram.</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NotificationManager</name>
|
<name>NotificationManager</name>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
|
@ -955,6 +1022,10 @@
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
<translation>You don't have any chats yet.</translation>
|
<translation>You don't have any chats yet.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Chat</source>
|
||||||
|
<translation>New Chat</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PinnedMessageItem</name>
|
<name>PinnedMessageItem</name>
|
||||||
|
@ -1625,5 +1696,13 @@
|
||||||
<source>You are already a member of this chat.</source>
|
<source>You are already a member of this chat.</source>
|
||||||
<translation>You are already a member of this chat.</translation>
|
<translation>You are already a member of this chat.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closed!</source>
|
||||||
|
<translation>Closed!</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pending acknowledgement</source>
|
||||||
|
<translation>Pending acknowledgement</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -176,6 +176,10 @@
|
||||||
<numerusform>%1 en línea</numerusform>
|
<numerusform>%1 en línea</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatInformationTabItemMembersGroups</name>
|
<name>ChatInformationTabItemMembersGroups</name>
|
||||||
|
@ -257,6 +261,10 @@
|
||||||
<source>Mark all messages as read</source>
|
<source>Mark all messages as read</source>
|
||||||
<translation>Marcar todos como leídos</translation>
|
<translation>Marcar todos como leídos</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No message in this chat.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatPage</name>
|
<name>ChatPage</name>
|
||||||
|
@ -365,6 +373,18 @@
|
||||||
<numerusform>%1 en línea</numerusform>
|
<numerusform>%1 en línea</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closing chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Close Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -893,6 +913,53 @@
|
||||||
<translation>Este mensaje fue reenviado. Autor original: %1</translation>
|
<translation>Este mensaje fue reenviado. Autor original: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>NewChatPage</name>
|
||||||
|
<message>
|
||||||
|
<source>Your Contacts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You don't have any contacts.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Private Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>End-to-end-encrypted, accessible on this device only</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading contacts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Search a contact...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Contacts successfully synchronized with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NotificationManager</name>
|
<name>NotificationManager</name>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
|
@ -944,6 +1011,10 @@
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
<translation>No hay todavía ninguna charla.</translation>
|
<translation>No hay todavía ninguna charla.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PinnedMessageItem</name>
|
<name>PinnedMessageItem</name>
|
||||||
|
@ -1606,5 +1677,13 @@
|
||||||
<source>You are already a member of this chat.</source>
|
<source>You are already a member of this chat.</source>
|
||||||
<translation>Ya eres miembro de este grupo.</translation>
|
<translation>Ya eres miembro de este grupo.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closed!</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pending acknowledgement</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -179,6 +179,10 @@
|
||||||
<numerusform>%1 paikalla</numerusform>
|
<numerusform>%1 paikalla</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatInformationTabItemMembersGroups</name>
|
<name>ChatInformationTabItemMembersGroups</name>
|
||||||
|
@ -260,6 +264,10 @@
|
||||||
<source>Mark all messages as read</source>
|
<source>Mark all messages as read</source>
|
||||||
<translation>Merkitse kaikki viestit luetuiksi</translation>
|
<translation>Merkitse kaikki viestit luetuiksi</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No message in this chat.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatPage</name>
|
<name>ChatPage</name>
|
||||||
|
@ -375,6 +383,18 @@
|
||||||
<numerusform>%1 paikalla</numerusform>
|
<numerusform>%1 paikalla</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closing chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Close Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -904,6 +924,53 @@
|
||||||
<translation>Välitetty viesti. Alkuperäinen lähettäjä: %1</translation>
|
<translation>Välitetty viesti. Alkuperäinen lähettäjä: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>NewChatPage</name>
|
||||||
|
<message>
|
||||||
|
<source>Your Contacts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You don't have any contacts.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Private Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>End-to-end-encrypted, accessible on this device only</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading contacts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Search a contact...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Contacts successfully synchronized with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NotificationManager</name>
|
<name>NotificationManager</name>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
|
@ -956,6 +1023,10 @@
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
<translation>Sinulla ei ole vielä keskusteluja.</translation>
|
<translation>Sinulla ei ole vielä keskusteluja.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PinnedMessageItem</name>
|
<name>PinnedMessageItem</name>
|
||||||
|
@ -1626,5 +1697,13 @@
|
||||||
<source>You are already a member of this chat.</source>
|
<source>You are already a member of this chat.</source>
|
||||||
<translation>Olet jo tämän ryhmän jäsen.</translation>
|
<translation>Olet jo tämän ryhmän jäsen.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closed!</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pending acknowledgement</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -176,6 +176,10 @@
|
||||||
<numerusform></numerusform>
|
<numerusform></numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatInformationTabItemMembersGroups</name>
|
<name>ChatInformationTabItemMembersGroups</name>
|
||||||
|
@ -257,6 +261,10 @@
|
||||||
<source>Mark all messages as read</source>
|
<source>Mark all messages as read</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No message in this chat.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatPage</name>
|
<name>ChatPage</name>
|
||||||
|
@ -365,6 +373,18 @@
|
||||||
<numerusform></numerusform>
|
<numerusform></numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closing chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Close Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -893,6 +913,53 @@
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>NewChatPage</name>
|
||||||
|
<message>
|
||||||
|
<source>Your Contacts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You don't have any contacts.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Private Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>End-to-end-encrypted, accessible on this device only</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading contacts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Search a contact...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Contacts successfully synchronized with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NotificationManager</name>
|
<name>NotificationManager</name>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
|
@ -944,6 +1011,10 @@
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PinnedMessageItem</name>
|
<name>PinnedMessageItem</name>
|
||||||
|
@ -1606,5 +1677,13 @@
|
||||||
<source>You are already a member of this chat.</source>
|
<source>You are already a member of this chat.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closed!</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pending acknowledgement</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -179,6 +179,10 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatInformationTabItemMembersGroups</name>
|
<name>ChatInformationTabItemMembersGroups</name>
|
||||||
|
@ -260,6 +264,10 @@
|
||||||
<source>Mark all messages as read</source>
|
<source>Mark all messages as read</source>
|
||||||
<translation>Segna tutti i messaggi come già letti</translation>
|
<translation>Segna tutti i messaggi come già letti</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No message in this chat.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatPage</name>
|
<name>ChatPage</name>
|
||||||
|
@ -375,6 +383,18 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closing chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Close Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -903,6 +923,53 @@
|
||||||
<translation>Questo è un messaggio inoltrato. Autore originale: %1</translation>
|
<translation>Questo è un messaggio inoltrato. Autore originale: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>NewChatPage</name>
|
||||||
|
<message>
|
||||||
|
<source>Your Contacts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You don't have any contacts.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Private Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>End-to-end-encrypted, accessible on this device only</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading contacts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Search a contact...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Contacts successfully synchronized with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NotificationManager</name>
|
<name>NotificationManager</name>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
|
@ -955,6 +1022,10 @@
|
||||||
<source>Loading chat list...</source>
|
<source>Loading chat list...</source>
|
||||||
<translation>Carica lista chat...</translation>
|
<translation>Carica lista chat...</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PinnedMessageItem</name>
|
<name>PinnedMessageItem</name>
|
||||||
|
@ -1625,5 +1696,13 @@
|
||||||
<source>You are already a member of this chat.</source>
|
<source>You are already a member of this chat.</source>
|
||||||
<translation>Sei già membro di questa chat.</translation>
|
<translation>Sei già membro di questa chat.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closed!</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pending acknowledgement</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -182,6 +182,10 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatInformationTabItemMembersGroups</name>
|
<name>ChatInformationTabItemMembersGroups</name>
|
||||||
|
@ -263,6 +267,10 @@
|
||||||
<source>Mark all messages as read</source>
|
<source>Mark all messages as read</source>
|
||||||
<translation>Zaznacz wszystkie wiadomości jako przeczytane</translation>
|
<translation>Zaznacz wszystkie wiadomości jako przeczytane</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No message in this chat.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatPage</name>
|
<name>ChatPage</name>
|
||||||
|
@ -385,6 +393,18 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closing chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Close Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -913,6 +933,53 @@
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>NewChatPage</name>
|
||||||
|
<message>
|
||||||
|
<source>Your Contacts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You don't have any contacts.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Private Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>End-to-end-encrypted, accessible on this device only</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading contacts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Search a contact...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Contacts successfully synchronized with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NotificationManager</name>
|
<name>NotificationManager</name>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
|
@ -966,6 +1033,10 @@
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
<translation>Nie masz jeszcze żadnych czatów.</translation>
|
<translation>Nie masz jeszcze żadnych czatów.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PinnedMessageItem</name>
|
<name>PinnedMessageItem</name>
|
||||||
|
@ -1644,5 +1715,13 @@
|
||||||
<source>You are already a member of this chat.</source>
|
<source>You are already a member of this chat.</source>
|
||||||
<translation>Jesteś już członkiem tego czatu.</translation>
|
<translation>Jesteś już członkiem tego czatu.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closed!</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pending acknowledgement</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -182,6 +182,10 @@
|
||||||
<numerusform></numerusform>
|
<numerusform></numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatInformationTabItemMembersGroups</name>
|
<name>ChatInformationTabItemMembersGroups</name>
|
||||||
|
@ -263,6 +267,10 @@
|
||||||
<source>Mark all messages as read</source>
|
<source>Mark all messages as read</source>
|
||||||
<translation>Отметить все сообщения как прочитанные</translation>
|
<translation>Отметить все сообщения как прочитанные</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No message in this chat.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatPage</name>
|
<name>ChatPage</name>
|
||||||
|
@ -385,6 +393,18 @@
|
||||||
<numerusform></numerusform>
|
<numerusform></numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closing chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Close Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -913,6 +933,53 @@
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>NewChatPage</name>
|
||||||
|
<message>
|
||||||
|
<source>Your Contacts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You don't have any contacts.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Private Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>End-to-end-encrypted, accessible on this device only</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading contacts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Search a contact...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Contacts successfully synchronized with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NotificationManager</name>
|
<name>NotificationManager</name>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
|
@ -966,6 +1033,10 @@
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
<translation>Тут пока ничего нет</translation>
|
<translation>Тут пока ничего нет</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PinnedMessageItem</name>
|
<name>PinnedMessageItem</name>
|
||||||
|
@ -1644,5 +1715,13 @@
|
||||||
<source>You are already a member of this chat.</source>
|
<source>You are already a member of this chat.</source>
|
||||||
<translation>Вы уже в этом чате.</translation>
|
<translation>Вы уже в этом чате.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closed!</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pending acknowledgement</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -179,6 +179,10 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatInformationTabItemMembersGroups</name>
|
<name>ChatInformationTabItemMembersGroups</name>
|
||||||
|
@ -260,6 +264,10 @@
|
||||||
<source>Mark all messages as read</source>
|
<source>Mark all messages as read</source>
|
||||||
<translation>Markera alla meddelanden som lästa</translation>
|
<translation>Markera alla meddelanden som lästa</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No message in this chat.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatPage</name>
|
<name>ChatPage</name>
|
||||||
|
@ -375,6 +383,18 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closing chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Close Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -903,6 +923,53 @@
|
||||||
<translation>Detta meddelande är vidarebefordrat. Ursprunglig avsändare: %1</translation>
|
<translation>Detta meddelande är vidarebefordrat. Ursprunglig avsändare: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>NewChatPage</name>
|
||||||
|
<message>
|
||||||
|
<source>Your Contacts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You don't have any contacts.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Private Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>End-to-end-encrypted, accessible on this device only</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading contacts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Search a contact...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Contacts successfully synchronized with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NotificationManager</name>
|
<name>NotificationManager</name>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
|
@ -955,6 +1022,10 @@
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
<translation>Du har inga chattar än.</translation>
|
<translation>Du har inga chattar än.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PinnedMessageItem</name>
|
<name>PinnedMessageItem</name>
|
||||||
|
@ -1625,5 +1696,13 @@
|
||||||
<source>You are already a member of this chat.</source>
|
<source>You are already a member of this chat.</source>
|
||||||
<translation>Du är redan medlem i den här chatten.</translation>
|
<translation>Du är redan medlem i den här chatten.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closed!</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pending acknowledgement</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -176,6 +176,10 @@
|
||||||
<numerusform>%1 位在线</numerusform>
|
<numerusform>%1 位在线</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatInformationTabItemMembersGroups</name>
|
<name>ChatInformationTabItemMembersGroups</name>
|
||||||
|
@ -257,6 +261,10 @@
|
||||||
<source>Mark all messages as read</source>
|
<source>Mark all messages as read</source>
|
||||||
<translation>标记全部消息为已读</translation>
|
<translation>标记全部消息为已读</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No message in this chat.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatPage</name>
|
<name>ChatPage</name>
|
||||||
|
@ -365,6 +373,18 @@
|
||||||
<numerusform>%1 位在线</numerusform>
|
<numerusform>%1 位在线</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closing chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Close Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -893,6 +913,53 @@
|
||||||
<translation>此消息为转发消息,原作者: %1</translation>
|
<translation>此消息为转发消息,原作者: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>NewChatPage</name>
|
||||||
|
<message>
|
||||||
|
<source>Your Contacts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You don't have any contacts.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Private Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>End-to-end-encrypted, accessible on this device only</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading contacts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Search a contact...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Contacts successfully synchronized with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NotificationManager</name>
|
<name>NotificationManager</name>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
|
@ -944,6 +1011,10 @@
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
<translation>你还没有任何对话。</translation>
|
<translation>你还没有任何对话。</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PinnedMessageItem</name>
|
<name>PinnedMessageItem</name>
|
||||||
|
@ -1606,5 +1677,13 @@
|
||||||
<source>You are already a member of this chat.</source>
|
<source>You are already a member of this chat.</source>
|
||||||
<translation>你已是此对话成员。</translation>
|
<translation>你已是此对话成员。</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closed!</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pending acknowledgement</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -179,6 +179,10 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatInformationTabItemMembersGroups</name>
|
<name>ChatInformationTabItemMembersGroups</name>
|
||||||
|
@ -260,6 +264,10 @@
|
||||||
<source>Mark all messages as read</source>
|
<source>Mark all messages as read</source>
|
||||||
<translation>Mark all messages as read</translation>
|
<translation>Mark all messages as read</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No message in this chat.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatPage</name>
|
<name>ChatPage</name>
|
||||||
|
@ -375,6 +383,18 @@
|
||||||
<numerusform>%1 online</numerusform>
|
<numerusform>%1 online</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>This secret chat is not yet ready. Your chat partner needs to go online first.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closing chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Close Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -903,6 +923,53 @@
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>NewChatPage</name>
|
||||||
|
<message>
|
||||||
|
<source>Your Contacts</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>You don't have any contacts.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Private Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Secret Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>End-to-end-encrypted, accessible on this device only</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading contacts...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Search a contact...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Synchronize Contacts with Telegram</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not synchronize your contacts with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Contacts successfully synchronized with Telegram.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NotificationManager</name>
|
<name>NotificationManager</name>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
|
@ -955,6 +1022,10 @@
|
||||||
<source>You don't have any chats yet.</source>
|
<source>You don't have any chats yet.</source>
|
||||||
<translation>You don't have any chats yet.</translation>
|
<translation>You don't have any chats yet.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>New Chat</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PinnedMessageItem</name>
|
<name>PinnedMessageItem</name>
|
||||||
|
@ -1625,5 +1696,13 @@
|
||||||
<source>You are already a member of this chat.</source>
|
<source>You are already a member of this chat.</source>
|
||||||
<translation>You are already a member of this chat.</translation>
|
<translation>You are already a member of this chat.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Closed!</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Pending acknowledgement</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
Loading…
Reference in a new issue