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/chatlistmodel.cpp \
|
||||
src/chatmodel.cpp \
|
||||
src/contactsmodel.cpp \
|
||||
src/dbusadaptor.cpp \
|
||||
src/dbusinterface.cpp \
|
||||
src/emojisearchworker.cpp \
|
||||
|
@ -72,6 +73,7 @@ DISTFILES += qml/harbour-fernschreiber.qml \
|
|||
qml/pages/ChatSelectionPage.qml \
|
||||
qml/pages/CoverPage.qml \
|
||||
qml/pages/InitializationPage.qml \
|
||||
qml/pages/NewChatPage.qml \
|
||||
qml/pages/OverviewPage.qml \
|
||||
qml/pages/AboutPage.qml \
|
||||
qml/pages/PollCreationPage.qml \
|
||||
|
@ -145,6 +147,7 @@ HEADERS += \
|
|||
src/appsettings.h \
|
||||
src/chatlistmodel.h \
|
||||
src/chatmodel.h \
|
||||
src/contactsmodel.h \
|
||||
src/dbusadaptor.h \
|
||||
src/dbusinterface.h \
|
||||
src/debuglog.h \
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import WerkWolf.Fernschreiber 1.0
|
||||
|
||||
import "../js/twemoji.js" as Emoji
|
||||
import "../js/functions.js" as Functions
|
||||
|
@ -14,12 +15,13 @@ PhotoTextsListItem {
|
|||
// chat title
|
||||
primaryText.text: title ? Emoji.emojify(title + ( display.notification_settings.mute_for > 0 ? " 🔇" : "" ), Theme.fontSizeMedium) : qsTr("Unknown")
|
||||
// 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
|
||||
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
|
||||
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
|
||||
isSecret: ( chat_type === TelegramAPI.ChatTypeSecret )
|
||||
|
||||
openMenuOnPressAndHold: true//chat_id != overviewPage.ownUserId
|
||||
menu: ContextMenu {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import WerkWolf.Fernschreiber 1.0
|
||||
|
||||
ListItem {
|
||||
|
||||
id: chatListViewItem
|
||||
|
@ -9,7 +11,8 @@ ListItem {
|
|||
property alias secondaryText: secondaryText //usually last message
|
||||
property alias tertiaryText: tertiaryText //usually last message date
|
||||
|
||||
property int unreadCount
|
||||
property int unreadCount: 0
|
||||
property bool isSecret: false
|
||||
property alias pictureThumbnail: pictureThumbnail
|
||||
|
||||
contentHeight: mainRow.height + separator.height + 2 * Theme.paddingMedium
|
||||
|
@ -50,6 +53,24 @@ ListItem {
|
|||
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 {
|
||||
id: chatUnreadMessagesCountBackground
|
||||
color: Theme.highlightBackgroundColor
|
||||
|
@ -104,6 +125,7 @@ ListItem {
|
|||
width: parent.width - Theme.paddingMedium - prologSecondaryText.width
|
||||
truncationMode: TruncationMode.Fade
|
||||
textFormat: Text.StyledText
|
||||
visible: prologSecondaryText.width < ( parent.width - Theme.paddingLarge )
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,15 @@ SilicaFlickable {
|
|||
tdLibWrapper.getUserFullInfo(chatInformationPage.chatPartnerGroupId);
|
||||
tdLibWrapper.getUserProfilePhotos(chatInformationPage.chatPartnerGroupId, 100, 0);
|
||||
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":
|
||||
chatInformationPage.isBasicGroup = true;
|
||||
chatInformationPage.chatPartnerGroupId = chatInformation.type.basic_group_id.toString();
|
||||
|
@ -60,8 +69,8 @@ SilicaFlickable {
|
|||
chatInformationPage.isChannel = chatInformationPage.groupInformation.is_channel;
|
||||
break;
|
||||
}
|
||||
Debug.log("is set up", chatInformationPage.isPrivateChat, chatInformationPage.isBasicGroup, chatInformationPage.isSuperGroup, chatInformationPage.chatPartnerGroupId)
|
||||
if(!chatInformationPage.isPrivateChat) {
|
||||
Debug.log("is set up", chatInformationPage.isPrivateChat, chatInformationPage.isSecretChat, chatInformationPage.isBasicGroup, chatInformationPage.isSuperGroup, chatInformationPage.chatPartnerGroupId)
|
||||
if(!(chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat)) {
|
||||
updateGroupStatusText();
|
||||
}
|
||||
|
||||
|
@ -147,18 +156,18 @@ SilicaFlickable {
|
|||
}
|
||||
}
|
||||
onUserFullInfoReceived: {
|
||||
if(chatInformationPage.isPrivateChat && userFullInfo["@extra"] === chatInformationPage.chatPartnerGroupId) {
|
||||
if((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && userFullInfo["@extra"] === chatInformationPage.chatPartnerGroupId) {
|
||||
chatInformationPage.chatPartnerFullInformation = userFullInfo;
|
||||
}
|
||||
}
|
||||
onUserFullInfoUpdated: {
|
||||
if(chatInformationPage.isPrivateChat && userId === chatInformationPage.chatPartnerGroupId) {
|
||||
if((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && userId === chatInformationPage.chatPartnerGroupId) {
|
||||
chatInformationPage.chatPartnerFullInformation = userFullInfo;
|
||||
}
|
||||
}
|
||||
|
||||
onUserProfilePhotosReceived: {
|
||||
if(chatInformationPage.isPrivateChat && extra === chatInformationPage.chatPartnerGroupId) {
|
||||
if((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) && extra === chatInformationPage.chatPartnerGroupId) {
|
||||
chatInformationPage.chatPartnerProfilePhotos = photos;
|
||||
}
|
||||
}
|
||||
|
@ -180,14 +189,10 @@ SilicaFlickable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Component.onCompleted: {7
|
||||
Component.onCompleted: {
|
||||
initializePage();
|
||||
}
|
||||
|
||||
|
||||
|
||||
ListModel {
|
||||
id: membersList
|
||||
}
|
||||
|
@ -227,13 +232,13 @@ SilicaFlickable {
|
|||
}
|
||||
text: chatInformation.notification_settings.mute_for > 0 ? qsTr("Unmute Chat") : qsTr("Mute Chat")
|
||||
}
|
||||
// MenuItem { //TODO Implement
|
||||
// visible: !userIsMember
|
||||
// onClicked: {
|
||||
// tdLibWrapper.joinChat(chatInformationPage.chatInformation.id);
|
||||
// }
|
||||
// text: qsTr("Join Chat")
|
||||
// }
|
||||
MenuItem {
|
||||
visible: chatInformationPage.isPrivateChat
|
||||
onClicked: {
|
||||
tdLibWrapper.createNewSecretChat(chatInformationPage.chatPartnerGroupId);
|
||||
}
|
||||
text: qsTr("New Secret Chat")
|
||||
}
|
||||
}
|
||||
// header
|
||||
PageHeader {
|
||||
|
@ -278,14 +283,14 @@ SilicaFlickable {
|
|||
active: imageContainer.hasImage
|
||||
asynchronous: true
|
||||
anchors.fill: chatPictureThumbnail
|
||||
source: chatInformationPage.isPrivateChat
|
||||
source: ( chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat)
|
||||
? "ChatInformationProfilePictureList.qml"
|
||||
: "ChatInformationProfilePicture.qml"
|
||||
}
|
||||
}
|
||||
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")
|
||||
description: chatInformationPage.isPrivateChat ? ("@"+(chatInformationPage.privateChatUserInformation.username || chatInformationPage.chatPartnerGroupId)) : ""
|
||||
description: (chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) ? ("@"+(chatInformationPage.privateChatUserInformation.username || chatInformationPage.chatPartnerGroupId)) : ""
|
||||
}
|
||||
|
||||
SilicaFlickable {
|
||||
|
@ -350,7 +355,7 @@ SilicaFlickable {
|
|||
|
||||
ChatInformationEditArea {
|
||||
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")
|
||||
text: chatInformationPage.chatInformation.title
|
||||
|
||||
|
@ -375,13 +380,13 @@ SilicaFlickable {
|
|||
}
|
||||
}
|
||||
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.")
|
||||
headerText: qsTr("Info", "group or user infotext header")
|
||||
multiLine: true
|
||||
text: (chatInformationPage.isPrivateChat ? chatInformationPage.chatPartnerFullInformation.bio : chatInformationPage.groupFullInformation.description) || ""
|
||||
text: ((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) ? chatInformationPage.chatPartnerFullInformation.bio : chatInformationPage.groupFullInformation.description) || ""
|
||||
onSaveButtonClicked: {
|
||||
if(chatInformationPage.isPrivateChat) { // own bio
|
||||
if ((chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat)) { // own bio
|
||||
tdLibWrapper.setBio(textValue);
|
||||
} else { // group info
|
||||
tdLibWrapper.setChatDescription(chatInformationPage.chatInformation.id, textValue);
|
||||
|
@ -391,7 +396,7 @@ SilicaFlickable {
|
|||
|
||||
ChatInformationTextItem {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -408,7 +413,7 @@ SilicaFlickable {
|
|||
visible: !!inviteLinkItem.text
|
||||
ChatInformationTextItem {
|
||||
id: inviteLinkItem
|
||||
text: !isPrivateChat ? chatInformationPage.groupFullInformation.invite_link : ""
|
||||
text: !(chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) ? chatInformationPage.groupFullInformation.invite_link : ""
|
||||
width: parent.width - inviteLinkButton.width
|
||||
}
|
||||
IconButton {
|
||||
|
|
|
@ -27,15 +27,15 @@ import "../../js/functions.js" as Functions
|
|||
|
||||
ChatInformationTabItemBase {
|
||||
id: tabBase
|
||||
loadingText: isPrivateChat ? qsTr("Loading common chats…", "chats you have in common with a user") : qsTr("Loading group members…")
|
||||
loading: ( chatInformationPage.isSuperGroup || chatInformationPage.isPrivateChat) && !chatInformationPage.isChannel
|
||||
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.isSecretChat) && !chatInformationPage.isChannel
|
||||
loadingVisible: loading && membersView.count === 0
|
||||
|
||||
property var chatPartnerCommonGroupsIds: ([]);
|
||||
|
||||
SilicaListView {
|
||||
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
|
||||
height: tabBase.height
|
||||
width: tabBase.width
|
||||
|
@ -68,7 +68,7 @@ ChatInformationTabItemBase {
|
|||
ViewPlaceholder {
|
||||
y: Theme.paddingLarge
|
||||
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 {
|
||||
pictureThumbnail {
|
||||
|
@ -204,7 +204,7 @@ ChatInformationTabItemBase {
|
|||
}
|
||||
}
|
||||
onChatsReceived: {// common chats with user
|
||||
if(isPrivateChat && chats["@extra"] === chatInformationPage.chatPartnerGroupId) {
|
||||
if((isPrivateChat || isSecretChat) && chats["@extra"] === chatInformationPage.chatPartnerGroupId) {
|
||||
tabBase.chatPartnerCommonGroupsIds = chats.chat_ids;
|
||||
delegateModel.applyFilter();
|
||||
// if we set it directly, the views start scrolling
|
||||
|
@ -221,7 +221,7 @@ ChatInformationTabItemBase {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if(chatInformationPage.isPrivateChat) {
|
||||
if(chatInformationPage.isPrivateChat || chatInformationPage.isSecretChat) {
|
||||
tdLibWrapper.getGroupsInCommon(chatInformationPage.chatPartnerGroupId, 200, 0); // we only use the first 200
|
||||
} else if(chatInformationPage.isSuperGroup) {
|
||||
fetchMoreMembersTimer.start();
|
||||
|
|
|
@ -124,14 +124,14 @@ Item {
|
|||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if(!(isPrivateChat && chatPartnerGroupId === myUserId.toString())) {
|
||||
if(!((isPrivateChat || isSecretChat) && chatPartnerGroupId === myUserId.toString())) {
|
||||
tabModel.append({
|
||||
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"
|
||||
});
|
||||
}
|
||||
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({
|
||||
tab:"ChatInformationTabItemSettings",
|
||||
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));
|
||||
}
|
||||
|
||||
function getChatPartnerStatusText(statusType, was_online) {
|
||||
switch(statusType) {
|
||||
case "userStatusEmpty":
|
||||
|
@ -176,6 +177,18 @@ function getChatPartnerStatusText(statusType, was_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) {
|
||||
// chatMemberStatusAdministrator, chatMemberStatusBanned, chatMemberStatusCreator, chatMemberStatusLeft, chatMemberStatusMember, and chatMemberStatusRestricted.
|
||||
switch(statusType) {
|
||||
|
|
|
@ -54,10 +54,10 @@ function emojify(str, emojiSize) {
|
|||
basePath,
|
||||
iconId,
|
||||
'.svg',
|
||||
'" align="middle" width="',
|
||||
emojiSize,
|
||||
'" align="bottom" width="',
|
||||
Math.round(emojiSize * 6 / 5 ),
|
||||
'" height="',
|
||||
emojiSize,
|
||||
Math.round(emojiSize * 6 / 5 ),
|
||||
'"/>'
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,13 +34,14 @@ Page {
|
|||
property int myUserId: tdLibWrapper.getUserInformation().id;
|
||||
|
||||
property bool isPrivateChat: false
|
||||
property bool isSecretChat: false
|
||||
property bool isBasicGroup: false
|
||||
property bool isSuperGroup: false
|
||||
property bool isChannel: false
|
||||
|
||||
property string chatPartnerGroupId
|
||||
|
||||
property bool userIsMember: (isPrivateChat && chatInformation["@type"]) || // should be optimized
|
||||
property bool userIsMember: ((isPrivateChat || isSecretChat ) && chatInformation["@type"]) || // should be optimized
|
||||
(isBasicGroup || isSuperGroup) && (
|
||||
(groupInformation.status["@type"] === "chatMemberStatusMember")
|
||||
|| (groupInformation.status["@type"] === "chatMemberStatusAdministrator")
|
||||
|
|
|
@ -36,8 +36,11 @@ Page {
|
|||
property bool isInitialized: false;
|
||||
readonly property int myUserId: tdLibWrapper.getUserInformation().id;
|
||||
property var chatInformation;
|
||||
property var secretChatDetails;
|
||||
property alias chatPicture: chatPictureThumbnail.photoData
|
||||
property bool isPrivateChat: false;
|
||||
property bool isSecretChat: false;
|
||||
property bool isSecretChatReady: false;
|
||||
property bool isBasicGroup: false;
|
||||
property bool isSuperGroup: false;
|
||||
property bool isChannel: false;
|
||||
|
@ -46,7 +49,7 @@ Page {
|
|||
property int chatOnlineMemberCount: 0;
|
||||
property var emojiProposals;
|
||||
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) && (
|
||||
(chatGroupInformation.status["@type"] === "chatMemberStatusMember")
|
||||
|| (chatGroupInformation.status["@type"] === "chatMemberStatusAdministrator")
|
||||
|
@ -108,6 +111,16 @@ Page {
|
|||
return
|
||||
}
|
||||
var statusText = Functions.getChatPartnerStatusText(chatPartnerInformation.status['@type'], chatPartnerInformation.status.was_online);
|
||||
if (chatPage.secretChatDetails) {
|
||||
var secretChatStatus = Functions.getSecretChatStatus(chatPage.secretChatDetails);
|
||||
if (statusText && secretChatStatus) {
|
||||
statusText += " - ";
|
||||
}
|
||||
if (secretChatStatus) {
|
||||
statusText += secretChatStatus;
|
||||
}
|
||||
}
|
||||
|
||||
if (statusText) {
|
||||
chatStatusText.text = statusText;
|
||||
}
|
||||
|
@ -138,12 +151,16 @@ Page {
|
|||
chatView.currentIndex = -1;
|
||||
chatView.lastReadSentIndex = 0;
|
||||
var chatType = chatInformation.type['@type'];
|
||||
isPrivateChat = ( chatType === "chatTypePrivate" );
|
||||
isPrivateChat = chatType === "chatTypePrivate";
|
||||
isSecretChat = chatType === "chatTypeSecret";
|
||||
isBasicGroup = ( chatType === "chatTypeBasicGroup" );
|
||||
isSuperGroup = ( chatType === "chatTypeSupergroup" );
|
||||
if (isPrivateChat) {
|
||||
if (isPrivateChat || isSecretChat) {
|
||||
chatPartnerInformation = tdLibWrapper.getUserInformation(chatInformation.type.user_id);
|
||||
updateChatPartnerStatusText();
|
||||
if (isSecretChat) {
|
||||
tdLibWrapper.getSecretChat(chatInformation.type.secret_chat_id);
|
||||
}
|
||||
}
|
||||
else if (isBasicGroup) {
|
||||
chatGroupInformation = tdLibWrapper.getBasicGroup(chatInformation.type.basic_group_id);
|
||||
|
@ -291,11 +308,12 @@ Page {
|
|||
|| groupStatusType === "chatMemberStatusAdministrator"
|
||||
|| groupStatusType === "chatMemberStatusCreator"
|
||||
|| (groupStatusType === "chatMemberStatusRestricted" && groupStatus.permissions[privilege])
|
||||
|| (chatPage.isSecretChat && chatPage.isSecretChatReady)
|
||||
}
|
||||
function canPinMessages() {
|
||||
Debug.log("Can we pin messages?");
|
||||
if (chatPage.isPrivateChat) {
|
||||
Debug.log("Private Chat: No!");
|
||||
if (chatPage.isPrivateChat || chatPage.isSecretChat) {
|
||||
Debug.log("Private/Secret Chat: No!");
|
||||
return false;
|
||||
}
|
||||
if (chatPage.chatGroupInformation.status["@type"] === "chatMemberStatusCreator") {
|
||||
|
@ -367,7 +385,7 @@ Page {
|
|||
Connections {
|
||||
target: tdLibWrapper
|
||||
onUserUpdated: {
|
||||
if (isPrivateChat && chatPartnerInformation.id.toString() === userId ) {
|
||||
if ((isPrivateChat || isSecretChat) && chatPartnerInformation.id.toString() === userId ) {
|
||||
chatPartnerInformation = userInformation;
|
||||
updateChatPartnerStatusText();
|
||||
}
|
||||
|
@ -410,6 +428,22 @@ Page {
|
|||
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 {
|
||||
|
@ -507,7 +541,7 @@ Page {
|
|||
Timer {
|
||||
id: chatContactTimeUpdater
|
||||
interval: 60000
|
||||
running: isPrivateChat
|
||||
running: isPrivateChat || isSecretChat
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
updateChatPartnerStatusText();
|
||||
|
@ -543,6 +577,19 @@ Page {
|
|||
|
||||
PullDownMenu {
|
||||
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 {
|
||||
id: joinLeaveChatMenuItem
|
||||
visible: (chatPage.isSuperGroup || chatPage.isBasicGroup) && chatGroupInformation && chatGroupInformation.status["@type"] !== "chatMemberStatusBanned"
|
||||
|
@ -604,14 +651,18 @@ Page {
|
|||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
ProfileThumbnail {
|
||||
id: chatPictureThumbnail
|
||||
replacementStringHint: chatNameText.text
|
||||
Item {
|
||||
width: chatOverviewItem.height
|
||||
height: chatOverviewItem.height
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: chatPage.isPortrait ? Theme.paddingMedium : Theme.paddingSmall
|
||||
|
||||
ProfileThumbnail {
|
||||
id: chatPictureThumbnail
|
||||
replacementStringHint: chatNameText.text
|
||||
width: parent.height
|
||||
height: parent.height
|
||||
|
||||
// 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...
|
||||
|
@ -623,6 +674,28 @@ Page {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
id: chatOverviewItem
|
||||
width: parent.width - chatPictureThumbnail.width - Theme.paddingMedium
|
||||
|
@ -855,8 +928,9 @@ Page {
|
|||
VerticalScrollDecorator {}
|
||||
|
||||
ViewPlaceholder {
|
||||
id: chatViewPlaceholder
|
||||
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 {
|
||||
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"
|
||||
onClicked: {
|
||||
pageStack.push(Qt.resolvedUrl("../pages/PollCreationPage.qml"), { "chatId" : chatInformation.id, groupName: chatInformation.title});
|
||||
|
@ -1340,7 +1414,7 @@ Page {
|
|||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
visible: selectedMessages.every(function(message){
|
||||
return message.can_be_forwarded
|
||||
return message.can_be_forwarded && !chatPage.isSecretChat
|
||||
})
|
||||
width: visible ? Theme.itemSizeMedium : 0
|
||||
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.getRecentStickers();
|
||||
tdLibWrapper.getInstalledStickerSets();
|
||||
tdLibWrapper.getContacts();
|
||||
}
|
||||
|
||||
function initializePage() {
|
||||
|
@ -197,6 +198,10 @@ Page {
|
|||
text: qsTr("Settings")
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("../pages/SettingsPage.qml"))
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("New Chat")
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("../pages/NewChatPage.qml"))
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
|
|
|
@ -46,6 +46,7 @@ namespace {
|
|||
const QString IS_CHANNEL("is_channel");
|
||||
const QString PINNED_MESSAGE_ID("pinned_message_id");
|
||||
const QString _TYPE("@type");
|
||||
const QString SECRET_CHAT_ID("secret_chat_id");
|
||||
}
|
||||
|
||||
class ChatListModel::ChatData
|
||||
|
@ -64,6 +65,7 @@ public:
|
|||
RoleLastMessageText,
|
||||
RoleLastMessageStatus,
|
||||
RoleChatMemberStatus,
|
||||
RoleSecretChatState,
|
||||
RoleIsChannel
|
||||
};
|
||||
|
||||
|
@ -86,6 +88,7 @@ public:
|
|||
bool updateLastReadInboxMessageId(qlonglong messageId);
|
||||
QVector<int> updateLastMessage(const QVariantMap &message);
|
||||
QVector<int> updateGroup(const TDLibWrapper::Group *group);
|
||||
QVector<int> updateSecretChat(const QVariantMap &secretChatDetails);
|
||||
|
||||
public:
|
||||
QVariantMap chatData;
|
||||
|
@ -94,6 +97,7 @@ public:
|
|||
qlonglong groupId;
|
||||
TDLibWrapper::ChatType chatType;
|
||||
TDLibWrapper::ChatMemberStatus memberStatus;
|
||||
TDLibWrapper::SecretChatState secretChatState;
|
||||
QVariantMap userInformation;
|
||||
};
|
||||
|
||||
|
@ -103,6 +107,7 @@ ChatListModel::ChatData::ChatData(const QVariantMap &data, const QVariantMap &us
|
|||
order(data.value(ORDER).toLongLong()),
|
||||
groupId(0),
|
||||
memberStatus(TDLibWrapper::ChatMemberStatusUnknown),
|
||||
secretChatState(TDLibWrapper::SecretChatStateUnknown),
|
||||
userInformation(userInfo)
|
||||
{
|
||||
const QVariantMap type(data.value(TYPE).toMap());
|
||||
|
@ -227,7 +232,11 @@ bool ChatListModel::ChatData::isHidden() const
|
|||
break;
|
||||
case TDLibWrapper::ChatTypeUnknown:
|
||||
case TDLibWrapper::ChatTypePrivate:
|
||||
break;
|
||||
case TDLibWrapper::ChatTypeSecret:
|
||||
if (secretChatState == TDLibWrapper::SecretChatStateClosed) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
|
@ -288,6 +297,18 @@ QVector<int> ChatListModel::ChatData::updateGroup(const TDLibWrapper::Group *gro
|
|||
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)
|
||||
{
|
||||
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(superGroupUpdated(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
|
||||
relativeTimeRefreshTimer = new QTimer(this);
|
||||
|
@ -332,6 +356,7 @@ QHash<int,QByteArray> ChatListModel::roleNames() const
|
|||
roles.insert(ChatData::RoleLastMessageText, "last_message_text");
|
||||
roles.insert(ChatData::RoleLastMessageStatus, "last_message_status");
|
||||
roles.insert(ChatData::RoleChatMemberStatus, "chat_member_status");
|
||||
roles.insert(ChatData::RoleSecretChatState, "secret_chat_state");
|
||||
roles.insert(ChatData::RoleIsChannel, "is_channel");
|
||||
return roles;
|
||||
}
|
||||
|
@ -359,6 +384,7 @@ QVariant ChatListModel::data(const QModelIndex &index, int role) const
|
|||
case ChatData::RoleLastMessageDate: return data->senderMessageDate();
|
||||
case ChatData::RoleLastMessageStatus: return data->senderMessageStatus();
|
||||
case ChatData::RoleChatMemberStatus: return data->memberStatus;
|
||||
case ChatData::RoleSecretChatState: return data->secretChatState;
|
||||
case ChatData::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
|
||||
{
|
||||
return showHiddenChats;
|
||||
|
@ -509,10 +567,19 @@ void ChatListModel::setShowAllChats(bool showAll)
|
|||
void ChatListModel::handleChatDiscovered(const QString &, const QVariantMap &chatToBeAdded)
|
||||
{
|
||||
ChatData *chat = new ChatData(chatToBeAdded, tdLibWrapper->getUserInformation());
|
||||
|
||||
const TDLibWrapper::Group *group = tdLibWrapper->getGroup(chat->groupId);
|
||||
if (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()) {
|
||||
LOG("Hidden chat" << chat->chatId);
|
||||
hiddenChats.insert(chat->chatId, chat);
|
||||
|
@ -707,6 +774,33 @@ void ChatListModel::handleGroupUpdated(qlonglong 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()
|
||||
{
|
||||
LOG("Refreshing timestamps");
|
||||
|
|
|
@ -54,6 +54,8 @@ private slots:
|
|||
void handleMessageSendSucceeded(const QString &messageId, const QString &oldMessageId, const QVariantMap &message);
|
||||
void handleChatNotificationSettingsUpdated(const QString &chatId, const QVariantMap &chatNotificationSettings);
|
||||
void handleGroupUpdated(qlonglong groupId);
|
||||
void handleSecretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat);
|
||||
void handleChatTitleUpdated(const QString &chatId, const QString &title);
|
||||
void handleRelativeTimeRefreshTimer();
|
||||
|
||||
signals:
|
||||
|
@ -65,6 +67,7 @@ private:
|
|||
class ChatData;
|
||||
void addVisibleChat(ChatData *chat);
|
||||
void updateChatVisibility(const TDLibWrapper::Group *group);
|
||||
void updateSecretChatVisibility(const QVariantMap secretChatDetails);
|
||||
int updateChatOrder(int chatIndex);
|
||||
|
||||
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)
|
||||
{
|
||||
if (messageContent.isEmpty()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString contentType = messageContent.value("@type").toString();
|
||||
|
||||
if (contentType == "messageText") {
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "stickermanager.h"
|
||||
#include "tgsplugin.h"
|
||||
#include "fernschreiberutils.h"
|
||||
#include "contactsmodel.h"
|
||||
|
||||
// The default filter can be overridden by QT_LOGGING_RULES envinronment variable, e.g.
|
||||
// QT_LOGGING_RULES="fernschreiber.*=true" harbour-fernschreiber
|
||||
|
@ -96,6 +97,14 @@ int main(int argc, char *argv[])
|
|||
StickerManager stickerManager(tdLibWrapper);
|
||||
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->show();
|
||||
return app->exec();
|
||||
|
|
|
@ -44,6 +44,7 @@ namespace {
|
|||
const QString UNREAD_COUNT("unread_count");
|
||||
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 SECRET_CHAT("secret_chat");
|
||||
|
||||
const QString TYPE("@type");
|
||||
const QString EXTRA("@extra");
|
||||
|
@ -126,6 +127,9 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren
|
|||
handlers.insert("users", &TDLibReceiver::processUsers);
|
||||
handlers.insert("error", &TDLibReceiver::processError);
|
||||
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)
|
||||
|
@ -523,3 +527,22 @@ void TDLibReceiver::processError(const QVariantMap &receivedInformation)
|
|||
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 usersReceived(const QString &extra, const QVariantList &userIds, int totalUsers);
|
||||
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:
|
||||
typedef void (TDLibReceiver::*Handler)(const QVariantMap &);
|
||||
|
@ -145,6 +148,9 @@ private:
|
|||
void processUsers(const QVariantMap &receivedInformation);
|
||||
void processError(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
|
||||
|
|
|
@ -37,8 +37,10 @@ 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 VALUE("value");
|
||||
|
||||
const QString _TYPE("@type");
|
||||
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(chats(QVariantMap)), this, SIGNAL(chatsReceived(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(stickers(QVariantList)), this, SIGNAL(stickersReceived(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(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(contactsImported(QVariantList, QVariantList)), this, SIGNAL(contactsImported(QVariantList, 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)
|
||||
{
|
||||
LOG("Retrieving UserFullInfo");
|
||||
LOG("Retrieving UserFullInfo" << userId);
|
||||
QVariantMap requestObject;
|
||||
requestObject.insert(_TYPE, "getUserFullInfo");
|
||||
requestObject.insert(_EXTRA, userId);
|
||||
|
@ -633,6 +638,16 @@ void TDLibWrapper::createPrivateChat(const QString &userId)
|
|||
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)
|
||||
{
|
||||
LOG("Creating Supergroup Chat");
|
||||
|
@ -802,6 +817,42 @@ void TDLibWrapper::getDeepLinkInfo(const QString &link)
|
|||
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)
|
||||
{
|
||||
LOG("Searching emoji" << queryString);
|
||||
|
@ -823,6 +874,11 @@ QVariantMap TDLibWrapper::getUserInformation(const QString &userId)
|
|||
return this->allUsers.value(userId).toMap();
|
||||
}
|
||||
|
||||
bool TDLibWrapper::hasUserInformation(const QString &userId)
|
||||
{
|
||||
return this->allUsers.contains(userId);
|
||||
}
|
||||
|
||||
QVariantMap TDLibWrapper::getUserInformationByName(const QString &userName)
|
||||
{
|
||||
return this->allUserNames.value(userName).toMap();
|
||||
|
@ -868,6 +924,11 @@ QVariantMap TDLibWrapper::getChat(const QString &chatId)
|
|||
return this->chats.value(chatId).toMap();
|
||||
}
|
||||
|
||||
QVariantMap TDLibWrapper::getSecretChatFromCache(qlonglong secretChatId)
|
||||
{
|
||||
return this->secretChats.value(secretChatId);
|
||||
}
|
||||
|
||||
QString TDLibWrapper::getOptionString(const QString &optionName)
|
||||
{
|
||||
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()
|
||||
{
|
||||
setOptionBoolean("use_storage_optimizer", appSettings->storageOptimizer());
|
||||
|
@ -1154,7 +1227,7 @@ void TDLibWrapper::setInitialParameters()
|
|||
initialParameters.insert("use_file_database", true);
|
||||
initialParameters.insert("use_chat_info_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());
|
||||
QSettings hardwareSettings("/etc/hw-release", QSettings::NativeFormat);
|
||||
initialParameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString());
|
||||
|
@ -1292,6 +1365,14 @@ TDLibWrapper::ChatMemberStatus TDLibWrapper::chatMemberStatusFromString(const QS
|
|||
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
|
||||
{
|
||||
const QString statusType(groupInfo.value(STATUS).toMap().value(_TYPE).toString());
|
||||
|
|
|
@ -79,6 +79,14 @@ public:
|
|||
};
|
||||
Q_ENUM(ChatMemberStatus)
|
||||
|
||||
enum SecretChatState {
|
||||
SecretChatStateUnknown,
|
||||
SecretChatStateClosed,
|
||||
SecretChatStatePending,
|
||||
SecretChatStateReady,
|
||||
};
|
||||
Q_ENUM(SecretChatState)
|
||||
|
||||
class Group {
|
||||
public:
|
||||
Group(qlonglong id) : groupId(id) { }
|
||||
|
@ -94,12 +102,14 @@ public:
|
|||
Q_INVOKABLE TDLibWrapper::ConnectionState getConnectionState();
|
||||
Q_INVOKABLE QVariantMap getUserInformation();
|
||||
Q_INVOKABLE QVariantMap getUserInformation(const QString &userId);
|
||||
Q_INVOKABLE bool hasUserInformation(const QString &userId);
|
||||
Q_INVOKABLE QVariantMap getUserInformationByName(const QString &userName);
|
||||
Q_INVOKABLE QVariantMap getUnreadMessageInformation();
|
||||
Q_INVOKABLE QVariantMap getUnreadChatInformation();
|
||||
Q_INVOKABLE QVariantMap getBasicGroup(qlonglong groupId) const;
|
||||
Q_INVOKABLE QVariantMap getSuperGroup(qlonglong groupId) const;
|
||||
Q_INVOKABLE QVariantMap getChat(const QString &chatId);
|
||||
Q_INVOKABLE QVariantMap getSecretChatFromCache(qlonglong secretChatId);
|
||||
Q_INVOKABLE QString getOptionString(const QString &optionName);
|
||||
Q_INVOKABLE void copyFileToDownloads(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 getUserFullInfo(const QString &userId);
|
||||
Q_INVOKABLE void createPrivateChat(const QString &userId);
|
||||
Q_INVOKABLE void createNewSecretChat(const QString &userId);
|
||||
Q_INVOKABLE void createSupergroupChat(const QString &supergroupId);
|
||||
Q_INVOKABLE void createBasicGroupChat(const QString &basicGroupId);
|
||||
Q_INVOKABLE void getGroupsInCommon(const QString &userId, int limit, int offset);
|
||||
|
@ -162,6 +173,10 @@ public:
|
|||
Q_INVOKABLE void searchPublicChat(const QString &userName);
|
||||
Q_INVOKABLE void joinChatByInviteLink(const QString &inviteLink);
|
||||
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 ;))
|
||||
Q_INVOKABLE void searchEmoji(const QString &queryString);
|
||||
|
@ -172,6 +187,7 @@ public:
|
|||
const Group* getGroup(qlonglong groupId) const;
|
||||
static ChatType chatTypeFromString(const QString &type);
|
||||
static ChatMemberStatus chatMemberStatusFromString(const QString &status);
|
||||
static SecretChatState secretChatStateFromString(const QString &state);
|
||||
|
||||
signals:
|
||||
void versionDetected(const QString &version);
|
||||
|
@ -205,6 +221,8 @@ signals:
|
|||
void messagesDeleted(const QString &chatId, const QVariantList &messageIds);
|
||||
void chatsReceived(const QVariantMap &chats);
|
||||
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 stickersReceived(const QVariantList &stickers);
|
||||
void installedStickerSetsUpdated(const QVariantList &stickerSetIds);
|
||||
|
@ -225,6 +243,7 @@ signals:
|
|||
void chatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId);
|
||||
void usersReceived(const QString &extra, const QVariantList &userIds, int totalUsers);
|
||||
void errorReceived(const int code, const QString &message);
|
||||
void contactsImported(const QVariantList &importerCount, const QVariantList &userIds);
|
||||
|
||||
public slots:
|
||||
void handleVersionDetected(const QString &version);
|
||||
|
@ -243,6 +262,8 @@ public slots:
|
|||
void handleStickerSets(const QVariantList &stickerSets);
|
||||
void handleEmojiSearchCompleted(const QString &queryString, const QVariantList &resultList);
|
||||
void handleOpenWithChanged();
|
||||
void handleSecretChatReceived(qlonglong secretChatId, const QVariantMap &secretChat);
|
||||
void handleSecretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat);
|
||||
void handleStorageOptimizerChanged();
|
||||
|
||||
private:
|
||||
|
@ -267,6 +288,7 @@ private:
|
|||
QVariantMap allUsers;
|
||||
QVariantMap allUserNames;
|
||||
QVariantMap chats;
|
||||
QMap<qlonglong, QVariantMap> secretChats;
|
||||
QVariantMap unreadMessageInformation;
|
||||
QVariantMap unreadChatInformation;
|
||||
QHash<qlonglong,Group*> basicGroups;
|
||||
|
|
|
@ -179,6 +179,10 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Secret Chat</source>
|
||||
<translation>Neuer geheimer Chat</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatInformationTabItemMembersGroups</name>
|
||||
|
@ -260,6 +264,10 @@
|
|||
<source>Mark all messages as read</source>
|
||||
<translation>Nachrichten als gelesen markieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No message in this chat.</source>
|
||||
<translation>Keine Nachricht in diesem Chat</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatPage</name>
|
||||
|
@ -375,6 +383,18 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</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>
|
||||
<name>ChatSelectionPage</name>
|
||||
|
@ -903,6 +923,53 @@
|
|||
<translation>Diese Nachricht wurde weitergeleitet. Ursprünglicher Autor: %1</translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>NotificationManager</name>
|
||||
<message numerus="yes">
|
||||
|
@ -955,6 +1022,10 @@
|
|||
<source>You don't have any chats yet.</source>
|
||||
<translation>Sie haben noch keine Chats.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Chat</source>
|
||||
<translation>Neuer Chat</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PinnedMessageItem</name>
|
||||
|
@ -1625,5 +1696,13 @@
|
|||
<source>You are already a member of this chat.</source>
|
||||
<translation>Sie sind bereits Mitglied dieses Chats.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Closed!</source>
|
||||
<translation>Geschlossen!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pending acknowledgement</source>
|
||||
<translation>Ausstehende Bestätigung</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -179,6 +179,10 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Secret Chat</source>
|
||||
<translation>New Secret Chat</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatInformationTabItemMembersGroups</name>
|
||||
|
@ -260,6 +264,10 @@
|
|||
<source>Mark all messages as read</source>
|
||||
<translation>Mark all messages as read</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No message in this chat.</source>
|
||||
<translation>No message in this chat.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatPage</name>
|
||||
|
@ -375,6 +383,18 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</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>
|
||||
<name>ChatSelectionPage</name>
|
||||
|
@ -903,6 +923,53 @@
|
|||
<translation>This message was forwarded. Original author: %1</translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>NotificationManager</name>
|
||||
<message numerus="yes">
|
||||
|
@ -955,6 +1022,10 @@
|
|||
<source>You don't have any chats yet.</source>
|
||||
<translation>You don't have any chats yet.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Chat</source>
|
||||
<translation>New Chat</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PinnedMessageItem</name>
|
||||
|
@ -1625,5 +1696,13 @@
|
|||
<source>You are already a member of this chat.</source>
|
||||
<translation>You are already a member of this chat.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Closed!</source>
|
||||
<translation>Closed!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pending acknowledgement</source>
|
||||
<translation>Pending acknowledgement</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -176,6 +176,10 @@
|
|||
<numerusform>%1 en línea</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Secret Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatInformationTabItemMembersGroups</name>
|
||||
|
@ -257,6 +261,10 @@
|
|||
<source>Mark all messages as read</source>
|
||||
<translation>Marcar todos como leídos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No message in this chat.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatPage</name>
|
||||
|
@ -365,6 +373,18 @@
|
|||
<numerusform>%1 en línea</numerusform>
|
||||
</translation>
|
||||
</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>
|
||||
<name>ChatSelectionPage</name>
|
||||
|
@ -893,6 +913,53 @@
|
|||
<translation>Este mensaje fue reenviado. Autor original: %1</translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>NotificationManager</name>
|
||||
<message numerus="yes">
|
||||
|
@ -944,6 +1011,10 @@
|
|||
<source>You don't have any chats yet.</source>
|
||||
<translation>No hay todavía ninguna charla.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PinnedMessageItem</name>
|
||||
|
@ -1606,5 +1677,13 @@
|
|||
<source>You are already a member of this chat.</source>
|
||||
<translation>Ya eres miembro de este grupo.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Closed!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pending acknowledgement</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -179,6 +179,10 @@
|
|||
<numerusform>%1 paikalla</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Secret Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatInformationTabItemMembersGroups</name>
|
||||
|
@ -260,6 +264,10 @@
|
|||
<source>Mark all messages as read</source>
|
||||
<translation>Merkitse kaikki viestit luetuiksi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No message in this chat.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatPage</name>
|
||||
|
@ -375,6 +383,18 @@
|
|||
<numerusform>%1 paikalla</numerusform>
|
||||
</translation>
|
||||
</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>
|
||||
<name>ChatSelectionPage</name>
|
||||
|
@ -904,6 +924,53 @@
|
|||
<translation>Välitetty viesti. Alkuperäinen lähettäjä: %1</translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>NotificationManager</name>
|
||||
<message numerus="yes">
|
||||
|
@ -956,6 +1023,10 @@
|
|||
<source>You don't have any chats yet.</source>
|
||||
<translation>Sinulla ei ole vielä keskusteluja.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PinnedMessageItem</name>
|
||||
|
@ -1626,5 +1697,13 @@
|
|||
<source>You are already a member of this chat.</source>
|
||||
<translation>Olet jo tämän ryhmän jäsen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Closed!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pending acknowledgement</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -176,6 +176,10 @@
|
|||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Secret Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatInformationTabItemMembersGroups</name>
|
||||
|
@ -257,6 +261,10 @@
|
|||
<source>Mark all messages as read</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No message in this chat.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatPage</name>
|
||||
|
@ -365,6 +373,18 @@
|
|||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</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>
|
||||
<name>ChatSelectionPage</name>
|
||||
|
@ -893,6 +913,53 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>NotificationManager</name>
|
||||
<message numerus="yes">
|
||||
|
@ -944,6 +1011,10 @@
|
|||
<source>You don't have any chats yet.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PinnedMessageItem</name>
|
||||
|
@ -1606,5 +1677,13 @@
|
|||
<source>You are already a member of this chat.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Closed!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pending acknowledgement</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -179,6 +179,10 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Secret Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatInformationTabItemMembersGroups</name>
|
||||
|
@ -260,6 +264,10 @@
|
|||
<source>Mark all messages as read</source>
|
||||
<translation>Segna tutti i messaggi come già letti</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No message in this chat.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatPage</name>
|
||||
|
@ -375,6 +383,18 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</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>
|
||||
<name>ChatSelectionPage</name>
|
||||
|
@ -903,6 +923,53 @@
|
|||
<translation>Questo è un messaggio inoltrato. Autore originale: %1</translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>NotificationManager</name>
|
||||
<message numerus="yes">
|
||||
|
@ -955,6 +1022,10 @@
|
|||
<source>Loading chat list...</source>
|
||||
<translation>Carica lista chat...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PinnedMessageItem</name>
|
||||
|
@ -1625,5 +1696,13 @@
|
|||
<source>You are already a member of this chat.</source>
|
||||
<translation>Sei già membro di questa chat.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Closed!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pending acknowledgement</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -182,6 +182,10 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Secret Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatInformationTabItemMembersGroups</name>
|
||||
|
@ -263,6 +267,10 @@
|
|||
<source>Mark all messages as read</source>
|
||||
<translation>Zaznacz wszystkie wiadomości jako przeczytane</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No message in this chat.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatPage</name>
|
||||
|
@ -385,6 +393,18 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</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>
|
||||
<name>ChatSelectionPage</name>
|
||||
|
@ -913,6 +933,53 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>NotificationManager</name>
|
||||
<message numerus="yes">
|
||||
|
@ -966,6 +1033,10 @@
|
|||
<source>You don't have any chats yet.</source>
|
||||
<translation>Nie masz jeszcze żadnych czatów.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PinnedMessageItem</name>
|
||||
|
@ -1644,5 +1715,13 @@
|
|||
<source>You are already a member of this chat.</source>
|
||||
<translation>Jesteś już członkiem tego czatu.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Closed!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pending acknowledgement</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -182,6 +182,10 @@
|
|||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Secret Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatInformationTabItemMembersGroups</name>
|
||||
|
@ -263,6 +267,10 @@
|
|||
<source>Mark all messages as read</source>
|
||||
<translation>Отметить все сообщения как прочитанные</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No message in this chat.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatPage</name>
|
||||
|
@ -385,6 +393,18 @@
|
|||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</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>
|
||||
<name>ChatSelectionPage</name>
|
||||
|
@ -913,6 +933,53 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>NotificationManager</name>
|
||||
<message numerus="yes">
|
||||
|
@ -966,6 +1033,10 @@
|
|||
<source>You don't have any chats yet.</source>
|
||||
<translation>Тут пока ничего нет</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PinnedMessageItem</name>
|
||||
|
@ -1644,5 +1715,13 @@
|
|||
<source>You are already a member of this chat.</source>
|
||||
<translation>Вы уже в этом чате.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Closed!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pending acknowledgement</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -179,6 +179,10 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Secret Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatInformationTabItemMembersGroups</name>
|
||||
|
@ -260,6 +264,10 @@
|
|||
<source>Mark all messages as read</source>
|
||||
<translation>Markera alla meddelanden som lästa</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No message in this chat.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatPage</name>
|
||||
|
@ -375,6 +383,18 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</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>
|
||||
<name>ChatSelectionPage</name>
|
||||
|
@ -903,6 +923,53 @@
|
|||
<translation>Detta meddelande är vidarebefordrat. Ursprunglig avsändare: %1</translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>NotificationManager</name>
|
||||
<message numerus="yes">
|
||||
|
@ -955,6 +1022,10 @@
|
|||
<source>You don't have any chats yet.</source>
|
||||
<translation>Du har inga chattar än.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PinnedMessageItem</name>
|
||||
|
@ -1625,5 +1696,13 @@
|
|||
<source>You are already a member of this chat.</source>
|
||||
<translation>Du är redan medlem i den här chatten.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Closed!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pending acknowledgement</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -176,6 +176,10 @@
|
|||
<numerusform>%1 位在线</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Secret Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatInformationTabItemMembersGroups</name>
|
||||
|
@ -257,6 +261,10 @@
|
|||
<source>Mark all messages as read</source>
|
||||
<translation>标记全部消息为已读</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No message in this chat.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatPage</name>
|
||||
|
@ -365,6 +373,18 @@
|
|||
<numerusform>%1 位在线</numerusform>
|
||||
</translation>
|
||||
</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>
|
||||
<name>ChatSelectionPage</name>
|
||||
|
@ -893,6 +913,53 @@
|
|||
<translation>此消息为转发消息,原作者: %1</translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>NotificationManager</name>
|
||||
<message numerus="yes">
|
||||
|
@ -944,6 +1011,10 @@
|
|||
<source>You don't have any chats yet.</source>
|
||||
<translation>你还没有任何对话。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PinnedMessageItem</name>
|
||||
|
@ -1606,5 +1677,13 @@
|
|||
<source>You are already a member of this chat.</source>
|
||||
<translation>你已是此对话成员。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Closed!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pending acknowledgement</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -179,6 +179,10 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Secret Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatInformationTabItemMembersGroups</name>
|
||||
|
@ -260,6 +264,10 @@
|
|||
<source>Mark all messages as read</source>
|
||||
<translation>Mark all messages as read</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No message in this chat.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatPage</name>
|
||||
|
@ -375,6 +383,18 @@
|
|||
<numerusform>%1 online</numerusform>
|
||||
</translation>
|
||||
</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>
|
||||
<name>ChatSelectionPage</name>
|
||||
|
@ -903,6 +923,53 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>NotificationManager</name>
|
||||
<message numerus="yes">
|
||||
|
@ -955,6 +1022,10 @@
|
|||
<source>You don't have any chats yet.</source>
|
||||
<translation>You don't have any chats yet.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PinnedMessageItem</name>
|
||||
|
@ -1625,5 +1696,13 @@
|
|||
<source>You are already a member of this chat.</source>
|
||||
<translation>You are already a member of this chat.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Closed!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pending acknowledgement</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
Loading…
Reference in a new issue