Synchronize contacts with Telegram

This commit is contained in:
Sebastian Wolf 2020-11-26 22:18:51 +01:00
parent 7ab58d3730
commit deacb7f0ea
20 changed files with 270 additions and 9 deletions

View file

@ -28,24 +28,56 @@ Page {
property bool isLoading: true; property bool isLoading: true;
onStatusChanged: {
if (status === PageStatus.Active) {
contactsModel.hydrateContacts();
contactsListView.model = contactsModel;
newChatPage.isLoading = false;
}
}
function resetFocus() { function resetFocus() {
contactsSearchField.focus = false; contactsSearchField.focus = false;
newChatPage.focus = true; newChatPage.focus = true;
} }
function reloadContacts() {
contactsModel.hydrateContacts();
contactsListView.model = contactsModel;
newChatPage.isLoading = false;
}
onStatusChanged: {
if (status === PageStatus.Active) {
reloadContacts();
}
}
Connections {
target: contactsModel
onErrorSynchronizingContacts: {
reloadContacts();
appNotification.show(qsTr("Could not synchronize your contacts with Telegram."));
}
}
Connections {
target: tdLibWrapper
onContactsImported: {
reloadContacts();
appNotification.show(qsTr("Contacts successfully synchronized with Telegram."));
}
}
SilicaFlickable { SilicaFlickable {
id: newChatContainer id: newChatContainer
contentHeight: newChatPage.height contentHeight: newChatPage.height
anchors.fill: parent anchors.fill: parent
PullDownMenu {
visible: contactsModel.canSynchronizeContacts()
MenuItem {
onClicked: {
newChatPage.isLoading = true;
contactsModel.synchronizeContacts();
}
text: qsTr("Synchronize Contacts with Telegram")
}
}
Column { Column {
id: newChatPageColumn id: newChatPageColumn
width: newChatPage.width width: newChatPage.width

View file

@ -325,6 +325,7 @@ ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper) : showHiddenChats(false
connect(tdLibWrapper, SIGNAL(basicGroupUpdated(qlonglong)), this, SLOT(handleGroupUpdated(qlonglong))); connect(tdLibWrapper, SIGNAL(basicGroupUpdated(qlonglong)), this, SLOT(handleGroupUpdated(qlonglong)));
connect(tdLibWrapper, SIGNAL(secretChatUpdated(QString, QVariantMap)), this, SLOT(handleSecretChatUpdated(QString, QVariantMap))); connect(tdLibWrapper, SIGNAL(secretChatUpdated(QString, QVariantMap)), this, SLOT(handleSecretChatUpdated(QString, QVariantMap)));
connect(tdLibWrapper, SIGNAL(secretChatReceived(QString, QVariantMap)), this, SLOT(handleSecretChatUpdated(QString, QVariantMap))); connect(tdLibWrapper, SIGNAL(secretChatReceived(QString, QVariantMap)), this, SLOT(handleSecretChatUpdated(QString, QVariantMap)));
connect(tdLibWrapper, SIGNAL(chatTitleUpdated(QString, QString)), this, SLOT(handleChatTitleUpdated(QString, QString)));
// Don't start the timer until we have at least one chat // Don't start the timer until we have at least one chat
relativeTimeRefreshTimer = new QTimer(this); relativeTimeRefreshTimer = new QTimer(this);
@ -779,6 +780,27 @@ void ChatListModel::handleSecretChatUpdated(const QString &secretChatId, const Q
updateSecretChatVisibility(secretChat); updateSecretChatVisibility(secretChat);
} }
void ChatListModel::handleChatTitleUpdated(const QString &chatId, const QString &title)
{
qlonglong chatIdLongLong = chatId.toLongLong();
if (chatIndexMap.contains(chatIdLongLong)) {
LOG("Updating title for" << chatId);
const int chatIndex = chatIndexMap.value(chatIdLongLong);
ChatData *chat = chatList.at(chatIndex);
chat->chatData.insert(TITLE, title);
QVector<int> changedRoles;
changedRoles.append(ChatData::RoleTitle);
const QModelIndex modelIndex(index(chatIndex));
emit dataChanged(modelIndex, modelIndex, changedRoles);
} else {
ChatData *chat = hiddenChats.value(chatId.toLongLong());
if (chat) {
LOG("Updating title for hidden chat" << chatId);
chat->chatData.insert(TITLE, title);
}
}
}
void ChatListModel::handleRelativeTimeRefreshTimer() void ChatListModel::handleRelativeTimeRefreshTimer()
{ {
LOG("Refreshing timestamps"); LOG("Refreshing timestamps");

View file

@ -55,6 +55,7 @@ private slots:
void handleChatNotificationSettingsUpdated(const QString &chatId, const QVariantMap &chatNotificationSettings); void handleChatNotificationSettingsUpdated(const QString &chatId, const QVariantMap &chatNotificationSettings);
void handleGroupUpdated(qlonglong groupId); void handleGroupUpdated(qlonglong groupId);
void handleSecretChatUpdated(const QString &secretChatId, const QVariantMap &secretChat); void handleSecretChatUpdated(const QString &secretChatId, const QVariantMap &secretChat);
void handleChatTitleUpdated(const QString &chatId, const QString &title);
void handleRelativeTimeRefreshTimer(); void handleRelativeTimeRefreshTimer();
signals: signals:

View file

@ -38,8 +38,17 @@ ContactsModel::ContactsModel(TDLibWrapper *tdLibWrapper, QObject *parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
{ {
this->tdLibWrapper = tdLibWrapper; this->tdLibWrapper = tdLibWrapper;
connect(this->tdLibWrapper, SIGNAL(usersReceived(QString, QVariantList, int)), this, SLOT(handleUsersReceived(QString, QVariantList, int))); 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;
}
} }
int ContactsModel::rowCount(const QModelIndex &) const int ContactsModel::rowCount(const QModelIndex &) const
@ -143,3 +152,36 @@ void ContactsModel::applyFilter(const QString &filter)
} }
endResetModel(); endResetModel();
} }
void 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);
}
emit contactsSynchronized();
} else {
LOG("Error selecting contacts from database!");
emit errorSynchronizingContacts();
}
}
bool ContactsModel::canSynchronizeContacts()
{
return this->canUseDeviceContacts;
}

View file

@ -22,6 +22,8 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QVariantList> #include <QVariantList>
#include <QSqlDatabase>
#include <QSqlQuery>
#include "tdlibwrapper.h" #include "tdlibwrapper.h"
@ -36,6 +38,12 @@ public:
Q_INVOKABLE void hydrateContacts(); Q_INVOKABLE void hydrateContacts();
Q_INVOKABLE void applyFilter(const QString &filter); Q_INVOKABLE void applyFilter(const QString &filter);
Q_INVOKABLE void synchronizeContacts();
Q_INVOKABLE bool canSynchronizeContacts();
signals:
void contactsSynchronized();
void errorSynchronizingContacts();
public slots: public slots:
void handleUsersReceived(const QString &extra, const QVariantList &userIds, int totalUsers); void handleUsersReceived(const QString &extra, const QVariantList &userIds, int totalUsers);
@ -46,6 +54,8 @@ private:
QVariantList filteredContacts; QVariantList filteredContacts;
QList<QString> contactIds; QList<QString> contactIds;
QString filter; QString filter;
QSqlDatabase deviceContactsDatabase;
bool canUseDeviceContacts;
}; };
#endif // CONTACTSMODEL_H #endif // CONTACTSMODEL_H

View file

@ -129,6 +129,7 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren
handlers.insert("ok", &TDLibReceiver::nop); handlers.insert("ok", &TDLibReceiver::nop);
handlers.insert("secretChat", &TDLibReceiver::processSecretChat); handlers.insert("secretChat", &TDLibReceiver::processSecretChat);
handlers.insert("updateSecretChat", &TDLibReceiver::processUpdateSecretChat); handlers.insert("updateSecretChat", &TDLibReceiver::processUpdateSecretChat);
handlers.insert("importedContacts", &TDLibReceiver::processImportedContacts);
} }
void TDLibReceiver::setActive(bool active) void TDLibReceiver::setActive(bool active)
@ -539,3 +540,9 @@ void TDLibReceiver::processUpdateSecretChat(const QVariantMap &receivedInformati
QVariantMap updatedSecretChat = receivedInformation.value(SECRET_CHAT).toMap(); QVariantMap updatedSecretChat = receivedInformation.value(SECRET_CHAT).toMap();
emit secretChatUpdated(updatedSecretChat.value(ID).toString(), updatedSecretChat); emit secretChatUpdated(updatedSecretChat.value(ID).toString(), updatedSecretChat);
} }
void TDLibReceiver::processImportedContacts(const QVariantMap &receivedInformation)
{
LOG("Contacts were imported");
emit contactsImported(receivedInformation.value("importer_count").toList(), receivedInformation.value("user_ids").toList());
}

View file

@ -87,6 +87,8 @@ signals:
void errorReceived(const int code, const QString &message); void errorReceived(const int code, const QString &message);
void secretChat(const QString &secretChatId, const QVariantMap &secretChat); void secretChat(const QString &secretChatId, const QVariantMap &secretChat);
void secretChatUpdated(const QString &secretChatId, const QVariantMap &secretChat); void secretChatUpdated(const QString &secretChatId, const QVariantMap &secretChat);
void contactsImported(const QVariantList &importerCount, const QVariantList &userIds);
private: private:
typedef void (TDLibReceiver::*Handler)(const QVariantMap &); typedef void (TDLibReceiver::*Handler)(const QVariantMap &);
@ -148,6 +150,7 @@ private:
void nop(const QVariantMap &receivedInformation); void nop(const QVariantMap &receivedInformation);
void processSecretChat(const QVariantMap &receivedInformation); void processSecretChat(const QVariantMap &receivedInformation);
void processUpdateSecretChat(const QVariantMap &receivedInformation); void processUpdateSecretChat(const QVariantMap &receivedInformation);
void processImportedContacts(const QVariantMap &receivedInformation);
}; };
#endif // TDLIBRECEIVER_H #endif // TDLIBRECEIVER_H

View file

@ -116,6 +116,7 @@ TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface,
connect(this->tdLibReceiver, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)), this, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong))); connect(this->tdLibReceiver, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)), this, SIGNAL(chatPinnedMessageUpdated(qlonglong, qlonglong)));
connect(this->tdLibReceiver, SIGNAL(usersReceived(QString, QVariantList, int)), this, SIGNAL(usersReceived(QString, QVariantList, int))); connect(this->tdLibReceiver, SIGNAL(usersReceived(QString, QVariantList, int)), this, SIGNAL(usersReceived(QString, QVariantList, int)));
connect(this->tdLibReceiver, SIGNAL(errorReceived(int, QString)), this, SIGNAL(errorReceived(int, QString))); connect(this->tdLibReceiver, SIGNAL(errorReceived(int, QString)), this, SIGNAL(errorReceived(int, QString)));
connect(this->tdLibReceiver, SIGNAL(contactsImported(QVariantList, QVariantList)), this, SIGNAL(contactsImported(QVariantList, QVariantList)));
connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList))); connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList)));
@ -842,6 +843,15 @@ void TDLibWrapper::closeSecretChat(const QString &secretChatId)
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::importContacts(const QVariantList &contacts)
{
LOG("Importing contacts");
QVariantMap requestObject;
requestObject.insert(_TYPE, "importContacts");
requestObject.insert("contacts", contacts);
this->sendRequest(requestObject);
}
void TDLibWrapper::searchEmoji(const QString &queryString) void TDLibWrapper::searchEmoji(const QString &queryString)
{ {
LOG("Searching emoji" << queryString); LOG("Searching emoji" << queryString);

View file

@ -176,6 +176,7 @@ public:
Q_INVOKABLE void getContacts(); Q_INVOKABLE void getContacts();
Q_INVOKABLE void getSecretChat(const QString &secretChatId); Q_INVOKABLE void getSecretChat(const QString &secretChatId);
Q_INVOKABLE void closeSecretChat(const QString &secretChatId); Q_INVOKABLE void closeSecretChat(const QString &secretChatId);
Q_INVOKABLE void importContacts(const QVariantList &contacts);
// Others (candidates for extraction ;)) // Others (candidates for extraction ;))
Q_INVOKABLE void searchEmoji(const QString &queryString); Q_INVOKABLE void searchEmoji(const QString &queryString);
@ -242,6 +243,7 @@ signals:
void chatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId); void chatPinnedMessageUpdated(qlonglong chatId, qlonglong pinnedMessageId);
void usersReceived(const QString &extra, const QVariantList &userIds, int totalUsers); void usersReceived(const QString &extra, const QVariantList &userIds, int totalUsers);
void errorReceived(const int code, const QString &message); void errorReceived(const int code, const QString &message);
void contactsImported(const QVariantList &importerCount, const QVariantList &userIds);
public slots: public slots:
void handleVersionDetected(const QString &version); void handleVersionDetected(const QString &version);

View file

@ -944,6 +944,18 @@
<source>Loading contacts...</source> <source>Loading contacts...</source>
<translation>Lade Kontakte...</translation> <translation>Lade Kontakte...</translation>
</message> </message>
<message>
<source>Synchronize Contacts with Telegram</source>
<translation>Kontakte mit Telegram synchronisieren</translation>
</message>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Konnte Ihre Kontakte nicht mit Telegram synchronisieren.</translation>
</message>
<message>
<source>Contacts successfully synchronized with Telegram.</source>
<translation>Die Kontakte wurden erfolgreich mit Telegram synchronisiert.</translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -944,6 +944,18 @@
<source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source> <source>Transport-encrypted, uses Telegram Cloud, sharable across devices</source>
<translation>Transport-encrypted, uses Telegram Cloud, sharable across devices</translation> <translation>Transport-encrypted, uses Telegram Cloud, sharable across devices</translation>
</message> </message>
<message>
<source>Synchronize Contacts with Telegram</source>
<translation>Synchronize Contacts with Telegram</translation>
</message>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation>Could not synchronize your contacts with Telegram.</translation>
</message>
<message>
<source>Contacts successfully synchronized with Telegram.</source>
<translation>Contacts successfully synchronized with Telegram.</translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -934,6 +934,18 @@
<source>Search a contact...</source> <source>Search a contact...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Synchronize Contacts with Telegram</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Contacts successfully synchronized with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -945,6 +945,18 @@
<source>Search a contact...</source> <source>Search a contact...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Synchronize Contacts with Telegram</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Contacts successfully synchronized with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -934,6 +934,18 @@
<source>Search a contact...</source> <source>Search a contact...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Synchronize Contacts with Telegram</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Contacts successfully synchronized with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -944,6 +944,18 @@
<source>Search a contact...</source> <source>Search a contact...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Synchronize Contacts with Telegram</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Contacts successfully synchronized with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -954,6 +954,18 @@
<source>Search a contact...</source> <source>Search a contact...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Synchronize Contacts with Telegram</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Contacts successfully synchronized with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -954,6 +954,18 @@
<source>Search a contact...</source> <source>Search a contact...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Synchronize Contacts with Telegram</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Contacts successfully synchronized with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -944,6 +944,18 @@
<source>Search a contact...</source> <source>Search a contact...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Synchronize Contacts with Telegram</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Contacts successfully synchronized with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -934,6 +934,18 @@
<source>Search a contact...</source> <source>Search a contact...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Synchronize Contacts with Telegram</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Contacts successfully synchronized with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>

View file

@ -944,6 +944,18 @@
<source>Search a contact...</source> <source>Search a contact...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Synchronize Contacts with Telegram</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not synchronize your contacts with Telegram.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Contacts successfully synchronized with Telegram.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NotificationManager</name> <name>NotificationManager</name>