Add 'mark chat as read/unread' feature, fixes #240

This commit is contained in:
Sebastian Wolf 2020-12-31 00:19:36 +01:00
parent fa0aaf431e
commit 232049422a
No known key found for this signature in database
GPG key ID: CEA9522B5F38A90A
20 changed files with 164 additions and 2 deletions

View file

@ -22,6 +22,7 @@ PhotoTextsListItem {
tertiaryText.text: ( last_message_date ? ( last_message_date.length === 0 ? "" : Functions.getDateTimeElapsed(last_message_date) + Emoji.emojify(last_message_status, tertiaryText.font.pixelSize) ) : "" ) tertiaryText.text: ( last_message_date ? ( last_message_date.length === 0 ? "" : Functions.getDateTimeElapsed(last_message_date) + Emoji.emojify(last_message_status, tertiaryText.font.pixelSize) ) : "" )
unreadCount: unread_count unreadCount: unread_count
isSecret: ( chat_type === TelegramAPI.ChatTypeSecret ) isSecret: ( chat_type === TelegramAPI.ChatTypeSecret )
isMarkedAsUnread: is_marked_as_unread
openMenuOnPressAndHold: true//chat_id != overviewPage.ownUserId openMenuOnPressAndHold: true//chat_id != overviewPage.ownUserId
@ -45,10 +46,27 @@ PhotoTextsListItem {
visible: unread_count > 0 visible: unread_count > 0
onClicked: { onClicked: {
tdLibWrapper.viewMessage(chat_id, display.last_message.id, true); tdLibWrapper.viewMessage(chat_id, display.last_message.id, true);
tdLibWrapper.toggleChatIsMarkedAsUnread(chat_id, false);
} }
text: qsTr("Mark all messages as read") text: qsTr("Mark all messages as read")
} }
MenuItem {
visible: unread_count === 0 && !is_marked_as_unread
onClicked: {
tdLibWrapper.toggleChatIsMarkedAsUnread(chat_id, true);
}
text: qsTr("Mark chat as unread")
}
MenuItem {
visible: unread_count === 0 && is_marked_as_unread
onClicked: {
tdLibWrapper.toggleChatIsMarkedAsUnread(chat_id, false);
}
text: qsTr("Mark chat as read")
}
MenuItem { MenuItem {
visible: chat_id != listItem.ownUserId visible: chat_id != listItem.ownUserId
onClicked: { onClicked: {

View file

@ -13,6 +13,7 @@ ListItem {
property int unreadCount: 0 property int unreadCount: 0
property bool isSecret: false property bool isSecret: false
property bool isVerified: false property bool isVerified: false
property bool isMarkedAsUnread: false
property alias pictureThumbnail: pictureThumbnail property alias pictureThumbnail: pictureThumbnail
contentHeight: mainRow.height + separator.height + 2 * Theme.paddingMedium contentHeight: mainRow.height + separator.height + 2 * Theme.paddingMedium
@ -75,7 +76,7 @@ ListItem {
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
radius: parent.width / 2 radius: parent.width / 2
visible: chatListViewItem.unreadCount > 0 visible: chatListViewItem.unreadCount > 0 || chatListViewItem.isMarkedAsUnread
} }
Text { Text {
@ -84,7 +85,7 @@ ListItem {
font.bold: true font.bold: true
color: Theme.primaryColor color: Theme.primaryColor
anchors.centerIn: chatUnreadMessagesCountBackground anchors.centerIn: chatUnreadMessagesCountBackground
visible: chatUnreadMessagesCountBackground.visible visible: chatListViewItem.unreadCount > 0
text: chatListViewItem.unreadCount > 99 ? "99+" : chatListViewItem.unreadCount text: chatListViewItem.unreadCount > 99 ? "99+" : chatListViewItem.unreadCount
} }
} }

View file

@ -171,6 +171,7 @@ Page {
stickerManager.setNeedsReload(false); stickerManager.setNeedsReload(false);
} }
tdLibWrapper.getChatPinnedMessage(chatInformation.id); tdLibWrapper.getChatPinnedMessage(chatInformation.id);
tdLibWrapper.toggleChatIsMarkedAsUnread(chatInformation.id, false);
} }
function getMessageStatusText(message, listItemIndex, lastReadSentIndex, useElapsed) { function getMessageStatusText(message, listItemIndex, lastReadSentIndex, useElapsed) {

View file

@ -46,6 +46,7 @@ namespace {
const QString SENDING_STATE("sending_state"); const QString SENDING_STATE("sending_state");
const QString IS_CHANNEL("is_channel"); const QString IS_CHANNEL("is_channel");
const QString IS_VERIFIED("is_verified"); const QString IS_VERIFIED("is_verified");
const QString IS_MARKED_AS_UNREAD("is_marked_as_unread");
const QString PINNED_MESSAGE_ID("pinned_message_id"); const QString PINNED_MESSAGE_ID("pinned_message_id");
const QString _TYPE("@type"); const QString _TYPE("@type");
const QString SECRET_CHAT_ID("secret_chat_id"); const QString SECRET_CHAT_ID("secret_chat_id");
@ -72,6 +73,7 @@ public:
QString senderMessageStatus() const; QString senderMessageStatus() const;
bool isChannel() const; bool isChannel() const;
bool isHidden() const; bool isHidden() const;
bool isMarkedAsUnread() const;
bool updateUnreadCount(int unreadCount); bool updateUnreadCount(int unreadCount);
bool updateLastReadInboxMessageId(qlonglong messageId); bool updateLastReadInboxMessageId(qlonglong messageId);
QVector<int> updateLastMessage(const QVariantMap &message); QVector<int> updateLastMessage(const QVariantMap &message);
@ -244,6 +246,11 @@ bool ChatListModel::ChatData::isHidden() const
return false; return false;
} }
bool ChatListModel::ChatData::isMarkedAsUnread() const
{
return chatData.value(IS_MARKED_AS_UNREAD).toBool();
}
bool ChatListModel::ChatData::updateUnreadCount(int count) bool ChatListModel::ChatData::updateUnreadCount(int count)
{ {
const int prevUnreadCount(unreadCount()); const int prevUnreadCount(unreadCount());
@ -335,6 +342,7 @@ ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper) : showHiddenChats(false
connect(tdLibWrapper, SIGNAL(secretChatUpdated(qlonglong, QVariantMap)), this, SLOT(handleSecretChatUpdated(qlonglong, QVariantMap))); 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(secretChatReceived(qlonglong, QVariantMap)), this, SLOT(handleSecretChatUpdated(qlonglong, QVariantMap)));
connect(tdLibWrapper, SIGNAL(chatTitleUpdated(QString, QString)), this, SLOT(handleChatTitleUpdated(QString, QString))); connect(tdLibWrapper, SIGNAL(chatTitleUpdated(QString, QString)), this, SLOT(handleChatTitleUpdated(QString, QString)));
connect(tdLibWrapper, SIGNAL(chatIsMarkedAsUnreadUpdated(qlonglong, bool)), this, SLOT(handleChatIsMarkedAsUnreadUpdated(qlonglong, bool)));
// 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);
@ -368,6 +376,7 @@ QHash<int,QByteArray> ChatListModel::roleNames() const
roles.insert(ChatListModel::RoleSecretChatState, "secret_chat_state"); roles.insert(ChatListModel::RoleSecretChatState, "secret_chat_state");
roles.insert(ChatListModel::RoleIsVerified, "is_verified"); roles.insert(ChatListModel::RoleIsVerified, "is_verified");
roles.insert(ChatListModel::RoleIsChannel, "is_channel"); roles.insert(ChatListModel::RoleIsChannel, "is_channel");
roles.insert(ChatListModel::RoleIsMarkedAsUnread, "is_marked_as_unread");
roles.insert(ChatListModel::RoleFilter, "filter"); roles.insert(ChatListModel::RoleFilter, "filter");
return roles; return roles;
} }
@ -398,6 +407,7 @@ QVariant ChatListModel::data(const QModelIndex &index, int role) const
case ChatListModel::RoleSecretChatState: return data->secretChatState; case ChatListModel::RoleSecretChatState: return data->secretChatState;
case ChatListModel::RoleIsVerified: return data->verified; case ChatListModel::RoleIsVerified: return data->verified;
case ChatListModel::RoleIsChannel: return data->isChannel(); case ChatListModel::RoleIsChannel: return data->isChannel();
case ChatListModel::RoleIsMarkedAsUnread: return data->isMarkedAsUnread();
case ChatListModel::RoleFilter: return QString(data->title() + " " + data->senderMessageText()).trimmed(); case ChatListModel::RoleFilter: return QString(data->title() + " " + data->senderMessageText()).trimmed();
} }
} }
@ -814,6 +824,26 @@ void ChatListModel::handleChatTitleUpdated(const QString &chatId, const QString
} }
} }
void ChatListModel::handleChatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread)
{
if (chatIndexMap.contains(chatId)) {
LOG("Updating chat is marked as unread for" << chatId << chatIsMarkedAsUnread);
const int chatIndex = chatIndexMap.value(chatId);
ChatData *chat = chatList.at(chatIndex);
chat->chatData.insert(IS_MARKED_AS_UNREAD, chatIsMarkedAsUnread);
QVector<int> changedRoles;
changedRoles.append(ChatListModel::RoleIsMarkedAsUnread);
const QModelIndex modelIndex(index(chatIndex));
emit dataChanged(modelIndex, modelIndex, changedRoles);
} else {
ChatData *chat = hiddenChats.value(chatId);
if (chat) {
LOG("Updating chat is marked as unread for hidden chat" << chatId);
chat->chatData.insert(IS_MARKED_AS_UNREAD, chatIsMarkedAsUnread);
}
}
}
void ChatListModel::handleRelativeTimeRefreshTimer() void ChatListModel::handleRelativeTimeRefreshTimer()
{ {
LOG("Refreshing timestamps"); LOG("Refreshing timestamps");

View file

@ -46,6 +46,7 @@ public:
RoleSecretChatState, RoleSecretChatState,
RoleIsVerified, RoleIsVerified,
RoleIsChannel, RoleIsChannel,
RoleIsMarkedAsUnread,
RoleFilter RoleFilter
}; };
@ -76,6 +77,7 @@ private slots:
void handleGroupUpdated(qlonglong groupId); void handleGroupUpdated(qlonglong groupId);
void handleSecretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat); void handleSecretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat);
void handleChatTitleUpdated(const QString &chatId, const QString &title); void handleChatTitleUpdated(const QString &chatId, const QString &title);
void handleChatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread);
void handleRelativeTimeRefreshTimer(); void handleRelativeTimeRefreshTimer();
signals: signals:

View file

@ -133,6 +133,7 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren
handlers.insert("updateSecretChat", &TDLibReceiver::processUpdateSecretChat); handlers.insert("updateSecretChat", &TDLibReceiver::processUpdateSecretChat);
handlers.insert("importedContacts", &TDLibReceiver::processImportedContacts); handlers.insert("importedContacts", &TDLibReceiver::processImportedContacts);
handlers.insert("updateMessageEdited", &TDLibReceiver::processUpdateMessageEdited); handlers.insert("updateMessageEdited", &TDLibReceiver::processUpdateMessageEdited);
handlers.insert("updateChatIsMarkedAsUnread", &TDLibReceiver::processUpdateChatIsMarkedAsUnread);
} }
void TDLibReceiver::setActive(bool active) void TDLibReceiver::setActive(bool active)
@ -569,3 +570,9 @@ void TDLibReceiver::processImportedContacts(const QVariantMap &receivedInformati
LOG("Contacts were imported"); LOG("Contacts were imported");
emit contactsImported(receivedInformation.value("importer_count").toList(), receivedInformation.value("user_ids").toList()); emit contactsImported(receivedInformation.value("importer_count").toList(), receivedInformation.value("user_ids").toList());
} }
void TDLibReceiver::processUpdateChatIsMarkedAsUnread(const QVariantMap &receivedInformation)
{
LOG("The unread state of a chat was updated");
emit chatIsMarkedAsUnreadUpdated(receivedInformation.value(CHAT_ID).toLongLong(), receivedInformation.value("is_marked_as_unread").toBool());
}

View file

@ -90,6 +90,7 @@ signals:
void secretChat(qlonglong secretChatId, const QVariantMap &secretChat); void secretChat(qlonglong secretChatId, const QVariantMap &secretChat);
void secretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat); void secretChatUpdated(qlonglong secretChatId, const QVariantMap &secretChat);
void contactsImported(const QVariantList &importerCount, const QVariantList &userIds); void contactsImported(const QVariantList &importerCount, const QVariantList &userIds);
void chatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread);
private: private:
typedef void (TDLibReceiver::*Handler)(const QVariantMap &); typedef void (TDLibReceiver::*Handler)(const QVariantMap &);
@ -155,6 +156,7 @@ private:
void processUpdateSecretChat(const QVariantMap &receivedInformation); void processUpdateSecretChat(const QVariantMap &receivedInformation);
void processUpdateMessageEdited(const QVariantMap &receivedInformation); void processUpdateMessageEdited(const QVariantMap &receivedInformation);
void processImportedContacts(const QVariantMap &receivedInformation); void processImportedContacts(const QVariantMap &receivedInformation);
void processUpdateChatIsMarkedAsUnread(const QVariantMap &receivedInformation);
}; };
#endif // TDLIBRECEIVER_H #endif // TDLIBRECEIVER_H

View file

@ -125,6 +125,7 @@ TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface,
connect(this->tdLibReceiver, SIGNAL(errorReceived(int, QString, QString)), this, SLOT(handleErrorReceived(int, QString, QString))); connect(this->tdLibReceiver, SIGNAL(errorReceived(int, QString, QString)), this, SLOT(handleErrorReceived(int, QString, QString)));
connect(this->tdLibReceiver, SIGNAL(contactsImported(QVariantList, QVariantList)), this, SIGNAL(contactsImported(QVariantList, QVariantList))); connect(this->tdLibReceiver, SIGNAL(contactsImported(QVariantList, QVariantList)), this, SIGNAL(contactsImported(QVariantList, QVariantList)));
connect(this->tdLibReceiver, SIGNAL(messageEditedUpdated(qlonglong, qlonglong, QVariantMap)), this, SIGNAL(messageEditedUpdated(qlonglong, qlonglong, QVariantMap))); connect(this->tdLibReceiver, SIGNAL(messageEditedUpdated(qlonglong, qlonglong, QVariantMap)), this, SIGNAL(messageEditedUpdated(qlonglong, qlonglong, QVariantMap)));
connect(this->tdLibReceiver, SIGNAL(chatIsMarkedAsUnreadUpdated(qlonglong, bool)), this, SIGNAL(chatIsMarkedAsUnreadUpdated(qlonglong, bool)));
connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList))); connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList)));
@ -971,6 +972,16 @@ void TDLibWrapper::readAllChatMentions(qlonglong chatId)
this->sendRequest(requestObject); this->sendRequest(requestObject);
} }
void TDLibWrapper::toggleChatIsMarkedAsUnread(qlonglong chatId, bool isMarkedAsUnread)
{
LOG("Toggle chat is marked as unread" << chatId << isMarkedAsUnread);
QVariantMap requestObject;
requestObject.insert(_TYPE, "toggleChatIsMarkedAsUnread");
requestObject.insert(CHAT_ID, chatId);
requestObject.insert("is_marked_as_unread", isMarkedAsUnread);
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

@ -182,6 +182,7 @@ public:
Q_INVOKABLE void searchChatMessages(qlonglong chatId, const QString &query, qlonglong fromMessageId = 0); Q_INVOKABLE void searchChatMessages(qlonglong chatId, const QString &query, qlonglong fromMessageId = 0);
Q_INVOKABLE void searchPublicChats(const QString &query); Q_INVOKABLE void searchPublicChats(const QString &query);
Q_INVOKABLE void readAllChatMentions(qlonglong chatId); Q_INVOKABLE void readAllChatMentions(qlonglong chatId);
Q_INVOKABLE void toggleChatIsMarkedAsUnread(qlonglong chatId, bool isMarkedAsUnread);
// Others (candidates for extraction ;)) // Others (candidates for extraction ;))
Q_INVOKABLE void searchEmoji(const QString &queryString); Q_INVOKABLE void searchEmoji(const QString &queryString);
@ -251,6 +252,7 @@ signals:
void errorReceived(int code, const QString &message, const QString &extra); void errorReceived(int code, const QString &message, const QString &extra);
void contactsImported(const QVariantList &importerCount, const QVariantList &userIds); void contactsImported(const QVariantList &importerCount, const QVariantList &userIds);
void messageNotFound(qlonglong chatId, qlonglong messageId); void messageNotFound(qlonglong chatId, qlonglong messageId);
void chatIsMarkedAsUnreadUpdated(qlonglong chatId, bool chatIsMarkedAsUnread);
public slots: public slots:
void handleVersionDetected(const QString &version); void handleVersionDetected(const QString &version);

View file

@ -264,6 +264,14 @@
<source>No message in this chat.</source> <source>No message in this chat.</source>
<translation>Keine Nachricht in diesem Chat</translation> <translation>Keine Nachricht in diesem Chat</translation>
</message> </message>
<message>
<source>Mark chat as unread</source>
<translation>Chat als ungelesen markieren</translation>
</message>
<message>
<source>Mark chat as read</source>
<translation>Chat als gelesen markieren</translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>

View file

@ -264,6 +264,14 @@
<source>No message in this chat.</source> <source>No message in this chat.</source>
<translation>No message in this chat.</translation> <translation>No message in this chat.</translation>
</message> </message>
<message>
<source>Mark chat as unread</source>
<translation>Mark chat as unread</translation>
</message>
<message>
<source>Mark chat as read</source>
<translation>Mark chat as read</translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>

View file

@ -261,6 +261,14 @@
<source>No message in this chat.</source> <source>No message in this chat.</source>
<translation>No hay mensaje en esta charla.</translation> <translation>No hay mensaje en esta charla.</translation>
</message> </message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Mark chat as read</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>

View file

@ -264,6 +264,14 @@
<source>No message in this chat.</source> <source>No message in this chat.</source>
<translation>Tässä keskustelussa ei ole viestejä.</translation> <translation>Tässä keskustelussa ei ole viestejä.</translation>
</message> </message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Mark chat as read</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>

View file

@ -261,6 +261,14 @@
<source>No message in this chat.</source> <source>No message in this chat.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Mark chat as read</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>

View file

@ -264,6 +264,14 @@
<source>No message in this chat.</source> <source>No message in this chat.</source>
<translation>Nessun messaggio.</translation> <translation>Nessun messaggio.</translation>
</message> </message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Mark chat as read</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>

View file

@ -267,6 +267,14 @@
<source>No message in this chat.</source> <source>No message in this chat.</source>
<translation>Brak wiadomości na tym czacie.</translation> <translation>Brak wiadomości na tym czacie.</translation>
</message> </message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Mark chat as read</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>

View file

@ -267,6 +267,14 @@
<source>No message in this chat.</source> <source>No message in this chat.</source>
<translation>В чате нет сообщений</translation> <translation>В чате нет сообщений</translation>
</message> </message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Mark chat as read</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>

View file

@ -264,6 +264,14 @@
<source>No message in this chat.</source> <source>No message in this chat.</source>
<translation>Inget meddelande i den här chatten.</translation> <translation>Inget meddelande i den här chatten.</translation>
</message> </message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Mark chat as read</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>

View file

@ -261,6 +261,14 @@
<source>No message in this chat.</source> <source>No message in this chat.</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Mark chat as read</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>

View file

@ -264,6 +264,14 @@
<source>No message in this chat.</source> <source>No message in this chat.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Mark chat as unread</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Mark chat as read</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ChatPage</name> <name>ChatPage</name>