From c1afa189f3e70da85c5df6b601441ba86cc0a020 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 22 Aug 2019 18:24:02 +0300 Subject: [PATCH] Support multiple chat lists. GitOrigin-RevId: 8adecce0ddf36bb2408eecc19b7f1fc8856151fc --- example/cpp/td_example.cpp | 2 +- example/csharp/TdExample.cs | 2 +- .../org/drinkless/tdlib/example/Example.java | 63 +- td/generate/scheme/td_api.tl | 38 +- td/generate/scheme/td_api.tlo | Bin 158640 -> 158772 bytes td/telegram/ConfigManager.cpp | 1 + td/telegram/DialogDb.cpp | 13 +- td/telegram/DialogDb.h | 6 +- td/telegram/MessagesManager.cpp | 1032 ++++++++++------- td/telegram/MessagesManager.h | 127 +- td/telegram/Td.cpp | 16 +- td/telegram/TdDb.cpp | 6 +- td/telegram/UpdatesManager.cpp | 6 +- td/telegram/Version.h | 1 + td/telegram/cli.cpp | 28 +- tdnet/td/net/TcpListener.cpp | 2 - 16 files changed, 802 insertions(+), 541 deletions(-) diff --git a/example/cpp/td_example.cpp b/example/cpp/td_example.cpp index 8bb7aafa..b65c2761 100644 --- a/example/cpp/td_example.cpp +++ b/example/cpp/td_example.cpp @@ -114,7 +114,7 @@ class TdExample { send_query(std::move(send_message), {}); } else if (action == "c") { std::cerr << "Loading chat list..." << std::endl; - send_query(td_api::make_object(std::numeric_limits::max(), 0, 20), + send_query(td_api::make_object(nullptr, std::numeric_limits::max(), 0, 20), [this](Object object) { if (object->get_id() == td_api::error::ID) { return; diff --git a/example/csharp/TdExample.cs b/example/csharp/TdExample.cs index 72820897..c066684b 100644 --- a/example/csharp/TdExample.cs +++ b/example/csharp/TdExample.cs @@ -231,7 +231,7 @@ namespace TdExample _gotAuthorization.Reset(); _gotAuthorization.WaitOne(); - _client.Send(new TdApi.GetChats(Int64.MaxValue, 0, 100), _defaultHandler); // preload chat list + _client.Send(new TdApi.GetChats(null, Int64.MaxValue, 0, 100), _defaultHandler); // preload main chat list while (_haveAuthorization) { GetCommand(); diff --git a/example/java/org/drinkless/tdlib/example/Example.java b/example/java/org/drinkless/tdlib/example/Example.java index 70602f3c..6476c682 100644 --- a/example/java/org/drinkless/tdlib/example/Example.java +++ b/example/java/org/drinkless/tdlib/example/Example.java @@ -42,8 +42,8 @@ public final class Example { private static final ConcurrentMap secretChats = new ConcurrentHashMap(); private static final ConcurrentMap chats = new ConcurrentHashMap(); - private static final NavigableSet chatList = new TreeSet(); - private static boolean haveFullChatList = false; + private static final NavigableSet mainChatList = new TreeSet(); + private static boolean haveFullMainChatList = false; private static final ConcurrentMap usersFullInfo = new ConcurrentHashMap(); private static final ConcurrentMap basicGroupsFullInfo = new ConcurrentHashMap(); @@ -72,17 +72,23 @@ public final class Example { } private static void setChatOrder(TdApi.Chat chat, long order) { - synchronized (chatList) { - if (chat.order != 0) { - boolean isRemoved = chatList.remove(new OrderedChat(chat.order, chat.id)); - assert isRemoved; - } + synchronized (mainChatList) { + synchronized (chat) { + if (chat.chatList == null || chat.chatList.getConstructor() != TdApi.ChatListMain.CONSTRUCTOR) { + return; + } - chat.order = order; + if (chat.order != 0) { + boolean isRemoved = mainChatList.remove(new OrderedChat(chat.order, chat.id)); + assert isRemoved; + } - if (chat.order != 0) { - boolean isAdded = chatList.add(new OrderedChat(chat.order, chat.id)); - assert isAdded; + chat.order = order; + + if (chat.order != 0) { + boolean isAdded = mainChatList.add(new OrderedChat(chat.order, chat.id)); + assert isAdded; + } } } } @@ -201,7 +207,7 @@ public final class Example { if (commands.length > 1) { limit = toInt(commands[1]); } - getChatList(limit); + getMainChatList(limit); break; } case "gc": @@ -232,18 +238,18 @@ public final class Example { } } - private static void getChatList(final int limit) { - synchronized (chatList) { - if (!haveFullChatList && limit > chatList.size()) { + private static void getMainChatList(final int limit) { + synchronized (mainChatList) { + if (!haveFullMainChatList && limit > mainChatList.size()) { // have enough chats in the chat list or chat list is too small long offsetOrder = Long.MAX_VALUE; long offsetChatId = 0; - if (!chatList.isEmpty()) { - OrderedChat last = chatList.last(); + if (!mainChatList.isEmpty()) { + OrderedChat last = mainChatList.last(); offsetOrder = last.order; offsetChatId = last.chatId; } - client.send(new TdApi.GetChats(offsetOrder, offsetChatId, limit - chatList.size()), new Client.ResultHandler() { + client.send(new TdApi.GetChats(new TdApi.ChatListMain(), offsetOrder, offsetChatId, limit - mainChatList.size()), new Client.ResultHandler() { @Override public void onResult(TdApi.Object object) { switch (object.getConstructor()) { @@ -253,12 +259,12 @@ public final class Example { case TdApi.Chats.CONSTRUCTOR: long[] chatIds = ((TdApi.Chats) object).chatIds; if (chatIds.length == 0) { - synchronized (chatList) { - haveFullChatList = true; + synchronized (mainChatList) { + haveFullMainChatList = true; } } // chats had already been received through updates, let's retry request - getChatList(limit); + getMainChatList(limit); break; default: System.err.println("Receive wrong response from TDLib:" + newLine + object); @@ -269,9 +275,9 @@ public final class Example { } // have enough chats in the chat list to answer request - java.util.Iterator iter = chatList.iterator(); + java.util.Iterator iter = mainChatList.iterator(); System.out.println(); - System.out.println("First " + limit + " chat(s) out of " + chatList.size() + " known chat(s):"); + System.out.println("First " + limit + " chat(s) out of " + mainChatList.size() + " known chat(s):"); for (int i = 0; i < limit; i++) { long chatId = iter.next().chatId; TdApi.Chat chat = chats.get(chatId); @@ -418,6 +424,17 @@ public final class Example { } break; } + case TdApi.UpdateChatChatList.CONSTRUCTOR: { + TdApi.UpdateChatChatList updateChat = (TdApi.UpdateChatChatList) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (mainChatList) { // to not change Chat.chatList while mainChatList is locked + synchronized (chat) { + assert chat.order == 0; // guaranteed by TDLib + chat.chatList = updateChat.chatList; + } + } + break; + } case TdApi.UpdateChatLastMessage.CONSTRUCTOR: { TdApi.UpdateChatLastMessage updateChat = (TdApi.UpdateChatLastMessage) object; TdApi.Chat chat = chats.get(updateChat.chatId); diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 3a036c91..2c04141a 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -604,19 +604,19 @@ chatTypeSupergroup supergroup_id:int32 is_channel:Bool = ChatType; chatTypeSecret secret_chat_id:int32 user_id:int32 = ChatType; -//@class ChatListType @description Describes a list of chats a chat belongs to +//@class ChatList @description Describes a list of chats //@description A main list of chats. Can contain ordinary chats and other lists of chats as folders -chatListTypeMain = ChatListType; +chatListMain = ChatList; -//@description A list of chats located at the top of main chat list. Unmuted chats are automatically moved from the Archive to the Main chat list -chatListTypeArchive = ChatListType; +//@description A list of chats usually located at the top of the main chat list. Unmuted chats are automatically moved from the Archive to the Main chat list when a new message arrives +chatListArchive = ChatList; //@description A chat. (Can be a private chat, basic group, supergroup, or secret chat) //@id Chat unique identifier //@type Type of the chat -//@list_type The type of a chat list the chat belongs to; may be null +//@chat_list A chat list to which the chat belongs; may be null //@title Chat title //@photo Chat photo; may be null //@permissions Actions that non-administrator chat members are allowed to take in the chat @@ -638,7 +638,7 @@ chatListTypeArchive = ChatListType; //@reply_markup_message_id Identifier of the message from which reply markup needs to be used; 0 if there is no default custom reply markup in the chat //@draft_message A draft of a message in the chat; may be null //@client_data Contains client-specific data associated with the chat. (For example, the chat position or local chat notification settings can be stored here.) Persistent if a message database is used -chat id:int53 type:ChatType list_type:ChatListType title:string photo:chatPhoto permissions:chatPermissions last_message:message order:int64 is_pinned:Bool is_marked_as_unread:Bool is_sponsored:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings pinned_message_id:int53 reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat; +chat id:int53 type:ChatType chat_list:ChatList title:string photo:chatPhoto permissions:chatPermissions last_message:message order:int64 is_pinned:Bool is_marked_as_unread:Bool is_sponsored:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings pinned_message_id:int53 reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat; //@description Represents a list of chats @chat_ids List of chat identifiers chats chat_ids:vector = Chats; @@ -2605,8 +2605,8 @@ updateMessageMentionRead chat_id:int53 message_id:int53 unread_mention_count:int //@description A new chat has been loaded/created. This update is guaranteed to come before the chat identifier is returned to the client. The chat field changes will be reported through separate updates @chat The chat updateNewChat chat:chat = Update; -//@description The list to which the chat belongs was changed @chat_id Chat identifier @list_type The new chat list type; may be null -updateChatListType chat_id:int53 list_type:ChatListType = Update; +//@description The list to which the chat belongs was changed. This update is guaranteed to be sent only when chat.order == 0 and the current or the new chat list is null @chat_id Chat identifier @chat_list The new chat's list; may be null +updateChatChatList chat_id:int53 chat_list:ChatList = Update; //@description The title of a chat was changed @chat_id Chat identifier @title The new chat title updateChatTitle chat_id:int53 title:string = Update; @@ -2740,13 +2740,15 @@ updateCall call:call = Update; //@description Some privacy setting rules have been changed @setting The privacy setting @rules New privacy rules updateUserPrivacySettingRules setting:UserPrivacySetting rules:userPrivacySettingRules = Update; -//@description Number of unread messages has changed. This update is sent only if a message database is used @unread_count Total number of unread messages @unread_unmuted_count Total number of unread messages in unmuted chats -updateUnreadMessageCount unread_count:int32 unread_unmuted_count:int32 = Update; +//@description Number of unread messages in a chat list has changed. This update is sent only if a message database is used @chat_list The chat list with changed number of unread messages +//@unread_count Total number of unread messages @unread_unmuted_count Total number of unread messages in unmuted chats +updateUnreadMessageCount chat_list:ChatList unread_count:int32 unread_unmuted_count:int32 = Update; //@description Number of unread chats, i.e. with unread messages or marked as unread, has changed. This update is sent only if a message database is used +//@chat_list The chat list with changed number of unread messages //@unread_count Total number of unread chats @unread_unmuted_count Total number of unread unmuted chats //@marked_as_unread_count Total number of chats marked as unread @marked_as_unread_unmuted_count Total number of unmuted chats marked as unread -updateUnreadChatCount unread_count:int32 unread_unmuted_count:int32 marked_as_unread_count:int32 marked_as_unread_unmuted_count:int32 = Update; +updateUnreadChatCount chat_list:ChatList unread_count:int32 unread_unmuted_count:int32 marked_as_unread_count:int32 marked_as_unread_unmuted_count:int32 = Update; //@description An option changed its value @name The option name @value The new option value updateOption name:string value:OptionValue = Update; @@ -2987,10 +2989,12 @@ getFile file_id:int32 = File; //@remote_file_id Remote identifier of the file to get @file_type File type, if known getRemoteFile remote_file_id:string file_type:FileType = File; -//@description Returns an ordered list of chats. Chats are sorted by the pair (order, chat_id) in decreasing order. (For example, to get a list of chats from the beginning, the offset_order should be equal to a biggest signed 64-bit number 9223372036854775807 == 2^63 - 1). -//-For optimal performance the number of returned chats is chosen by the library @offset_order Chat order to return chats from @offset_chat_id Chat identifier to return chats from +//@description Returns an ordered list of chats in a chat list. Chats are sorted by the pair (order, chat_id) in decreasing order. (For example, to get a list of chats from the beginning, the offset_order should be equal to a biggest signed 64-bit number 9223372036854775807 == 2^63 - 1). +//-For optimal performance the number of returned chats is chosen by the library +//@chat_list The chat list in which to return chats +//@offset_order Chat order to return chats from @offset_chat_id Chat identifier to return chats from //@limit The maximum number of chats to be returned. It is possible that fewer chats than the limit are returned even if the end of the list is not reached -getChats offset_order:int64 offset_chat_id:int53 limit:int32 = Chats; +getChats chat_list:ChatList offset_order:int64 offset_chat_id:int53 limit:int32 = Chats; //@description Searches a public chat by its username. Currently only private chats, supergroups and channels can be public. Returns the chat if found; otherwise an error is returned @username Username to be resolved searchPublicChat username:string = Chat; @@ -3341,7 +3345,7 @@ setChatDraftMessage chat_id:int53 draft_message:draftMessage = Ok; //@description Changes the notification settings of a chat @chat_id Chat identifier @notification_settings New notification settings for the chat setChatNotificationSettings chat_id:int53 notification_settings:chatNotificationSettings = Ok; -//@description Changes the pinned state of a chat. You can pin up to GetOption("pinned_chat_count_max") non-secret chats and the same number of secret chats @chat_id Chat identifier @is_pinned New value of is_pinned +//@description Changes the pinned state of a chat. You can pin up to GetOption("pinned_chat_count_max")/GetOption("pinned_archived_chat_count_max") non-secret chats and the same number of secret chats in the main/archive chat list @chat_id Chat identifier @is_pinned New value of is_pinned toggleChatIsPinned chat_id:int53 is_pinned:Bool = Ok; //@description Changes the marked as unread state of a chat @chat_id Chat identifier @is_marked_as_unread New value of is_marked_as_unread @@ -3407,8 +3411,8 @@ setScopeNotificationSettings scope:NotificationSettingsScope notification_settin resetAllNotificationSettings = Ok; -//@description Changes the order of pinned chats @chat_ids The new list of pinned chats -setPinnedChats chat_ids:vector = Ok; +//@description Changes the order of pinned chats @chat_list Chat list in which to change the order of pinned chats @chat_ids The new list of pinned chats +setPinnedChats chat_list:ChatList chat_ids:vector = Ok; //@description Downloads a file from the cloud. Download progress and completion of the download will be notified through updateFile updates diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 7030f619c96bcef15e80608ba35a5e5ea70b116f..c42a21ab756373552e37696174f938299d0f0ab8 100644 GIT binary patch delta 2619 zcma)8ZAhC}6wY}YU2H9joyBx!B?Y%xj2M{%3koU9YFnjgu`E!fktQ?F%x2XN8Pdjn zSgm#qUiDf(W3}^R{gI+24qVYq_Q4!vrJHdT=BNxeItXl*{%B$MzV|(^R=QC^IC<_l z&pG$G=iK`S=i??KalvJ6ob&FTCT*U(tcGO1lsvABu|UGfw;P`7I8<0sU8B)xB$A*@ z8h6GTOV&)EBiDb$o$o9Sb(Vs0mzHmbPM(1rVLd(rDY8YPHj99Y z&w^rEw@AeTTRID7Vd;Hf7na8dsd1c+7pf&QkKUv?K1diZsy_z-;X82-i{ZZJ^XO509<#4iw1}9@TXF%i1V`HiFbQk&0vN4aW)PaG zLh_mwNEUJ|etvb^Y59cn*fH>bxea8gEG{WMjjQ~;<9rS=^<2aP{1?F{N?BfyrPKWh zkW6h2pcAVl4T?n6SELG#7~7tiW?zCr;oEp=9eX+{El4IoyWr_)TvN|X>Izb5un83m zG@^pxMko_9B11F;$v>rt_Lh>`FSGfkzqFFUi;@s`Mkm1)FxAclTH;VqH=A9N+* z$}DQ{#3cSsq?qmm+wP6icKp&0v75!Ty;I?FY4|$7L4YQ>r6g**28n{-eQjL+k*m78tlX7% z@^lry+OfJ#%ufFO*WGddcNNgG9ymtA8M?%cY5x4J`5v|QfFT_TV_uZ-d!dG<7YE<> zH)|wcFP`p5FA5Rq#U3i`L#(b(6{in-&G-#iXktH3b8$Z|`1+whWSdj5$NhNq6adQ8C>B^oZDzfpzZ!&>}k|(dYo4s_y!_Rf)d#C1jB4+8Fx5b)3Nf^N^WH@Tq}L z4+EQ+K`3aCm-J+%SZR)yK`dr!5W*s{e+V}+H3Uln3*1nSe~qk!xshmE3_W}U4k})s zVoFo4lF~Sg?_BM$l8Sf-48yD*xXDs~XqS^?18@WY{m-9*RmXYYwbYdxSfpvhqq_4EdM##GWCJ?LE zT-GGhzpyMakN!S7^C7n_D!*t8;?z3N8b$VvVs?41Ro_tUB24ZOE&@!~kq3{W5S$vmKb$_9U<G(V6 z{LVSQd)~d-*w(H6s$1LD=my@bPH4(qO&;NjwqRYGVTMCbJK`s@DP(W$^wfUocBZAJ zal(9N-nt{xVAR~tDVtySz7{p=<=kc|Pzs>7;Hap|Ym<^b_OL7o*S^*JJtbVyY)=10AKdYX$xYMNTxrFa^<^FxkZGSfQBGfkLJ|EGVy9L;J7Oq~tAoSgf1 zN3*N-bLaob&3^Cc&soi`i*t?Pxkk_(m&YNi=iv;q9-oIo*%BV}W(HMugJLD_K`q1X zbc0Dpm7u+H^--~5?#&fU`F zai*L*jU!?om`u9=I96oAqmnZmUtS|9Yt)-&ef1_N8to>Y>z4}1jm3cJh+*E7x%xD#WwT(PG?7& zykDL%thqdf{YS@8T5S+9a}Y6?iUm|GqGE|4)Ujsl1Sjkwpq%@jimlai7GfVqQr~#W z9dPOmC?fH3EUujZ10yY-z;+uZkbZCi)|l5iiI{s5mu__ubGIgy+~5?Fm>W4h_n=OD zAH;ql<5OTrx|AMFLG$T@f${rCP9IVPZSQb4wa^c~{IhfI(j`t~>3yalQfj?mJ*^@8 z)6mBHY@5L^E-(Yi7q>f;qQF-gu83G?aW(w2IN8lvob1^wu7Pn5nFDi(-I{|+R!u7+ zW)Z>8FrS!G?MW4Pl2~&fgFF(Uh=t_NV@caQOfzhE9;x&TsG)5EG4}$Vb4?rVDZ!9F}ZF6Rhhy+t;@JI395h0$!5Nw=%q^ config) { shared_config.set_option_integer("basic_group_size_max", config->chat_size_max_); shared_config.set_option_integer("supergroup_size_max", config->megagroup_size_max_); shared_config.set_option_integer("pinned_chat_count_max", config->pinned_dialogs_count_max_); + shared_config.set_option_integer("pinned_archived_chat_count_max", config->pinned_infolder_count_max_); if (is_from_main_dc || !shared_config.have_option("expect_blocking")) { shared_config.set_option_boolean("expect_blocking", (config->flags_ & telegram_api::config::BLOCKED_MODE_MASK) != 0); diff --git a/td/telegram/DialogDb.cpp b/td/telegram/DialogDb.cpp index 1277caac..0e75e173 100644 --- a/td/telegram/DialogDb.cpp +++ b/td/telegram/DialogDb.cpp @@ -190,7 +190,8 @@ class DialogDbImpl : public DialogDbSyncInterface { get_last_notification_date(get_notification_group_stmt_, 1)); } - Result> get_dialogs(int64 order, DialogId dialog_id, int32 limit) override { + Result> get_dialogs(FolderId folder_id, int64 order, DialogId dialog_id, int32 limit) override { + // TODO use folder_id SCOPE_EXIT { get_dialogs_stmt_.reset(); }; @@ -305,8 +306,9 @@ class DialogDbAsync : public DialogDbAsyncInterface { void get_dialog(DialogId dialog_id, Promise promise) override { send_closure_later(impl_, &Impl::get_dialog, dialog_id, std::move(promise)); } - void get_dialogs(int64 order, DialogId dialog_id, int32 limit, Promise> promise) override { - send_closure_later(impl_, &Impl::get_dialogs, order, dialog_id, limit, std::move(promise)); + void get_dialogs(FolderId folder_id, int64 order, DialogId dialog_id, int32 limit, + Promise> promise) override { + send_closure_later(impl_, &Impl::get_dialogs, folder_id, order, dialog_id, limit, std::move(promise)); } void close(Promise<> promise) override { send_closure_later(impl_, &Impl::close, std::move(promise)); @@ -342,9 +344,10 @@ class DialogDbAsync : public DialogDbAsyncInterface { add_read_query(); promise.set_result(sync_db_->get_dialog(dialog_id)); } - void get_dialogs(int64 order, DialogId dialog_id, int32 limit, Promise> promise) { + void get_dialogs(FolderId folder_id, int64 order, DialogId dialog_id, int32 limit, + Promise> promise) { add_read_query(); - promise.set_result(sync_db_->get_dialogs(order, dialog_id, limit)); + promise.set_result(sync_db_->get_dialogs(folder_id, order, dialog_id, limit)); } void close(Promise<> promise) { do_flush(); diff --git a/td/telegram/DialogDb.h b/td/telegram/DialogDb.h index ede45031..f53a6079 100644 --- a/td/telegram/DialogDb.h +++ b/td/telegram/DialogDb.h @@ -7,6 +7,7 @@ #pragma once #include "td/telegram/DialogId.h" +#include "td/telegram/FolderId.h" #include "td/telegram/NotificationGroupId.h" #include "td/telegram/NotificationGroupKey.h" @@ -36,7 +37,7 @@ class DialogDbSyncInterface { virtual Result get_dialog(DialogId dialog_id) = 0; - virtual Result> get_dialogs(int64 order, DialogId dialog_id, int32 limit) = 0; + virtual Result> get_dialogs(FolderId folder_id, int64 order, DialogId dialog_id, int32 limit) = 0; virtual Result> get_notification_groups_by_last_notification_date( NotificationGroupKey notification_group_key, int32 limit) = 0; @@ -69,7 +70,8 @@ class DialogDbAsyncInterface { virtual void get_dialog(DialogId dialog_id, Promise promise) = 0; - virtual void get_dialogs(int64 order, DialogId dialog_id, int32 limit, Promise> promise) = 0; + virtual void get_dialogs(FolderId folder_id, int64 order, DialogId dialog_id, int32 limit, + Promise> promise) = 0; virtual void get_notification_groups_by_last_notification_date(NotificationGroupKey notification_group_key, int32 limit, diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index cceecd85..a8f08444 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -138,7 +138,7 @@ class GetDialogQuery : public Td::ResultHandler { td->contacts_manager_->on_get_users(std::move(result->users_), "GetDialogQuery"); td->contacts_manager_->on_get_chats(std::move(result->chats_), "GetDialogQuery"); td->messages_manager_->on_get_dialogs( - std::move(result->dialogs_), -1, std::move(result->messages_), + FolderId(), std::move(result->dialogs_), -1, std::move(result->messages_), PromiseCreator::lambda([td = td, dialog_id = dialog_id_](Result<> result) { if (result.is_ok()) { td->messages_manager_->on_get_dialog_query_finished(dialog_id, Status::OK()); @@ -159,14 +159,17 @@ class GetDialogQuery : public Td::ResultHandler { }; class GetPinnedDialogsActor : public NetActorOnce { + FolderId folder_id_; Promise promise_; public: explicit GetPinnedDialogsActor(Promise &&promise) : promise_(std::move(promise)) { } - NetQueryRef send(uint64 sequence_id) { - auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_getPinnedDialogs(0))); + NetQueryRef send(FolderId folder_id, uint64 sequence_id) { + folder_id_ = folder_id; + auto query = + G()->net_query_creator().create(create_storer(telegram_api::messages_getPinnedDialogs(folder_id.get()))); auto result = query.get_weak(); send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, std::move(query), actor_shared(this), sequence_id); @@ -185,7 +188,7 @@ class GetPinnedDialogsActor : public NetActorOnce { td->contacts_manager_->on_get_users(std::move(result->users_), "GetPinnedDialogsActor"); td->contacts_manager_->on_get_chats(std::move(result->chats_), "GetPinnedDialogsActor"); std::reverse(result->dialogs_.begin(), result->dialogs_.end()); - td->messages_manager_->on_get_dialogs(std::move(result->dialogs_), -2, std::move(result->messages_), + td->messages_manager_->on_get_dialogs(folder_id_, std::move(result->dialogs_), -2, std::move(result->messages_), std::move(promise_)); } @@ -397,14 +400,16 @@ class ExportChannelMessageLinkQuery : public Td::ResultHandler { }; class GetDialogListActor : public NetActorOnce { + FolderId folder_id_; Promise promise_; public: explicit GetDialogListActor(Promise &&promise) : promise_(std::move(promise)) { } - void send(int32 offset_date, ServerMessageId offset_message_id, DialogId offset_dialog_id, int32 limit, - uint64 sequence_id) { + void send(FolderId folder_id, int32 offset_date, ServerMessageId offset_message_id, DialogId offset_dialog_id, + int32 limit, uint64 sequence_id) { + folder_id_ = folder_id; auto input_peer = td->messages_manager_->get_input_peer(offset_dialog_id, AccessRights::Read); if (input_peer == nullptr) { input_peer = make_tl_object(); @@ -412,8 +417,9 @@ class GetDialogListActor : public NetActorOnce { int32 flags = telegram_api::messages_getDialogs::EXCLUDE_PINNED_MASK | telegram_api::messages_getDialogs::FOLDER_ID_MASK; - auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_getDialogs( - flags, false /*ignored*/, 0, offset_date, offset_message_id.get(), std::move(input_peer), limit, 0))); + auto query = G()->net_query_creator().create( + create_storer(telegram_api::messages_getDialogs(flags, false /*ignored*/, folder_id.get(), offset_date, + offset_message_id.get(), std::move(input_peer), limit, 0))); send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, std::move(query), actor_shared(this), sequence_id); } @@ -431,7 +437,7 @@ class GetDialogListActor : public NetActorOnce { auto dialogs = move_tl_object_as(ptr); td->contacts_manager_->on_get_users(std::move(dialogs->users_), "GetDialogListActor"); td->contacts_manager_->on_get_chats(std::move(dialogs->chats_), "GetDialogListActor"); - td->messages_manager_->on_get_dialogs(std::move(dialogs->dialogs_), + td->messages_manager_->on_get_dialogs(folder_id_, std::move(dialogs->dialogs_), narrow_cast(dialogs->dialogs_.size()), std::move(dialogs->messages_), std::move(promise_)); break; @@ -440,7 +446,7 @@ class GetDialogListActor : public NetActorOnce { auto dialogs = move_tl_object_as(ptr); td->contacts_manager_->on_get_users(std::move(dialogs->users_), "GetDialogListActor"); td->contacts_manager_->on_get_chats(std::move(dialogs->chats_), "GetDialogListActor"); - td->messages_manager_->on_get_dialogs(std::move(dialogs->dialogs_), max(dialogs->count_, 0), + td->messages_manager_->on_get_dialogs(folder_id_, std::move(dialogs->dialogs_), max(dialogs->count_, 0), std::move(dialogs->messages_), std::move(promise_)); break; } @@ -942,22 +948,25 @@ class ToggleDialogPinQuery : public Td::ResultHandler { if (!td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ToggleDialogPinQuery")) { LOG(ERROR) << "Receive error for ToggleDialogPinQuery: " << status; } - td->messages_manager_->on_update_dialog_is_pinned(dialog_id_, !is_pinned_); + td->messages_manager_->set_dialog_is_pinned(dialog_id_, !is_pinned_); promise_.set_error(std::move(status)); } }; class ReorderPinnedDialogsQuery : public Td::ResultHandler { + FolderId folder_id_; Promise promise_; public: explicit ReorderPinnedDialogsQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(const vector &dialog_ids) { + void send(FolderId folder_id, const vector &dialog_ids) { + folder_id_ = folder_id; int32 flags = telegram_api::messages_reorderPinnedDialogs::FORCE_MASK; send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_reorderPinnedDialogs( - flags, true /*ignored*/, 0, td->messages_manager_->get_input_dialog_peers(dialog_ids, AccessRights::Read))))); + flags, true /*ignored*/, folder_id.get(), + td->messages_manager_->get_input_dialog_peers(dialog_ids, AccessRights::Read))))); } void on_result(uint64 id, BufferSlice packet) override { @@ -979,7 +988,7 @@ class ReorderPinnedDialogsQuery : public Td::ResultHandler { if (!G()->close_flag()) { LOG(ERROR) << "Receive error for ReorderPinnedDialogsQuery: " << status; } - td->messages_manager_->on_update_pinned_dialogs(); + td->messages_manager_->on_update_pinned_dialogs(folder_id_); promise_.set_error(std::move(status)); } }; @@ -4303,6 +4312,9 @@ MessagesManager::MessagesManager(Td *td, ActorShared<> parent) : td_(td), parent update_dialog_online_member_count_timeout_.set_callback(on_update_dialog_online_member_count_timeout_callback); update_dialog_online_member_count_timeout_.set_callback_data(static_cast(this)); + preload_dialog_list_timeout_.set_callback(on_preload_dialog_list_timeout_callback); + preload_dialog_list_timeout_.set_callback_data(static_cast(this)); + sequence_dispatcher_ = create_actor("multi sequence dispatcher"); } @@ -4414,6 +4426,16 @@ void MessagesManager::on_update_dialog_online_member_count_timeout_callback(void &MessagesManager::on_update_dialog_online_member_count_timeout, DialogId(dialog_id_int)); } +void MessagesManager::on_preload_dialog_list_timeout_callback(void *messages_manager_ptr, int64 folder_id_int) { + if (G()->close_flag()) { + return; + } + + auto messages_manager = static_cast(messages_manager_ptr); + send_closure_later(messages_manager->actor_id(messages_manager), &MessagesManager::preload_dialog_list, + FolderId(narrow_cast(folder_id_int))); +} + BufferSlice MessagesManager::get_dialog_database_value(const Dialog *d) { // can't use log_event_store, because it tries to parse stored Dialog LogEventStorerCalcLength storer_calc_length; @@ -5375,14 +5397,17 @@ void MessagesManager::on_update_include_sponsored_dialog_to_unread_count() { return; } + auto folder_id = FolderId::main(); + auto &list = get_dialog_list(folder_id); const Dialog *d = get_dialog(sponsored_dialog_id_); CHECK(d != nullptr); auto unread_count = d->server_unread_count + d->local_unread_count; - if (unread_count != 0 && is_message_unread_count_inited_) { - send_update_unread_message_count(d->dialog_id, true, "on_update_include_sponsored_dialog_to_unread_count"); + if (unread_count != 0 && list.is_message_unread_count_inited_) { + send_update_unread_message_count(folder_id, d->dialog_id, true, + "on_update_include_sponsored_dialog_to_unread_count"); } - if ((unread_count != 0 || d->is_marked_as_unread) && is_dialog_unread_count_inited_) { - send_update_unread_chat_count(d->dialog_id, true, "on_update_include_sponsored_dialog_to_unread_count"); + if ((unread_count != 0 || d->is_marked_as_unread) && list.is_dialog_unread_count_inited_) { + send_update_unread_chat_count(folder_id, d->dialog_id, true, "on_update_include_sponsored_dialog_to_unread_count"); } } @@ -5983,8 +6008,9 @@ void MessagesManager::update_dialog_unmute_timeout(Dialog *d, bool old_use_defau dialog_unmute_timeout_.cancel_timeout(d->dialog_id.get()); } + auto &list = get_dialog_list(d->folder_id); if (old_mute_until != -1 && need_unread_counter(d->order) && - (is_message_unread_count_inited_ || is_dialog_unread_count_inited_)) { + (list.is_message_unread_count_inited_ || list.is_dialog_unread_count_inited_)) { auto unread_count = d->server_unread_count + d->local_unread_count; if (unread_count != 0 || d->is_marked_as_unread) { if (old_use_default || new_use_default) { @@ -5997,18 +6023,18 @@ void MessagesManager::update_dialog_unmute_timeout(Dialog *d, bool old_use_defau } } if ((old_mute_until != 0) != (new_mute_until != 0)) { - if (unread_count != 0 && is_message_unread_count_inited_) { + if (unread_count != 0 && list.is_message_unread_count_inited_) { int32 delta = old_mute_until != 0 ? -unread_count : unread_count; - unread_message_muted_count_ += delta; - send_update_unread_message_count(d->dialog_id, true, "update_dialog_unmute_timeout"); + list.unread_message_muted_count_ += delta; + send_update_unread_message_count(d->folder_id, d->dialog_id, true, "update_dialog_unmute_timeout"); } - if (is_dialog_unread_count_inited_) { + if (list.is_dialog_unread_count_inited_) { int32 delta = old_mute_until != 0 ? -1 : 1; - unread_dialog_muted_count_ += delta; + list.unread_dialog_muted_count_ += delta; if (unread_count == 0 && d->is_marked_as_unread) { - unread_dialog_muted_marked_count_ += delta; + list.unread_dialog_muted_marked_count_ += delta; } - send_update_unread_chat_count(d->dialog_id, true, "update_dialog_unmute_timeout"); + send_update_unread_chat_count(d->folder_id, d->dialog_id, true, "update_dialog_unmute_timeout"); } } } @@ -6029,44 +6055,50 @@ void MessagesManager::update_scope_unmute_timeout(NotificationSettingsScope scop dialog_unmute_timeout_.cancel_timeout(static_cast(scope) + 1); } - if (old_mute_until != -1 && (is_message_unread_count_inited_ || is_dialog_unread_count_inited_)) { + if (old_mute_until != -1 && !td_->auth_manager_->is_bot() && G()->parameters().use_message_db) { auto was_muted = old_mute_until != 0; auto is_muted = new_mute_until != 0; if (was_muted != is_muted) { - int32 delta = 0; - int32 total_count = 0; - int32 marked_count = 0; + std::unordered_map delta; + std::unordered_map total_count; + std::unordered_map marked_count; + std::unordered_set folder_ids; for (auto &dialog : dialogs_) { Dialog *d = dialog.second.get(); if (need_unread_counter(d->order) && d->notification_settings.use_default_mute_until && get_dialog_notification_setting_scope(d->dialog_id) == scope) { int32 unread_count = d->server_unread_count + d->local_unread_count; if (unread_count != 0) { - delta += unread_count; - total_count++; + delta[d->folder_id] += unread_count; + total_count[d->folder_id]++; + folder_ids.insert(d->folder_id); } else if (d->is_marked_as_unread) { - total_count++; - marked_count++; + total_count[d->folder_id]++; + marked_count[d->folder_id]++; + folder_ids.insert(d->folder_id); } } } - if (delta != 0 && is_message_unread_count_inited_) { - if (was_muted) { - unread_message_muted_count_ -= delta; - } else { - unread_message_muted_count_ += delta; + for (auto folder_id : folder_ids) { + auto &list = get_dialog_list(folder_id); + if (delta[folder_id] != 0 && list.is_message_unread_count_inited_) { + if (was_muted) { + list.unread_message_muted_count_ -= delta[folder_id]; + } else { + list.unread_message_muted_count_ += delta[folder_id]; + } + send_update_unread_message_count(folder_id, DialogId(), true, "update_scope_unmute_timeout"); } - send_update_unread_message_count(DialogId(), true, "update_scope_unmute_timeout"); - } - if (total_count != 0 && is_dialog_unread_count_inited_) { - if (was_muted) { - unread_dialog_muted_count_ -= total_count; - unread_dialog_muted_marked_count_ -= marked_count; - } else { - unread_dialog_muted_count_ += total_count; - unread_dialog_muted_marked_count_ += marked_count; + if (total_count[folder_id] != 0 && list.is_dialog_unread_count_inited_) { + if (was_muted) { + list.unread_dialog_muted_count_ -= total_count[folder_id]; + list.unread_dialog_muted_marked_count_ -= marked_count[folder_id]; + } else { + list.unread_dialog_muted_count_ += total_count[folder_id]; + list.unread_dialog_muted_marked_count_ += marked_count[folder_id]; + } + send_update_unread_chat_count(folder_id, DialogId(), true, "update_scope_unmute_timeout"); } - send_update_unread_chat_count(DialogId(), true, "update_scope_unmute_timeout"); } } } @@ -6765,7 +6797,8 @@ void MessagesManager::after_get_difference() { if (!pending_on_get_dialogs_.empty()) { LOG(INFO) << "Apply postponed results of getDialogs"; for (auto &res : pending_on_get_dialogs_) { - on_get_dialogs(std::move(res.dialogs), res.total_count, std::move(res.messages), std::move(res.promise)); + on_get_dialogs(res.folder_id, std::move(res.dialogs), res.total_count, std::move(res.messages), + std::move(res.promise)); } pending_on_get_dialogs_.clear(); } @@ -6777,13 +6810,13 @@ void MessagesManager::after_get_difference() { send_update_chat_read_inbox(get_dialog(dialog_id), false, "after_get_difference"); } } - if (have_postponed_unread_message_count_update_) { - LOG(INFO) << "Send postponed unread message count update"; - send_update_unread_message_count(DialogId(), false, "after_get_difference"); + while (!postponed_unread_message_count_updates_.empty()) { + auto folder_id = *postponed_unread_message_count_updates_.begin(); + send_update_unread_message_count(folder_id, DialogId(), false, "after_get_difference"); } - if (have_postponed_unread_chat_count_update_) { - LOG(INFO) << "Send postponed unread chat count update"; - send_update_unread_chat_count(DialogId(), false, "after_get_difference"); + while (!postponed_unread_chat_count_updates_.empty()) { + auto folder_id = *postponed_unread_chat_count_updates_.begin(); + send_update_unread_chat_count(folder_id, DialogId(), false, "after_get_difference"); } vector update_message_ids_to_delete; @@ -8593,14 +8626,19 @@ bool MessagesManager::need_unread_counter(int64 dialog_order) { return dialog_order != DEFAULT_ORDER; } -void MessagesManager::recalc_unread_count() { - if (td_->auth_manager_->is_bot() || !need_unread_count_recalc_) { +void MessagesManager::recalc_unread_count(FolderId folder_id) { + if (td_->auth_manager_->is_bot() || !G()->parameters().use_message_db) { + return; + } + + auto &list = get_dialog_list(folder_id); + if (list.need_unread_count_recalc_) { return; } LOG(INFO) << "Recalculate unread counts"; - need_unread_count_recalc_ = false; - is_message_unread_count_inited_ = true; - is_dialog_unread_count_inited_ = true; + list.need_unread_count_recalc_ = false; + list.is_message_unread_count_inited_ = true; + list.is_dialog_unread_count_inited_ = true; int32 total_count = 0; int32 muted_count = 0; @@ -8608,7 +8646,7 @@ void MessagesManager::recalc_unread_count() { int32 dialog_muted_count = 0; int32 dialog_marked_count = 0; int32 dialog_muted_marked_count = 0; - for (auto &dialog_date : ordered_server_dialogs_) { + for (auto &dialog_date : list.ordered_server_dialogs_) { auto dialog_id = dialog_date.get_dialog_id(); Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); @@ -8631,19 +8669,19 @@ void MessagesManager::recalc_unread_count() { } } - if (unread_message_total_count_ != total_count || unread_message_muted_count_ != muted_count) { - unread_message_total_count_ = total_count; - unread_message_muted_count_ = muted_count; - send_update_unread_message_count(DialogId(), true, "recalc_unread_count"); + if (list.unread_message_total_count_ != total_count || list.unread_message_muted_count_ != muted_count) { + list.unread_message_total_count_ = total_count; + list.unread_message_muted_count_ = muted_count; + send_update_unread_message_count(folder_id, DialogId(), true, "recalc_unread_count"); } - if (unread_dialog_total_count_ != dialog_total_count || unread_dialog_muted_count_ != dialog_muted_count || - unread_dialog_marked_count_ != dialog_marked_count || - unread_dialog_muted_marked_count_ != dialog_muted_marked_count) { - unread_dialog_total_count_ = dialog_total_count; - unread_dialog_muted_count_ = dialog_muted_count; - unread_dialog_marked_count_ = dialog_marked_count; - unread_dialog_muted_marked_count_ = dialog_muted_marked_count; - send_update_unread_chat_count(DialogId(), true, "recalc_unread_count"); + if (list.unread_dialog_total_count_ != dialog_total_count || list.unread_dialog_muted_count_ != dialog_muted_count || + list.unread_dialog_marked_count_ != dialog_marked_count || + list.unread_dialog_muted_marked_count_ != dialog_muted_marked_count) { + list.unread_dialog_total_count_ = dialog_total_count; + list.unread_dialog_muted_count_ = dialog_muted_count; + list.unread_dialog_marked_count_ = dialog_marked_count; + list.unread_dialog_muted_marked_count_ = dialog_muted_marked_count; + send_update_unread_chat_count(folder_id, DialogId(), true, "recalc_unread_count"); } } @@ -8668,28 +8706,29 @@ void MessagesManager::set_dialog_last_read_inbox_message_id(Dialog *d, MessageId d->local_unread_count = local_unread_count; int32 new_unread_count = d->server_unread_count + d->local_unread_count; int32 delta = new_unread_count - old_unread_count; - if (delta != 0 && need_unread_counter(d->order) && is_message_unread_count_inited_) { - unread_message_total_count_ += delta; + auto &list = get_dialog_list(d->folder_id); + if (delta != 0 && need_unread_counter(d->order) && list.is_message_unread_count_inited_) { + list.unread_message_total_count_ += delta; if (is_dialog_muted(d)) { - unread_message_muted_count_ += delta; + list.unread_message_muted_count_ += delta; } - send_update_unread_message_count(d->dialog_id, force_update, source); + send_update_unread_message_count(d->folder_id, d->dialog_id, force_update, source); } delta = static_cast(new_unread_count != 0) - static_cast(old_unread_count != 0); - if (delta != 0 && need_unread_counter(d->order) && is_dialog_unread_count_inited_) { + if (delta != 0 && need_unread_counter(d->order) && list.is_dialog_unread_count_inited_) { if (d->is_marked_as_unread) { - unread_dialog_marked_count_ -= delta; + list.unread_dialog_marked_count_ -= delta; } else { - unread_dialog_total_count_ += delta; + list.unread_dialog_total_count_ += delta; } if (is_dialog_muted(d)) { if (d->is_marked_as_unread) { - unread_dialog_muted_marked_count_ -= delta; + list.unread_dialog_muted_marked_count_ -= delta; } else { - unread_dialog_muted_count_ += delta; + list.unread_dialog_muted_count_ += delta; } } - send_update_unread_chat_count(d->dialog_id, force_update, source); + send_update_unread_chat_count(d->folder_id, d->dialog_id, force_update, source); } if (message_id != MessageId::min() && d->last_read_inbox_message_id.is_valid() && d->order != DEFAULT_ORDER && d->order != SPONSORED_DIALOG_ORDER) { @@ -9148,21 +9187,30 @@ void MessagesManager::init() { G()->shared_config().get_option_boolean("include_sponsored_chat_to_unread_count"); if (G()->parameters().use_message_db) { - auto last_database_server_dialog_date_string = G()->td_db()->get_binlog_pmc()->get("last_server_dialog_date"); - if (!last_database_server_dialog_date_string.empty()) { + auto last_database_server_dialog_dates = G()->td_db()->get_binlog_pmc()->prefix_get("last_server_dialog_date"); + for (auto &it : last_database_server_dialog_dates) { + auto r_folder_id = to_integer_safe(Slice(it.first).substr(Slice("last_server_dialog_date").size())); + if (r_folder_id.is_error()) { + LOG(ERROR) << "Can't parse folder ID from " << it.first; + continue; + } + string order_str; string dialog_id_str; - std::tie(order_str, dialog_id_str) = split(last_database_server_dialog_date_string); + std::tie(order_str, dialog_id_str) = split(it.second); auto r_order = to_integer_safe(order_str); auto r_dialog_id = to_integer_safe(dialog_id_str); if (r_order.is_error() || r_dialog_id.is_error()) { - LOG(ERROR) << "Can't parse " << last_database_server_dialog_date_string; + LOG(ERROR) << "Can't parse " << it.second; } else { - last_database_server_dialog_date_ = DialogDate(r_order.ok(), DialogId(r_dialog_id.ok())); + FolderId folder_id(r_folder_id.ok()); + auto &list = get_dialog_list(folder_id); + list.last_database_server_dialog_date_ = DialogDate(r_order.ok(), DialogId(r_dialog_id.ok())); + LOG(INFO) << "Loaded last_database_server_dialog_date_ " << list.last_database_server_dialog_date_ << " in " + << folder_id; } } - LOG(INFO) << "Load last_database_server_dialog_date_ = " << last_database_server_dialog_date_; auto sponsored_dialog_id_string = G()->td_db()->get_binlog_pmc()->get("sponsored_dialog_id"); if (sponsored_dialog_id_string.empty()) { @@ -9191,45 +9239,64 @@ void MessagesManager::init() { } } - auto unread_message_count_string = G()->td_db()->get_binlog_pmc()->get("unread_message_count"); - if (!unread_message_count_string.empty()) { + auto unread_message_counts = G()->td_db()->get_binlog_pmc()->prefix_get("unread_message_count"); + for (auto &it : unread_message_counts) { + auto r_folder_id = to_integer_safe(Slice(it.first).substr(Slice("unread_message_count").size())); + if (r_folder_id.is_error()) { + LOG(ERROR) << "Can't parse folder ID from " << it.first; + continue; + } string total_count; string muted_count; - std::tie(total_count, muted_count) = split(unread_message_count_string); + std::tie(total_count, muted_count) = split(it.second); auto r_total_count = to_integer_safe(total_count); auto r_muted_count = to_integer_safe(muted_count); if (r_total_count.is_error() || r_muted_count.is_error()) { - LOG(ERROR) << "Can't parse " << unread_message_count_string; + LOG(ERROR) << "Can't parse " << it.second; } else { - unread_message_total_count_ = r_total_count.ok(); - unread_message_muted_count_ = r_muted_count.ok(); - is_message_unread_count_inited_ = true; - send_update_unread_message_count(DialogId(), true, "load unread_message_count"); + FolderId folder_id(r_folder_id.ok()); + auto &list = get_dialog_list(folder_id); + list.unread_message_total_count_ = r_total_count.ok(); + list.unread_message_muted_count_ = r_muted_count.ok(); + list.is_message_unread_count_inited_ = true; + send_update_unread_message_count(folder_id, DialogId(), true, "load unread_message_count"); } } - auto unread_dialog_count_string = G()->td_db()->get_binlog_pmc()->get("unread_dialog_count"); - if (!unread_dialog_count_string.empty()) { - auto counts = - transform(full_split(unread_dialog_count_string), [](Slice str) { return to_integer_safe(str); }); + auto unread_dialog_counts = G()->td_db()->get_binlog_pmc()->prefix_get("unread_dialog_count"); + for (auto &it : unread_dialog_counts) { + auto r_folder_id = to_integer_safe(Slice(it.first).substr(Slice("unread_dialog_count").size())); + if (r_folder_id.is_error()) { + LOG(ERROR) << "Can't parse folder ID from " << it.first; + continue; + } + + auto counts = transform(full_split(it.second), [](Slice str) { return to_integer_safe(str); }); if (counts.size() != 4 || std::any_of(counts.begin(), counts.end(), [](auto &c) { return c.is_error(); })) { - LOG(ERROR) << "Can't parse " << unread_dialog_count_string; + LOG(ERROR) << "Can't parse " << it.second; } else { - unread_dialog_total_count_ = counts[0].ok(); - unread_dialog_muted_count_ = counts[1].ok(); - unread_dialog_marked_count_ = counts[2].ok(); - unread_dialog_muted_marked_count_ = counts[3].ok(); - is_dialog_unread_count_inited_ = true; - send_update_unread_chat_count(DialogId(), true, "load unread_dialog_count"); + FolderId folder_id(r_folder_id.ok()); + auto &list = get_dialog_list(folder_id); + list.unread_dialog_total_count_ = counts[0].ok(); + list.unread_dialog_muted_count_ = counts[1].ok(); + list.unread_dialog_marked_count_ = counts[2].ok(); + list.unread_dialog_muted_marked_count_ = counts[3].ok(); + list.is_dialog_unread_count_inited_ = true; + send_update_unread_chat_count(folder_id, DialogId(), true, "load unread_dialog_count"); } } ttl_db_loop_start(G()->server_time()); - } else { + + // erase old keys G()->td_db()->get_binlog_pmc()->erase("last_server_dialog_date"); G()->td_db()->get_binlog_pmc()->erase("unread_message_count"); G()->td_db()->get_binlog_pmc()->erase("unread_dialog_count"); + } else { + G()->td_db()->get_binlog_pmc()->erase_by_prefix("last_server_dialog_date"); + G()->td_db()->get_binlog_pmc()->erase_by_prefix("unread_message_count"); + G()->td_db()->get_binlog_pmc()->erase_by_prefix("unread_dialog_count"); G()->td_db()->get_binlog_pmc()->erase("promoted_dialog_id"); G()->td_db()->get_binlog_pmc()->erase("sponsored_dialog_id"); } @@ -10482,6 +10549,10 @@ void MessagesManager::set_dialog_is_empty(Dialog *d, const char *source) { void MessagesManager::set_dialog_is_pinned(DialogId dialog_id, bool is_pinned) { Dialog *d = get_dialog(dialog_id); + CHECK(d != nullptr); + if (!is_pinned && d->pinned_order == DEFAULT_ORDER) { + return; + } set_dialog_is_pinned(d, is_pinned); update_dialog_pos(d, false, "set_dialog_is_pinned"); } @@ -10499,7 +10570,7 @@ void MessagesManager::set_dialog_is_pinned(Dialog *d, bool is_pinned) { send_closure(G()->td(), &Td::send_update, make_tl_object(d->dialog_id.get(), is_pinned, get_dialog_public_order(d))); } - // there is no need to call update_dialog_pos otherwise, it will be called by the caller + // there is no need to call update_dialog_pos, it will be called by the caller } void MessagesManager::set_dialog_reply_markup(Dialog *d, MessageId message_id) { @@ -10718,12 +10789,13 @@ void MessagesManager::on_update_message_web_page(FullMessageId full_message_id, "on_update_message_web_page"); } -void MessagesManager::on_get_dialogs(vector> &&dialog_folders, int32 total_count, - vector> &&messages, Promise &&promise) { +void MessagesManager::on_get_dialogs(FolderId folder_id, vector> &&dialog_folders, + int32 total_count, vector> &&messages, + Promise &&promise) { if (td_->updates_manager_->running_get_difference()) { LOG(INFO) << "Postpone result of getDialogs"; - pending_on_get_dialogs_.push_back( - PendingOnGetDialogs{std::move(dialog_folders), total_count, std::move(messages), std::move(promise)}); + pending_on_get_dialogs_.push_back(PendingOnGetDialogs{folder_id, std::move(dialog_folders), total_count, + std::move(messages), std::move(promise)}); return; } bool from_dialog_list = total_count >= 0; @@ -10734,8 +10806,9 @@ void MessagesManager::on_get_dialogs(vector> DialogId dialog_id(static_cast(dialog_folders[0].get())->peer_); if (running_get_channel_difference(dialog_id)) { LOG(INFO) << "Postpone result of channels getDialogs for " << dialog_id; - pending_channel_on_get_dialogs_.emplace(dialog_id, PendingOnGetDialogs{std::move(dialog_folders), total_count, - std::move(messages), std::move(promise)}); + pending_channel_on_get_dialogs_.emplace( + dialog_id, PendingOnGetDialogs{folder_id, std::move(dialog_folders), total_count, std::move(messages), + std::move(promise)}); return; } } @@ -10750,9 +10823,9 @@ void MessagesManager::on_get_dialogs(vector> auto folder = telegram_api::move_object_as(dialog_folder); if (from_pinned_dialog_list) { // TODO updata unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int - FolderId folder_id(folder->folder_->id_); - if (folder_id == FolderId::archive()) { - // archive is expected + FolderId folder_folder_id(folder->folder_->id_); + if (folder_folder_id == FolderId::archive()) { + // archive is expected in pinned dialogs list break; } } @@ -10819,6 +10892,12 @@ void MessagesManager::on_get_dialogs(vector> LOG(ERROR) << "Last " << last_message_id << " in " << dialog_id << " not found"; return promise.set_error(Status::Error(500, "Wrong query result returned: last message not found")); } + FolderId dialog_folder_id((dialog->flags_ & DIALOG_FLAG_HAS_FOLDER_ID) != 0 ? dialog->folder_id_ : 0); + if (dialog_folder_id != folder_id) { + LOG(ERROR) << "Receive " << dialog_id << " in " << dialog_folder_id << " instead of " << folder_id; + continue; + } + DialogDate dialog_date = it->second; CHECK(dialog_date.get_dialog_id() == dialog_id); @@ -10832,23 +10911,24 @@ void MessagesManager::on_get_dialogs(vector> } } + auto &list = get_dialog_list(folder_id); if (from_dialog_list) { if (dialogs.empty()) { // if there is no more dialogs on the server max_dialog_date = MAX_DIALOG_DATE; } - if (last_server_dialog_date_ < max_dialog_date) { - last_server_dialog_date_ = max_dialog_date; - update_last_dialog_date(); + if (list.last_server_dialog_date_ < max_dialog_date) { + list.last_server_dialog_date_ = max_dialog_date; + update_last_dialog_date(folder_id); } else { LOG(ERROR) << "Last server dialog date didn't increased"; } } if (from_pinned_dialog_list) { max_dialog_date = DialogDate(get_dialog_order(MessageId(), MIN_PINNED_DIALOG_DATE - 1), DialogId()); - if (last_server_dialog_date_ < max_dialog_date) { - last_server_dialog_date_ = max_dialog_date; - update_last_dialog_date(); + if (list.last_server_dialog_date_ < max_dialog_date) { + list.last_server_dialog_date_ = max_dialog_date; + update_last_dialog_date(folder_id); } } @@ -10878,7 +10958,7 @@ void MessagesManager::on_get_dialogs(vector> } bool is_new = d->last_new_message_id == MessageId(); - set_dialog_folder_id(d, FolderId((dialog->flags_ & DIALOG_FLAG_HAS_PTS) != 0 ? dialog->folder_id_ : 0)); + set_dialog_folder_id(d, FolderId((dialog->flags_ & DIALOG_FLAG_HAS_FOLDER_ID) != 0 ? dialog->folder_id_ : 0)); on_update_dialog_notify_settings(dialog_id, std::move(dialog->notify_settings_), "on_get_dialogs"); if (!d->notification_settings.is_synchronized) { @@ -10911,10 +10991,6 @@ void MessagesManager::on_get_dialogs(vector> // TODO add pinned_message_id to telegram_api::dialog get_dialog_pinned_message(dialog_id, Auto()); } - if (!d->is_folder_id_inited && !td_->auth_manager_->is_bot()) { - // asynchronously get dialog folder message from the server - get_dialog_info_full(dialog_id, Auto()); - } need_update_dialog_pos |= update_dialog_draft_message( d, get_draft_message(td_->contacts_manager_.get(), std::move(dialog->draft_)), true, false); @@ -11002,7 +11078,7 @@ void MessagesManager::on_get_dialogs(vector> being_added_dialog_id_ = DialogId(); } if (from_pinned_dialog_list) { - auto pinned_dialog_ids = remove_secret_chat_dialog_ids(get_pinned_dialogs()); + auto pinned_dialog_ids = remove_secret_chat_dialog_ids(get_pinned_dialogs(folder_id)); std::reverse(pinned_dialog_ids.begin(), pinned_dialog_ids.end()); if (pinned_dialog_ids != added_dialog_ids) { LOG(INFO) << "Repair pinned dialogs order from " << format::as_array(pinned_dialog_ids) << " to " @@ -11687,11 +11763,14 @@ bool MessagesManager::load_dialog(DialogId dialog_id, int left_tries, Promise MessagesManager::get_dialogs(DialogDate offset, int32 limit, bool force, Promise &&promise) { - LOG(INFO) << "Get chats with offset " << offset << " and limit " << limit << ". Know about order of " - << ordered_dialogs_.size() << " chat(s). last_dialog_date_ = " << last_dialog_date_ - << ", last_server_dialog_date_ = " << last_server_dialog_date_ - << ", last_loaded_database_dialog_date_ = " << last_loaded_database_dialog_date_; +vector MessagesManager::get_dialogs(FolderId folder_id, DialogDate offset, int32 limit, bool force, + Promise &&promise) { + auto &list = get_dialog_list(folder_id); + LOG(INFO) << "Get chats in " << folder_id << " with offset " << offset << " and limit " << limit + << ". Know about order of " << list.ordered_dialogs_.size() + << " chat(s). last_dialog_date = " << list.last_dialog_date_ + << ", last_server_dialog_date = " << list.last_server_dialog_date_ + << ", last_loaded_database_dialog_date = " << list.last_loaded_database_dialog_date_; vector result; if (limit <= 0) { @@ -11703,8 +11782,8 @@ vector MessagesManager::get_dialogs(DialogDate offset, int32 limit, bo limit = MAX_GET_DIALOGS; } - auto it = ordered_dialogs_.upper_bound(offset); - auto end = ordered_dialogs_.end(); + auto it = list.ordered_dialogs_.upper_bound(offset); + auto end = list.ordered_dialogs_.end(); while (it != end && limit-- > 0) { result.push_back(it->get_dialog_id()); ++it; @@ -11715,44 +11794,45 @@ vector MessagesManager::get_dialogs(DialogDate offset, int32 limit, bo return result; } - load_dialog_list(limit, false, std::move(promise)); + load_dialog_list(folder_id, limit, false, std::move(promise)); return result; } -void MessagesManager::load_dialog_list(int32 limit, bool only_local, Promise &&promise) { - if (last_dialog_date_ == MAX_DIALOG_DATE) { +void MessagesManager::load_dialog_list(FolderId folder_id, int32 limit, bool only_local, Promise &&promise) { + auto &list = get_dialog_list(folder_id); + if (list.last_dialog_date_ == MAX_DIALOG_DATE) { return promise.set_value(Unit()); } - bool use_database = - G()->parameters().use_message_db && last_loaded_database_dialog_date_ < last_database_server_dialog_date_; + bool use_database = G()->parameters().use_message_db && + list.last_loaded_database_dialog_date_ < list.last_database_server_dialog_date_; if (only_local && !use_database) { return promise.set_value(Unit()); } - LOG(INFO) << "Load dialog list with limit " << limit; - auto &multipromise = load_dialog_list_multipromise_; + LOG(INFO) << "Load dialog list in " << folder_id << " with limit " << limit; + auto &multipromise = list.load_dialog_list_multipromise_; multipromise.add_promise(std::move(promise)); if (multipromise.promise_count() != 1) { // queries have already been sent, just wait for the result - if (use_database && load_dialog_list_limit_max_ != 0) { - load_dialog_list_limit_max_ = max(load_dialog_list_limit_max_, limit); + if (use_database && list.load_dialog_list_limit_max_ != 0) { + list.load_dialog_list_limit_max_ = max(list.load_dialog_list_limit_max_, limit); } return; } bool is_query_sent = false; if (use_database) { - load_dialog_list_from_database(limit, multipromise.get_promise()); + load_dialog_list_from_database(folder_id, limit, multipromise.get_promise()); is_query_sent = true; } else { - LOG(INFO) << "Get dialogs from " << last_server_dialog_date_; - reload_pinned_dialogs(multipromise.get_promise()); - if (last_dialog_date_ == last_server_dialog_date_) { + LOG(INFO) << "Get dialogs from " << list.last_server_dialog_date_; + reload_pinned_dialogs(folder_id, multipromise.get_promise()); + if (list.last_dialog_date_ == list.last_server_dialog_date_) { send_closure(td_->create_net_actor(multipromise.get_promise()), &GetDialogListActor::send, - last_server_dialog_date_.get_date(), - last_server_dialog_date_.get_message_id().get_next_server_message_id().get_server_message_id(), - last_server_dialog_date_.get_dialog_id(), int32{MAX_GET_DIALOGS}, + folder_id, list.last_server_dialog_date_.get_date(), + list.last_server_dialog_date_.get_message_id().get_next_server_message_id().get_server_message_id(), + list.last_server_dialog_date_.get_dialog_id(), int32{MAX_GET_DIALOGS}, get_sequence_dispatcher_id(DialogId(), MessageContentType::None)); is_query_sent = true; } @@ -11760,36 +11840,48 @@ void MessagesManager::load_dialog_list(int32 limit, bool only_local, Promise &&promise) { - LOG(INFO) << "Load " << limit << " dialogs from database from " << last_loaded_database_dialog_date_ - << ", last database server dialog date = " << last_database_server_dialog_date_; +void MessagesManager::load_dialog_list_from_database(FolderId folder_id, int32 limit, Promise &&promise) { + auto &list = get_dialog_list(folder_id); + LOG(INFO) << "Load " << limit << " chats in " << folder_id << " from database from " + << list.last_loaded_database_dialog_date_ + << ", last database server dialog date = " << list.last_database_server_dialog_date_; - CHECK(load_dialog_list_limit_max_ == 0); - load_dialog_list_limit_max_ = limit; + CHECK(list.load_dialog_list_limit_max_ == 0); + list.load_dialog_list_limit_max_ = limit; G()->td_db()->get_dialog_db_async()->get_dialogs( - last_loaded_database_dialog_date_.get_order(), last_loaded_database_dialog_date_.get_dialog_id(), limit, - PromiseCreator::lambda( - [actor_id = actor_id(this), limit, promise = std::move(promise)](vector result) mutable { - send_closure(actor_id, &MessagesManager::on_get_dialogs_from_database, limit, std::move(result), - std::move(promise)); - })); + folder_id, list.last_loaded_database_dialog_date_.get_order(), + list.last_loaded_database_dialog_date_.get_dialog_id(), limit, + PromiseCreator::lambda([actor_id = actor_id(this), folder_id, limit, + promise = std::move(promise)](vector result) mutable { + send_closure(actor_id, &MessagesManager::on_get_dialogs_from_database, folder_id, limit, std::move(result), + std::move(promise)); + })); } -void MessagesManager::on_get_dialogs_from_database(int32 limit, vector &&dialogs, +void MessagesManager::on_get_dialogs_from_database(FolderId folder_id, int32 limit, vector &&dialogs, Promise &&promise) { - LOG(INFO) << "Receive " << dialogs.size() << " from expected " << limit - << " dialogs in result of GetDialogsFromDatabase"; + auto &list = get_dialog_list(folder_id); + LOG(INFO) << "Receive " << dialogs.size() << " from expected " << limit << " chats in " << folder_id + << " in from database"; int32 new_get_dialogs_limit = 0; int32 have_more_dialogs_in_database = (limit == static_cast(dialogs.size())); - if (have_more_dialogs_in_database && limit < load_dialog_list_limit_max_) { - new_get_dialogs_limit = load_dialog_list_limit_max_ - limit; + if (have_more_dialogs_in_database && limit < list.load_dialog_list_limit_max_) { + new_get_dialogs_limit = list.load_dialog_list_limit_max_ - limit; } - load_dialog_list_limit_max_ = 0; + list.load_dialog_list_limit_max_ = 0; DialogDate max_dialog_date = MIN_DIALOG_DATE; + size_t dialogs_skipped = 0; for (auto &dialog : dialogs) { Dialog *d = on_load_dialog_from_database(DialogId(), std::move(dialog)); if (d == nullptr) { + dialogs_skipped++; + continue; + } + if (d->folder_id != folder_id) { + LOG(INFO) << "Skip " << d->dialog_id << " received from database, because it is in " << d->folder_id + << " instead of " << folder_id; + dialogs_skipped++; continue; } @@ -11801,98 +11893,94 @@ void MessagesManager::on_get_dialogs_from_database(int32 limit, vector(this)); - } - preload_dialog_list_timeout_.set_timeout_in(0.2); - + preload_dialog_list_timeout_.add_timeout_in(folder_id.get(), 0.2); promise.set_value(Unit()); } else { - load_dialog_list_from_database(new_get_dialogs_limit, std::move(promise)); + load_dialog_list_from_database(folder_id, new_get_dialogs_limit, std::move(promise)); } } -void MessagesManager::preload_dialog_list(void *messages_manager_void) { +void MessagesManager::preload_dialog_list(FolderId folder_id) { if (G()->close_flag()) { - LOG(INFO) << "Skip chat list preload, because of closing"; + LOG(INFO) << "Skip chat list preload because of closing"; return; } - CHECK(messages_manager_void != nullptr); - auto messages_manager = static_cast(messages_manager_void); - + auto &list = get_dialog_list(folder_id); CHECK(G()->parameters().use_message_db); - if (messages_manager->load_dialog_list_multipromise_.promise_count() != 0) { + if (list.load_dialog_list_multipromise_.promise_count() != 0) { LOG(INFO) << "Skip chat list preload, because there is a pending load chat list request"; return; } - if (messages_manager->ordered_dialogs_.size() > MAX_PRELOADED_DIALOGS) { + if (list.ordered_dialogs_.size() > MAX_PRELOADED_DIALOGS) { // do nothing if there are more than MAX_PRELOADED_DIALOGS dialogs already loaded - messages_manager->recalc_unread_count(); + recalc_unread_count(folder_id); return; } - if (messages_manager->last_loaded_database_dialog_date_ < messages_manager->last_database_server_dialog_date_) { + if (list.last_loaded_database_dialog_date_ < list.last_database_server_dialog_date_) { // if there are some dialogs in database, preload some of them - messages_manager->load_dialog_list(20, true, Auto()); - } else if (messages_manager->last_dialog_date_ != MAX_DIALOG_DATE) { + load_dialog_list(folder_id, 20, true, Auto()); + } else if (list.last_dialog_date_ != MAX_DIALOG_DATE) { // otherwise load more dialogs from the server - messages_manager->load_dialog_list(MAX_GET_DIALOGS, false, - PromiseCreator::lambda([messages_manager](Result result) { - if (result.is_ok()) { - messages_manager->recalc_unread_count(); - } - })); + load_dialog_list(folder_id, MAX_GET_DIALOGS, false, + PromiseCreator::lambda([actor_id = actor_id(this), folder_id](Result result) { + if (result.is_ok()) { + send_closure(actor_id, &MessagesManager::recalc_unread_count, folder_id); + } + })); } else { - messages_manager->recalc_unread_count(); + recalc_unread_count(folder_id); } } -vector MessagesManager::get_pinned_dialogs() const { +vector MessagesManager::get_pinned_dialogs(FolderId folder_id) const { vector result; - auto it = ordered_dialogs_.begin(); - auto end = ordered_dialogs_.end(); - while (it != end && it->get_date() >= MIN_PINNED_DIALOG_DATE) { - if (it->get_order() != SPONSORED_DIALOG_ORDER) { - result.push_back(it->get_dialog_id()); + auto *list = get_dialog_list(folder_id); + if (list != nullptr) { + for (const DialogDate &dialog_date : list->ordered_dialogs_) { + if (dialog_date.get_date() < MIN_PINNED_DIALOG_DATE) { + break; + } + if (dialog_date.get_order() != SPONSORED_DIALOG_ORDER) { + result.push_back(dialog_date.get_dialog_id()); + } } - ++it; } return result; } -void MessagesManager::reload_pinned_dialogs(Promise &&promise) { +void MessagesManager::reload_pinned_dialogs(FolderId folder_id, Promise &&promise) { if (G()->close_flag()) { return promise.set_error(Status::Error(500, "Request aborted")); } send_closure(td_->create_net_actor(std::move(promise)), &GetPinnedDialogsActor::send, - get_sequence_dispatcher_id(DialogId(), MessageContentType::None)); + folder_id, get_sequence_dispatcher_id(DialogId(), MessageContentType::None)); } vector MessagesManager::search_public_dialogs(const string &query, Promise &&promise) { @@ -12924,11 +13012,16 @@ void MessagesManager::clear_all_draft_messages(bool exclude_secret_chats, Promis td_->create_handler(std::move(promise))->send(); } -int32 MessagesManager::get_pinned_dialogs_limit() { - int32 limit = G()->shared_config().get_option_integer("pinned_chat_count_max"); +int32 MessagesManager::get_pinned_dialogs_limit(FolderId folder_id) { + Slice key{"pinned_chat_count_max"}; + int32 default_limit = 5; + if (folder_id != FolderId::main()) { + key = Slice("pinned_archived_chat_count_max"); + default_limit = 100; + } + int32 limit = clamp(G()->shared_config().get_option_integer(key), 0, 1000000); if (limit <= 0) { - const int32 DEFAULT_PINNED_DIALOGS_LIMIT = 5; - return DEFAULT_PINNED_DIALOGS_LIMIT; + return default_limit; } return limit; } @@ -12959,7 +13052,7 @@ Status MessagesManager::toggle_dialog_is_pinned(DialogId dialog_id, bool is_pinn } if (is_pinned) { - auto pinned_dialog_ids = get_pinned_dialogs(); + auto pinned_dialog_ids = get_pinned_dialogs(d->folder_id); auto pinned_dialog_count = pinned_dialog_ids.size(); auto secret_pinned_dialog_count = std::count_if(pinned_dialog_ids.begin(), pinned_dialog_ids.end(), @@ -12968,7 +13061,7 @@ Status MessagesManager::toggle_dialog_is_pinned(DialogId dialog_id, bool is_pinn ? secret_pinned_dialog_count : pinned_dialog_count - secret_pinned_dialog_count; - if (dialog_count >= static_cast(get_pinned_dialogs_limit())) { + if (dialog_count >= static_cast(get_pinned_dialogs_limit(d->folder_id))) { return Status::Error(400, "Maximum number of pinned chats exceeded"); } } @@ -13023,14 +13116,14 @@ void MessagesManager::toggle_dialog_is_pinned_on_server(DialogId dialog_id, bool td_->create_handler(get_erase_logevent_promise(logevent_id))->send(dialog_id, is_pinned); } -Status MessagesManager::set_pinned_dialogs(vector dialog_ids) { +Status MessagesManager::set_pinned_dialogs(FolderId folder_id, vector dialog_ids) { if (td_->auth_manager_->is_bot()) { return Status::Error(6, "Bots can't reorder pinned chats"); } int32 dialog_count = 0; int32 secret_dialog_count = 0; - auto dialog_count_limit = get_pinned_dialogs_limit(); + auto dialog_count_limit = get_pinned_dialogs_limit(folder_id); for (auto dialog_id : dialog_ids) { Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { @@ -13054,12 +13147,12 @@ Status MessagesManager::set_pinned_dialogs(vector dialog_ids) { return Status::Error(400, "Duplicate chats in the list of pinned chats"); } - auto pinned_dialog_ids = get_pinned_dialogs(); + auto pinned_dialog_ids = get_pinned_dialogs(folder_id); if (pinned_dialog_ids == dialog_ids) { return Status::OK(); } - LOG(INFO) << "Reorder pinned chats order from " << format::as_array(pinned_dialog_ids) << " to " - << format::as_array(dialog_ids); + LOG(INFO) << "Reorder pinned chats order in " << folder_id << " from " << format::as_array(pinned_dialog_ids) + << " to " << format::as_array(dialog_ids); auto server_old_dialog_ids = remove_secret_chat_dialog_ids(pinned_dialog_ids); auto server_new_dialog_ids = remove_secret_chat_dialog_ids(dialog_ids); @@ -13088,38 +13181,47 @@ Status MessagesManager::set_pinned_dialogs(vector dialog_ids) { } if (server_old_dialog_ids != server_new_dialog_ids) { - reorder_pinned_dialogs_on_server(server_new_dialog_ids, 0); + reorder_pinned_dialogs_on_server(folder_id, server_new_dialog_ids, 0); } return Status::OK(); } class MessagesManager::ReorderPinnedDialogsOnServerLogEvent { public: + FolderId folder_id_; vector dialog_ids_; template void store(StorerT &storer) const { + td::store(folder_id_, storer); td::store(dialog_ids_, storer); } template void parse(ParserT &parser) { + if (parser.version() >= static_cast(Version::SupportFolders)) { + td::parse(folder_id_, parser); + } else { + folder_id_ = FolderId(); + } td::parse(dialog_ids_, parser); } }; -uint64 MessagesManager::save_reorder_pinned_dialogs_on_server_logevent(const vector &dialog_ids) { - ReorderPinnedDialogsOnServerLogEvent logevent{dialog_ids}; +uint64 MessagesManager::save_reorder_pinned_dialogs_on_server_logevent(FolderId folder_id, + const vector &dialog_ids) { + ReorderPinnedDialogsOnServerLogEvent logevent{folder_id, dialog_ids}; auto storer = LogEventStorerImpl(logevent); return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::ReorderPinnedDialogsOnServer, storer); } -void MessagesManager::reorder_pinned_dialogs_on_server(const vector &dialog_ids, uint64 logevent_id) { +void MessagesManager::reorder_pinned_dialogs_on_server(FolderId folder_id, const vector &dialog_ids, + uint64 logevent_id) { if (logevent_id == 0 && G()->parameters().use_message_db) { - logevent_id = save_reorder_pinned_dialogs_on_server_logevent(dialog_ids); + logevent_id = save_reorder_pinned_dialogs_on_server_logevent(folder_id, dialog_ids); } - td_->create_handler(get_erase_logevent_promise(logevent_id))->send(dialog_ids); + td_->create_handler(get_erase_logevent_promise(logevent_id))->send(folder_id, dialog_ids); } Status MessagesManager::toggle_dialog_is_marked_as_unread(DialogId dialog_id, bool is_marked_as_unread) { @@ -13827,17 +13929,24 @@ td_api::object_ptr MessagesManager::get_chat_type_object(Dialo } } -td_api::object_ptr MessagesManager::get_chat_list_type_object(const Dialog *d) { +td_api::object_ptr MessagesManager::get_chat_list_object(const Dialog *d) { if (d->order == DEFAULT_ORDER) { return nullptr; } - if (d->folder_id == FolderId::archive()) { - return td_api::make_object(); - } - if (d->folder_id != FolderId::main()) { + if (d->folder_id != FolderId::main() && d->folder_id != FolderId::archive()) { LOG(ERROR) << "Have " << d->dialog_id << " in unknown " << d->folder_id; } - return td_api::make_object(); + return get_chat_list_object(d->folder_id); +} + +td_api::object_ptr MessagesManager::get_chat_list_object(FolderId folder_id) { + if (folder_id == FolderId::archive()) { + return td_api::make_object(); + } + if (folder_id == FolderId::main()) { + return td_api::make_object(); + } + return td_api::make_object(); } td_api::object_ptr MessagesManager::get_chat_object(const Dialog *d) const { @@ -13885,8 +13994,8 @@ td_api::object_ptr MessagesManager::get_chat_object(const Dialog * } return make_tl_object( - d->dialog_id.get(), get_chat_type_object(d->dialog_id), get_chat_list_type_object(d), - get_dialog_title(d->dialog_id), get_chat_photo_object(td_->file_manager_.get(), get_dialog_photo(d->dialog_id)), + d->dialog_id.get(), get_chat_type_object(d->dialog_id), get_chat_list_object(d), get_dialog_title(d->dialog_id), + get_chat_photo_object(td_->file_manager_.get(), get_dialog_photo(d->dialog_id)), get_dialog_permissions(d->dialog_id).get_chat_permissions_object(), get_message_object(d->dialog_id, get_message(d, d->last_message_id)), get_dialog_public_order(d), d->pinned_order != DEFAULT_ORDER, d->is_marked_as_unread, d->order == SPONSORED_DIALOG_ORDER, can_delete_for_self, @@ -13957,36 +14066,65 @@ int32 MessagesManager::get_scope_mute_until(DialogId dialog_id) const { vector MessagesManager::get_dialog_notification_settings_exceptions(NotificationSettingsScope scope, bool filter_scope, bool compare_sound, bool force, Promise &&promise) { - if (last_dialog_date_ == MAX_DIALOG_DATE || force) { - vector result; - auto my_dialog_id = get_my_dialog_id(); - for (const auto &it : ordered_server_dialogs_) { - auto dialog_id = it.get_dialog_id(); - if (filter_scope && get_dialog_notification_setting_scope(dialog_id) != scope) { - continue; - } - if (dialog_id == my_dialog_id) { - continue; - } + bool have_all_dialogs = true; + bool have_main_list = false; + bool have_archive_list = false; + for (auto &list : dialog_lists_) { + if (list.second.last_dialog_date_ != MAX_DIALOG_DATE) { + have_all_dialogs = false; + } + have_main_list |= list.first == FolderId::main(); + have_archive_list |= list.first == FolderId::archive(); + } + if (!have_main_list || !have_archive_list) { + have_all_dialogs = false; + } - Dialog *d = get_dialog(dialog_id); - CHECK(d != nullptr); - if (d->order == DEFAULT_ORDER) { - break; + if (have_all_dialogs || force) { + vector ordered_dialogs; + auto my_dialog_id = get_my_dialog_id(); + for (auto &list : dialog_lists_) { + for (const auto &it : list.second.ordered_server_dialogs_) { + auto dialog_id = it.get_dialog_id(); + if (filter_scope && get_dialog_notification_setting_scope(dialog_id) != scope) { + continue; + } + if (dialog_id == my_dialog_id) { + continue; + } + + Dialog *d = get_dialog(dialog_id); + CHECK(d != nullptr); + if (d->order == DEFAULT_ORDER) { + break; + } + if (are_default_dialog_notification_settings(d->notification_settings, compare_sound)) { + continue; + } + if (is_dialog_message_notification_disabled(dialog_id, std::numeric_limits::max())) { + continue; + } + ordered_dialogs.push_back(DialogDate(d->order, dialog_id)); } - if (are_default_dialog_notification_settings(d->notification_settings, compare_sound)) { - continue; - } - if (is_dialog_message_notification_disabled(dialog_id, std::numeric_limits::max())) { - continue; - } - result.push_back(dialog_id); + } + std::sort(ordered_dialogs.begin(), ordered_dialogs.end()); + + vector result; + for (auto &it : ordered_dialogs) { + CHECK(result.empty() || result.back() != it.get_dialog_id()); + result.push_back(it.get_dialog_id()); } promise.set_value(Unit()); return result; } - load_dialog_list(MAX_GET_DIALOGS, true, Auto()); + load_dialog_list(FolderId::main(), MAX_GET_DIALOGS, true, Auto()); + load_dialog_list(FolderId::archive(), MAX_GET_DIALOGS, true, Auto()); + for (auto &list : dialog_lists_) { + if (list.first != FolderId::main() && list.first != FolderId::archive()) { + load_dialog_list(list.first, MAX_GET_DIALOGS, true, Auto()); + } + } td_->create_handler(std::move(promise))->send(scope, filter_scope, compare_sound); return {}; @@ -20523,81 +20661,89 @@ void MessagesManager::send_update_chat_last_message_impl(const Dialog *d, const send_closure(G()->td(), &Td::send_update, std::move(update)); } -void MessagesManager::send_update_unread_message_count(DialogId dialog_id, bool force, const char *source) { +void MessagesManager::send_update_unread_message_count(FolderId folder_id, DialogId dialog_id, bool force, + const char *source) { if (td_->auth_manager_->is_bot() || !G()->parameters().use_message_db) { return; } - CHECK(is_message_unread_count_inited_); - if (unread_message_muted_count_ < 0 || unread_message_muted_count_ > unread_message_total_count_) { - LOG(ERROR) << "Unread message count became invalid: " << unread_message_total_count_ << '/' - << unread_message_total_count_ - unread_message_muted_count_ << " from " << source << " and " + auto &list = get_dialog_list(folder_id); + CHECK(list.is_message_unread_count_inited_); + if (list.unread_message_muted_count_ < 0 || list.unread_message_muted_count_ > list.unread_message_total_count_) { + LOG(ERROR) << "Unread message count became invalid: " << list.unread_message_total_count_ << '/' + << list.unread_message_total_count_ - list.unread_message_muted_count_ << " from " << source << " and " << dialog_id; - if (unread_message_muted_count_ < 0) { - unread_message_muted_count_ = 0; + if (list.unread_message_muted_count_ < 0) { + list.unread_message_muted_count_ = 0; } - if (unread_message_muted_count_ > unread_message_total_count_) { - unread_message_total_count_ = unread_message_muted_count_; - } - } - G()->td_db()->get_binlog_pmc()->set("unread_message_count", - PSTRING() << unread_message_total_count_ << ' ' << unread_message_muted_count_); - int32 unread_unmuted_count = unread_message_total_count_ - unread_message_muted_count_; - if (!force && running_get_difference_) { - LOG(INFO) << "Postpone updateUnreadMessageCount to " << unread_message_total_count_ << '/' << unread_unmuted_count - << " from " << source << " and " << dialog_id; - have_postponed_unread_message_count_update_ = true; - } else { - have_postponed_unread_message_count_update_ = false; - LOG(INFO) << "Send updateUnreadMessageCount to " << unread_message_total_count_ << '/' << unread_unmuted_count - << " from " << source << " and " << dialog_id; - send_closure(G()->td(), &Td::send_update, get_update_unread_message_count_object()); - } -} - -void MessagesManager::send_update_unread_chat_count(DialogId dialog_id, bool force, const char *source) { - if (td_->auth_manager_->is_bot() || !G()->parameters().use_message_db) { - return; - } - - CHECK(is_dialog_unread_count_inited_); - if (unread_dialog_muted_marked_count_ < 0 || unread_dialog_marked_count_ < unread_dialog_muted_marked_count_ || - unread_dialog_muted_count_ < unread_dialog_muted_marked_count_ || - unread_dialog_total_count_ + unread_dialog_muted_marked_count_ < - unread_dialog_muted_count_ + unread_dialog_marked_count_) { - LOG(ERROR) << "Unread chat count became invalid: " << unread_dialog_total_count_ << '/' - << unread_dialog_total_count_ - unread_dialog_muted_count_ << '/' << unread_dialog_marked_count_ << '/' - << unread_dialog_marked_count_ - unread_dialog_muted_marked_count_ << " from " << source << " and " - << dialog_id; - if (unread_dialog_muted_marked_count_ < 0) { - unread_dialog_muted_marked_count_ = 0; - } - if (unread_dialog_marked_count_ < unread_dialog_muted_marked_count_) { - unread_dialog_marked_count_ = unread_dialog_muted_marked_count_; - } - if (unread_dialog_muted_count_ < unread_dialog_muted_marked_count_) { - unread_dialog_muted_count_ = unread_dialog_muted_marked_count_; - } - if (unread_dialog_total_count_ + unread_dialog_muted_marked_count_ < - unread_dialog_muted_count_ + unread_dialog_marked_count_) { - unread_dialog_total_count_ = - unread_dialog_muted_count_ + unread_dialog_marked_count_ - unread_dialog_muted_marked_count_; + if (list.unread_message_muted_count_ > list.unread_message_total_count_) { + list.unread_message_total_count_ = list.unread_message_muted_count_; } } G()->td_db()->get_binlog_pmc()->set( - "unread_dialog_count", PSTRING() << unread_dialog_total_count_ << ' ' << unread_dialog_muted_count_ << ' ' - << unread_dialog_marked_count_ << ' ' << unread_dialog_muted_marked_count_); - bool need_postpone = !force && running_get_difference_; - int32 unread_unmuted_count = unread_dialog_total_count_ - unread_dialog_muted_count_; - int32 unread_unmuted_marked_count = unread_dialog_marked_count_ - unread_dialog_muted_marked_count_; - LOG(INFO) << (need_postpone ? "Postpone" : "Send") << " updateUnreadChatCount to " << unread_dialog_total_count_ - << '/' << unread_unmuted_count << '/' << unread_dialog_marked_count_ << '/' << unread_unmuted_marked_count - << " from " << source << " and " << dialog_id; - if (need_postpone) { - have_postponed_unread_chat_count_update_ = true; + PSTRING() << "unread_message_count" << folder_id.get(), + PSTRING() << list.unread_message_total_count_ << ' ' << list.unread_message_muted_count_); + int32 unread_unmuted_count = list.unread_message_total_count_ - list.unread_message_muted_count_; + if (!force && running_get_difference_) { + LOG(INFO) << "Postpone updateUnreadMessageCount in " << folder_id << " to " << list.unread_message_total_count_ + << '/' << unread_unmuted_count << " from " << source << " and " << dialog_id; + postponed_unread_message_count_updates_.insert(folder_id); } else { - have_postponed_unread_chat_count_update_ = false; - send_closure(G()->td(), &Td::send_update, get_update_unread_chat_count_object()); + postponed_unread_message_count_updates_.erase(folder_id); + LOG(INFO) << "Send updateUnreadMessageCount in " << folder_id << " to " << list.unread_message_total_count_ << '/' + << unread_unmuted_count << " from " << source << " and " << dialog_id; + send_closure(G()->td(), &Td::send_update, get_update_unread_message_count_object(folder_id, list)); + } +} + +void MessagesManager::send_update_unread_chat_count(FolderId folder_id, DialogId dialog_id, bool force, + const char *source) { + if (td_->auth_manager_->is_bot() || !G()->parameters().use_message_db) { + return; + } + + auto &list = get_dialog_list(folder_id); + CHECK(list.is_dialog_unread_count_inited_); + if (list.unread_dialog_muted_marked_count_ < 0 || + list.unread_dialog_marked_count_ < list.unread_dialog_muted_marked_count_ || + list.unread_dialog_muted_count_ < list.unread_dialog_muted_marked_count_ || + list.unread_dialog_total_count_ + list.unread_dialog_muted_marked_count_ < + list.unread_dialog_muted_count_ + list.unread_dialog_marked_count_) { + LOG(ERROR) << "Unread chat count became invalid: " << list.unread_dialog_total_count_ << '/' + << list.unread_dialog_total_count_ - list.unread_dialog_muted_count_ << '/' + << list.unread_dialog_marked_count_ << '/' + << list.unread_dialog_marked_count_ - list.unread_dialog_muted_marked_count_ << " from " << source + << " and " << dialog_id; + if (list.unread_dialog_muted_marked_count_ < 0) { + list.unread_dialog_muted_marked_count_ = 0; + } + if (list.unread_dialog_marked_count_ < list.unread_dialog_muted_marked_count_) { + list.unread_dialog_marked_count_ = list.unread_dialog_muted_marked_count_; + } + if (list.unread_dialog_muted_count_ < list.unread_dialog_muted_marked_count_) { + list.unread_dialog_muted_count_ = list.unread_dialog_muted_marked_count_; + } + if (list.unread_dialog_total_count_ + list.unread_dialog_muted_marked_count_ < + list.unread_dialog_muted_count_ + list.unread_dialog_marked_count_) { + list.unread_dialog_total_count_ = + list.unread_dialog_muted_count_ + list.unread_dialog_marked_count_ - list.unread_dialog_muted_marked_count_; + } + } + G()->td_db()->get_binlog_pmc()->set( + PSTRING() << "unread_dialog_count" << folder_id.get(), + PSTRING() << list.unread_dialog_total_count_ << ' ' << list.unread_dialog_muted_count_ << ' ' + << list.unread_dialog_marked_count_ << ' ' << list.unread_dialog_muted_marked_count_); + bool need_postpone = !force && running_get_difference_; + int32 unread_unmuted_count = list.unread_dialog_total_count_ - list.unread_dialog_muted_count_; + int32 unread_unmuted_marked_count = list.unread_dialog_marked_count_ - list.unread_dialog_muted_marked_count_; + LOG(INFO) << (need_postpone ? "Postpone" : "Send") << " updateUnreadChatCount in " << folder_id << " to " + << list.unread_dialog_total_count_ << '/' << unread_unmuted_count << '/' << list.unread_dialog_marked_count_ + << '/' << unread_unmuted_marked_count << " from " << source << " and " << dialog_id; + if (need_postpone) { + postponed_unread_chat_count_updates_.insert(folder_id); + } else { + postponed_unread_chat_count_updates_.erase(folder_id); + send_closure(G()->td(), &Td::send_update, get_update_unread_chat_count_object(folder_id, list)); } } @@ -20673,10 +20819,10 @@ void MessagesManager::send_update_chat_online_member_count(DialogId dialog_id, i make_tl_object(dialog_id.get(), online_member_count)); } -void MessagesManager::send_update_chat_list_type(const Dialog *d) const { - LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_list_type"; +void MessagesManager::send_update_chat_chat_list(const Dialog *d) const { + LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_chat_list"; send_closure(G()->td(), &Td::send_update, - make_tl_object(d->dialog_id.get(), get_chat_list_type_object(d))); + make_tl_object(d->dialog_id.get(), get_chat_list_object(d))); } void MessagesManager::on_send_message_get_quick_ack(int64 random_id) { @@ -21330,7 +21476,7 @@ bool MessagesManager::update_dialog_draft_message(Dialog *d, unique_ptrpinned_order == DEFAULT_ORDER) { return; } @@ -21349,18 +21497,20 @@ void MessagesManager::on_update_dialog_is_pinned(DialogId dialog_id, bool is_pin update_dialog_pos(d, false, "on_update_dialog_is_pinned"); } -void MessagesManager::on_update_pinned_dialogs() { +void MessagesManager::on_update_pinned_dialogs(FolderId folder_id) { // TODO logevent + delete_logevent_promise - auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this)](Unit /* ignore result */) { - send_closure(actor_id, &MessagesManager::reload_pinned_dialogs, Promise()); + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), folder_id](Unit /* ignore result */) { + send_closure(actor_id, &MessagesManager::reload_pinned_dialogs, folder_id, Promise()); }); // max ordinary pinned dialogs + max pinned secret chats + sponsored proxy - size_t needed_dialogs = 2 * get_pinned_dialogs_limit() + 1; - if (ordered_dialogs_.size() >= needed_dialogs) { + size_t needed_dialogs = 2 * get_pinned_dialogs_limit(folder_id) + (folder_id == FolderId::main() ? 1 : 0); + auto &list = get_dialog_list(folder_id); + if (list.ordered_dialogs_.size() >= needed_dialogs) { query_promise.set_value(Unit()); } else { - load_dialog_list(narrow_cast(needed_dialogs - ordered_dialogs_.size()), true, std::move(query_promise)); + load_dialog_list(folder_id, narrow_cast(needed_dialogs - list.ordered_dialogs_.size()), true, + std::move(query_promise)); } } @@ -21399,16 +21549,17 @@ void MessagesManager::set_dialog_is_marked_as_unread(Dialog *d, bool is_marked_a send_closure(G()->td(), &Td::send_update, make_tl_object(d->dialog_id.get(), is_marked_as_unread)); + auto &list = get_dialog_list(d->folder_id); if (d->server_unread_count + d->local_unread_count == 0 && need_unread_counter(d->order) && - is_dialog_unread_count_inited_) { + list.is_dialog_unread_count_inited_) { int32 delta = d->is_marked_as_unread ? 1 : -1; - unread_dialog_total_count_ += delta; - unread_dialog_marked_count_ += delta; + list.unread_dialog_total_count_ += delta; + list.unread_dialog_marked_count_ += delta; if (is_dialog_muted(d)) { - unread_dialog_muted_count_ += delta; - unread_dialog_muted_marked_count_ += delta; + list.unread_dialog_muted_count_ += delta; + list.unread_dialog_muted_marked_count_ += delta; } - send_update_unread_chat_count(d->dialog_id, true, "set_dialog_is_marked_as_unread"); + send_update_unread_chat_count(d->folder_id, d->dialog_id, true, "set_dialog_is_marked_as_unread"); } } @@ -21475,14 +21626,24 @@ void MessagesManager::set_dialog_folder_id(Dialog *d, FolderId folder_id) { return; } + LOG(INFO) << "Change " << d->dialog_id << " folder from " << d->folder_id << " to " << folder_id; + + // first remove the dialog from the old chat list + // this will send updateChatChatList if needed + if (d->pinned_order != DEFAULT_ORDER) { + set_dialog_is_pinned(d, false); + } + set_dialog_order(d, DEFAULT_ORDER, true, false, "set_dialog_folder_id old"); + + // change the folder d->folder_id = folder_id; d->is_folder_id_inited = true; - on_dialog_updated(d->dialog_id, "set_dialog_folder_id"); - LOG(INFO) << "Set " << d->dialog_id << " folder to " << folder_id; - if (d->order != DEFAULT_ORDER) { - send_update_chat_list_type(d); - } + // update dialog position in the new folder + // this will send updateChatChatList if needed + update_dialog_pos(d, false, "set_dialog_folder_id new", true, false); + + on_dialog_updated(d->dialog_id, "set_dialog_folder_id"); } void MessagesManager::on_create_new_dialog_success(int64 random_id, tl_object_ptr &&updates, @@ -25053,7 +25214,8 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab // asynchronously get dialog pinned message from the server get_dialog_pinned_message(dialog_id, Auto()); } - if (being_added_dialog_id_ != dialog_id && !d->is_folder_id_inited && !td_->auth_manager_->is_bot()) { + if (being_added_dialog_id_ != dialog_id && !d->is_folder_id_inited && !td_->auth_manager_->is_bot() && + order != DEFAULT_ORDER) { // asynchronously get dialog folder id from the server get_dialog_info_full(dialog_id, Auto()); } @@ -25366,7 +25528,8 @@ int64 MessagesManager::get_dialog_order(MessageId message_id, int32 message_date int64 MessagesManager::get_dialog_public_order(const Dialog *d) const { DialogDate dialog_date(d->order, d->dialog_id); - return dialog_date <= last_dialog_date_ ? d->order : 0; + auto *list = get_dialog_list(d->folder_id); + return list != nullptr && dialog_date <= list->last_dialog_date_ ? d->order : 0; } int64 MessagesManager::get_next_pinned_dialog_order() { @@ -25485,7 +25648,7 @@ void MessagesManager::update_dialog_pos(Dialog *d, bool remove_from_dialog_list, return; } } - if (new_order == DEFAULT_ORDER && d->dialog_id == sponsored_dialog_id_) { + if (new_order == DEFAULT_ORDER && d->dialog_id == sponsored_dialog_id_ && d->folder_id == FolderId::main()) { new_order = SPONSORED_DIALOG_ORDER; } @@ -25501,13 +25664,14 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen DialogDate old_date(d->order, dialog_id); DialogDate new_date(new_order, dialog_id); + auto &list = get_dialog_list(d->folder_id); std::set *ordered_dialogs_set = nullptr; switch (dialog_id.get_type()) { case DialogType::User: case DialogType::Chat: case DialogType::Channel: case DialogType::SecretChat: - ordered_dialogs_set = &ordered_server_dialogs_; + ordered_dialogs_set = &list.ordered_server_dialogs_; break; case DialogType::None: default: @@ -25525,8 +25689,8 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen LOG(INFO) << "Update order of " << dialog_id << " from " << d->order << " to " << new_order << " from " << source; bool need_update = false; - if (old_date <= last_dialog_date_) { - if (ordered_dialogs_.erase(old_date) == 0) { + if (old_date <= list.last_dialog_date_) { + if (list.ordered_dialogs_.erase(old_date) == 0) { UNREACHABLE(); } need_update = true; @@ -25538,8 +25702,8 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen << dialog_id << " has non-default order after loading from database from " << source; int64 updated_to = 0; - if (new_date <= last_dialog_date_) { - ordered_dialogs_.insert(new_date); + if (new_date <= list.last_dialog_date_) { + list.ordered_dialogs_.insert(new_date); need_update = true; updated_to = new_order; } @@ -25550,25 +25714,26 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen bool is_sponsored = (new_order == SPONSORED_DIALOG_ORDER); bool had_unread_counter = need_unread_counter(d->order); bool has_unread_counter = need_unread_counter(new_order); - bool need_update_folder = (d->order == DEFAULT_ORDER || new_order == DEFAULT_ORDER); + bool is_removed_from_folder = new_order == DEFAULT_ORDER; + bool is_added_to_folder = d->order == DEFAULT_ORDER; if (!is_loaded_from_database && had_unread_counter != has_unread_counter && !td_->auth_manager_->is_bot()) { auto unread_count = d->server_unread_count + d->local_unread_count; const char *change_source = had_unread_counter ? "on_dialog_leave" : "on_dialog_join"; - if (unread_count != 0 && is_message_unread_count_inited_) { + if (unread_count != 0 && list.is_message_unread_count_inited_) { if (had_unread_counter) { unread_count = -unread_count; } else { CHECK(has_unread_counter); } - unread_message_total_count_ += unread_count; + list.unread_message_total_count_ += unread_count; if (is_dialog_muted(d)) { - unread_message_muted_count_ += unread_count; + list.unread_message_muted_count_ += unread_count; } - send_update_unread_message_count(dialog_id, true, change_source); + send_update_unread_message_count(d->folder_id, dialog_id, true, change_source); } - if ((unread_count != 0 || d->is_marked_as_unread) && is_dialog_unread_count_inited_) { + if ((unread_count != 0 || d->is_marked_as_unread) && list.is_dialog_unread_count_inited_) { int delta = 1; if (had_unread_counter) { delta = -1; @@ -25576,17 +25741,17 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen CHECK(has_unread_counter); } - unread_dialog_total_count_ += delta; + list.unread_dialog_total_count_ += delta; if (unread_count == 0 && d->is_marked_as_unread) { - unread_dialog_marked_count_ += delta; + list.unread_dialog_marked_count_ += delta; } if (is_dialog_muted(d)) { - unread_dialog_muted_count_ += delta; + list.unread_dialog_muted_count_ += delta; if (unread_count == 0 && d->is_marked_as_unread) { - unread_dialog_muted_marked_count_ += delta; + list.unread_dialog_muted_marked_count_ += delta; } } - send_update_unread_chat_count(dialog_id, true, change_source); + send_update_unread_chat_count(d->folder_id, dialog_id, true, change_source); } auto dialog_type = dialog_id.get_type(); @@ -25609,10 +25774,9 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen } update_dialogs_hints_rating(d); - if (need_update_folder) { - send_update_chat_list_type(d); + if (is_added_to_folder) { + send_update_chat_chat_list(d); } - if (was_sponsored != is_sponsored) { send_update_chat_is_sponsored(d); if (!is_loaded_from_database && is_sponsored) { @@ -25625,39 +25789,44 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen if (need_update && need_send_update_chat_order) { send_closure(G()->td(), &Td::send_update, make_tl_object(dialog_id.get(), updated_to)); } + if (is_removed_from_folder) { + send_update_chat_chat_list(d); + } return true; } -void MessagesManager::update_last_dialog_date() { - auto old_last_dialog_date = last_dialog_date_; - last_dialog_date_ = last_server_dialog_date_; // std::min - CHECK(old_last_dialog_date <= last_dialog_date_); +void MessagesManager::update_last_dialog_date(FolderId folder_id) { + auto &list = get_dialog_list(folder_id); + auto old_last_dialog_date = list.last_dialog_date_; + list.last_dialog_date_ = list.last_server_dialog_date_; // std::min + CHECK(old_last_dialog_date <= list.last_dialog_date_); - LOG(INFO) << "Update last dialog date from " << old_last_dialog_date << " to " << last_dialog_date_; - LOG(INFO) << "Know about " << ordered_server_dialogs_.size() << " chats"; + LOG(INFO) << "Update last dialog date from " << old_last_dialog_date << " to " << list.last_dialog_date_; + LOG(INFO) << "Know about " << list.ordered_server_dialogs_.size() << " chats"; - if (old_last_dialog_date != last_dialog_date_) { - for (auto it = ordered_server_dialogs_.upper_bound(old_last_dialog_date); - it != ordered_server_dialogs_.end() && *it <= last_dialog_date_; ++it) { + if (old_last_dialog_date != list.last_dialog_date_) { + for (auto it = list.ordered_server_dialogs_.upper_bound(old_last_dialog_date); + it != list.ordered_server_dialogs_.end() && *it <= list.last_dialog_date_; ++it) { auto dialog_id = it->get_dialog_id(); auto d = get_dialog(dialog_id); CHECK(d != nullptr); - ordered_dialogs_.insert(DialogDate(d->order, d->dialog_id)); + list.ordered_dialogs_.insert(DialogDate(d->order, d->dialog_id)); send_closure(G()->td(), &Td::send_update, make_tl_object(d->dialog_id.get(), d->order)); } - if (last_dialog_date_ == MAX_DIALOG_DATE) { - recalc_unread_count(); + if (list.last_dialog_date_ == MAX_DIALOG_DATE) { + recalc_unread_count(folder_id); } } - if (G()->parameters().use_message_db && last_database_server_dialog_date_ < last_server_dialog_date_) { - auto last_server_dialog_date_string = PSTRING() << last_server_dialog_date_.get_order() << ' ' - << last_server_dialog_date_.get_dialog_id().get(); - G()->td_db()->get_binlog_pmc()->set("last_server_dialog_date", last_server_dialog_date_string); + if (G()->parameters().use_message_db && list.last_database_server_dialog_date_ < list.last_server_dialog_date_) { + auto last_server_dialog_date_string = PSTRING() << list.last_server_dialog_date_.get_order() << ' ' + << list.last_server_dialog_date_.get_dialog_id().get(); + G()->td_db()->get_binlog_pmc()->set(PSTRING() << "last_server_dialog_date" << folder_id.get(), + last_server_dialog_date_string); LOG(INFO) << "Save last server dialog date " << last_server_dialog_date_string; - last_database_server_dialog_date_ = last_server_dialog_date_; - last_loaded_database_dialog_date_ = last_server_dialog_date_; + list.last_database_server_dialog_date_ = list.last_server_dialog_date_; + list.last_loaded_database_dialog_date_ = list.last_server_dialog_date_; } } @@ -25784,6 +25953,26 @@ MessagesManager::Dialog *MessagesManager::on_load_dialog_from_database(DialogId return add_new_dialog(parse_dialog(dialog_id, value), true); } +MessagesManager::DialogList &MessagesManager::get_dialog_list(FolderId folder_id) { + if (folder_id != FolderId::archive()) { + folder_id = FolderId::main(); + } + auto &list = dialog_lists_[folder_id]; + list.folder_id = folder_id; + return list; +} + +const MessagesManager::DialogList *MessagesManager::get_dialog_list(FolderId folder_id) const { + if (folder_id != FolderId::archive()) { + folder_id = FolderId::main(); + } + auto it = dialog_lists_.find(folder_id); + if (it == dialog_lists_.end()) { + return nullptr; + } + return &it->second; +} + void MessagesManager::load_notification_settings() { if (td_->auth_manager_->is_bot()) { return; @@ -26355,7 +26544,8 @@ void MessagesManager::after_get_channel_difference(DialogId dialog_id, bool succ PendingOnGetDialogs res = std::move(it->second); pending_channel_on_get_dialogs_.erase(it); - on_get_dialogs(std::move(res.dialogs), res.total_count, std::move(res.messages), std::move(res.promise)); + on_get_dialogs(res.folder_id, std::move(res.dialogs), res.total_count, std::move(res.messages), + std::move(res.promise)); } // TODO resend some messages @@ -26860,7 +27050,7 @@ void MessagesManager::on_binlog_events(vector &&events) { break; } - reorder_pinned_dialogs_on_server(dialog_ids, event.id_); + reorder_pinned_dialogs_on_server(log_event.folder_id_, dialog_ids, event.id_); break; } case LogEvent::HandlerType::ToggleDialogIsMarkedAsUnreadOnServer: { @@ -27096,7 +27286,8 @@ bool MessagesManager::load_recently_found_dialogs(Promise &promise) { } resolve_recently_found_dialogs_multipromise_.get_promise().set_value(Unit()); } else { - get_dialogs(MIN_DIALOG_DATE, MAX_GET_DIALOGS, false, resolve_recently_found_dialogs_multipromise_.get_promise()); + get_dialogs(FolderId::main(), MIN_DIALOG_DATE, MAX_GET_DIALOGS, false, + resolve_recently_found_dialogs_multipromise_.get_promise()); td_->contacts_manager_->search_contacts("", 1, resolve_recently_found_dialogs_multipromise_.get_promise()); } } @@ -27425,12 +27616,13 @@ void MessagesManager::set_sponsored_dialog_id(DialogId dialog_id) { } } -td_api::object_ptr MessagesManager::get_update_unread_message_count_object() const { - CHECK(is_message_unread_count_inited_); - int32 unread_count = unread_message_total_count_; - int32 unread_unmuted_count = unread_message_total_count_ - unread_message_muted_count_; +td_api::object_ptr MessagesManager::get_update_unread_message_count_object( + FolderId folder_id, const DialogList &list) const { + CHECK(list.is_message_unread_count_inited_); + int32 unread_count = list.unread_message_total_count_; + int32 unread_unmuted_count = list.unread_message_total_count_ - list.unread_message_muted_count_; - if (!include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid()) { + if (!include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid() && folder_id == FolderId::main()) { const Dialog *d = get_dialog(sponsored_dialog_id_); CHECK(d != nullptr); auto sponsored_unread_count = d->server_unread_count + d->local_unread_count; @@ -27450,21 +27642,23 @@ td_api::object_ptr MessagesManager::get_update CHECK(unread_count >= 0); CHECK(unread_unmuted_count >= 0); - return td_api::make_object(unread_count, unread_unmuted_count); + return td_api::make_object(get_chat_list_object(folder_id), unread_count, + unread_unmuted_count); } -td_api::object_ptr MessagesManager::get_update_unread_chat_count_object() const { - CHECK(is_dialog_unread_count_inited_); - int32 unread_count = unread_dialog_total_count_; - int32 unread_unmuted_count = unread_count - unread_dialog_muted_count_; - int32 unread_marked_count = unread_dialog_marked_count_; - int32 unread_unmuted_marked_count = unread_marked_count - unread_dialog_muted_marked_count_; +td_api::object_ptr MessagesManager::get_update_unread_chat_count_object( + FolderId folder_id, const DialogList &list) const { + CHECK(list.is_dialog_unread_count_inited_); + int32 unread_count = list.unread_dialog_total_count_; + int32 unread_unmuted_count = unread_count - list.unread_dialog_muted_count_; + int32 unread_marked_count = list.unread_dialog_marked_count_; + int32 unread_unmuted_marked_count = unread_marked_count - list.unread_dialog_muted_marked_count_; CHECK(unread_count >= 0); CHECK(unread_unmuted_count >= 0); CHECK(unread_marked_count >= 0); CHECK(unread_unmuted_marked_count >= 0); - if (!include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid()) { + if (!include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid() && folder_id == FolderId::main()) { const Dialog *d = get_dialog(sponsored_dialog_id_); CHECK(d != nullptr); auto sponsored_unread_count = d->server_unread_count + d->local_unread_count; @@ -27482,18 +27676,22 @@ td_api::object_ptr MessagesManager::get_update_un } } - return td_api::make_object(unread_count, unread_unmuted_count, unread_marked_count, + return td_api::make_object(get_chat_list_object(folder_id), unread_count, + unread_unmuted_count, unread_marked_count, unread_unmuted_marked_count); } void MessagesManager::get_current_state(vector> &updates) const { if (!td_->auth_manager_->is_bot()) { if (G()->parameters().use_message_db) { - if (is_message_unread_count_inited_) { - updates.push_back(get_update_unread_message_count_object()); - } - if (is_dialog_unread_count_inited_) { - updates.push_back(get_update_unread_chat_count_object()); + for (auto &it : dialog_lists_) { + auto &list = it.second; + if (list.is_message_unread_count_inited_) { + updates.push_back(get_update_unread_message_count_object(it.first, list)); + } + if (list.is_dialog_unread_count_inited_) { + updates.push_back(get_update_unread_chat_count_object(it.first, list)); + } } } diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 061002f7..11923ade 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -259,8 +259,9 @@ class MessagesManager : public Actor { void on_update_message_web_page(FullMessageId full_message_id, bool have_web_page); - void on_get_dialogs(vector> &&dialog_folders, int32 total_count, - vector> &&messages, Promise &&promise); + void on_get_dialogs(FolderId folder_id, vector> &&dialog_folders, + int32 total_count, vector> &&messages, + Promise &&promise); void on_get_common_dialogs(UserId user_id, int32 offset_chat_id, vector> &&chats, int32 total_count); @@ -269,9 +270,9 @@ class MessagesManager : public Actor { void on_update_dialog_draft_message(DialogId dialog_id, tl_object_ptr &&draft_message); - void on_update_dialog_is_pinned(DialogId dialog_id, bool is_pinned); + void on_update_dialog_is_pinned(FolderId folder_id, DialogId dialog_id, bool is_pinned); - void on_update_pinned_dialogs(); + void on_update_pinned_dialogs(FolderId folder_id); void on_update_dialog_is_marked_as_unread(DialogId dialog_id, bool is_marked_as_unread); @@ -464,7 +465,7 @@ class MessagesManager : public Actor { void load_dialogs(vector dialog_ids, Promise &&promise); - vector get_dialogs(DialogDate offset, int32 limit, bool force, Promise &&promise); + vector get_dialogs(FolderId folder_id, DialogDate offset, int32 limit, bool force, Promise &&promise); vector search_public_dialogs(const string &query, Promise &&promise); @@ -521,13 +522,15 @@ class MessagesManager : public Actor { void clear_all_draft_messages(bool exclude_secret_chats, Promise &&promise); + void set_dialog_is_pinned(DialogId dialog_id, bool is_pinned); + Status toggle_dialog_is_pinned(DialogId dialog_id, bool is_pinned) TD_WARN_UNUSED_RESULT; Status toggle_dialog_is_marked_as_unread(DialogId dialog_id, bool is_marked_as_unread) TD_WARN_UNUSED_RESULT; Status toggle_dialog_silent_send_message(DialogId dialog_id, bool silent_send_message) TD_WARN_UNUSED_RESULT; - Status set_pinned_dialogs(vector dialog_ids) TD_WARN_UNUSED_RESULT; + Status set_pinned_dialogs(FolderId folder_id, vector dialog_ids) TD_WARN_UNUSED_RESULT; Status set_dialog_client_data(DialogId dialog_id, string &&client_data) TD_WARN_UNUSED_RESULT; @@ -1125,6 +1128,35 @@ class MessagesManager : public Actor { void parse(ParserT &parser); }; + struct DialogList { + FolderId folder_id; + bool is_message_unread_count_inited_ = false; + bool is_dialog_unread_count_inited_ = false; + bool need_unread_count_recalc_ = true; + int32 unread_message_total_count_ = 0; + int32 unread_message_muted_count_ = 0; + int32 unread_dialog_total_count_ = 0; + int32 unread_dialog_muted_count_ = 0; + int32 unread_dialog_marked_count_ = 0; + int32 unread_dialog_muted_marked_count_ = 0; + + std::set ordered_dialogs_; + std::set ordered_server_dialogs_; + + // date of last dialog in the dialog list + // last_dialog_date_ == min(last_server_dialog_date_, last_secret_chat_dialog_date_) + DialogDate last_dialog_date_ = MIN_DIALOG_DATE; // in memory + + // date of last known user/group/channel dialog in the right order + DialogDate last_server_dialog_date_ = MIN_DIALOG_DATE; + DialogDate last_loaded_database_dialog_date_ = MIN_DIALOG_DATE; + DialogDate last_database_server_dialog_date_ = MIN_DIALOG_DATE; + + MultiPromiseActor load_dialog_list_multipromise_{ + "LoadDialogListMultiPromiseActor"}; // should be defined before pending_on_get_dialogs_ + int32 load_dialog_list_limit_max_ = 0; + }; + class MessagesIteratorBase { vector stack_; @@ -1594,11 +1626,11 @@ class MessagesManager : public Actor { void try_reuse_notification_group(NotificationGroupInfo &group_info); - void load_dialog_list(int32 limit, bool only_local, Promise &&promise); + void load_dialog_list(FolderId folder_id, int32 limit, bool only_local, Promise &&promise); - void load_dialog_list_from_database(int32 limit, Promise &&promise); + void load_dialog_list_from_database(FolderId folder_id, int32 limit, Promise &&promise); - static void preload_dialog_list(void *messages_manager_void); + void preload_dialog_list(FolderId folderId); void update_message_count_by_index(Dialog *d, int diff, const Message *m); @@ -1731,9 +1763,9 @@ class MessagesManager : public Actor { void send_update_chat_last_message_impl(const Dialog *d, const char *source) const; - void send_update_unread_message_count(DialogId dialog_id, bool force, const char *source); + void send_update_unread_message_count(FolderId folder_id, DialogId dialog_id, bool force, const char *source); - void send_update_unread_chat_count(DialogId dialog_id, bool force, const char *source); + void send_update_unread_chat_count(FolderId folder_id, DialogId dialog_id, bool force, const char *source); void send_update_chat_read_inbox(const Dialog *d, bool force, const char *source); @@ -1745,7 +1777,7 @@ class MessagesManager : public Actor { void send_update_chat_online_member_count(DialogId dialog_id, int32 online_member_count) const; - void send_update_chat_list_type(const Dialog *d) const; + void send_update_chat_chat_list(const Dialog *d) const; tl_object_ptr get_message_object(DialogId dialog_id, const Message *m, bool for_event_log = false) const; @@ -1759,11 +1791,13 @@ class MessagesManager : public Actor { static bool need_unread_counter(int64 dialog_order); - void recalc_unread_count(); + void recalc_unread_count(FolderId folder_id); - td_api::object_ptr get_update_unread_message_count_object() const; + td_api::object_ptr get_update_unread_message_count_object( + FolderId folder_id, const DialogList &list) const; - td_api::object_ptr get_update_unread_chat_count_object() const; + td_api::object_ptr get_update_unread_chat_count_object(FolderId folder_id, + const DialogList &list) const; void set_dialog_last_read_inbox_message_id(Dialog *d, MessageId message_id, int32 server_unread_count, int32 local_unread_count, bool force_update, const char *source); @@ -1784,12 +1818,10 @@ class MessagesManager : public Actor { void set_dialog_is_empty(Dialog *d, const char *source); - static int32 get_pinned_dialogs_limit(); + static int32 get_pinned_dialogs_limit(FolderId folder_id); static vector remove_secret_chat_dialog_ids(vector dialog_ids); - void set_dialog_is_pinned(DialogId dialog_id, bool is_pinned); - void set_dialog_is_pinned(Dialog *d, bool is_pinned); void set_dialog_is_marked_as_unread(Dialog *d, bool is_marked_as_unread); @@ -1802,7 +1834,7 @@ class MessagesManager : public Actor { void toggle_dialog_is_marked_as_unread_on_server(DialogId dialog_id, bool is_marked_as_unread, uint64 logevent_id); - void reorder_pinned_dialogs_on_server(const vector &dialog_ids, uint64 logevent_id); + void reorder_pinned_dialogs_on_server(FolderId folder_id, const vector &dialog_ids, uint64 logevent_id); void set_dialog_reply_markup(Dialog *d, MessageId message_id); @@ -1864,7 +1896,9 @@ class MessagesManager : public Actor { td_api::object_ptr get_chat_type_object(DialogId dialog_id) const; - static td_api::object_ptr get_chat_list_type_object(const Dialog *d); + static td_api::object_ptr get_chat_list_object(const Dialog *d); + + static td_api::object_ptr get_chat_list_object(FolderId folder_id); td_api::object_ptr get_chat_object(const Dialog *d) const; @@ -1878,19 +1912,23 @@ class MessagesManager : public Actor { Dialog *on_load_dialog_from_database(DialogId dialog_id, const BufferSlice &value); - void on_get_dialogs_from_database(int32 limit, vector &&dialogs, Promise &&promise); + void on_get_dialogs_from_database(FolderId folder_id, int32 limit, vector &&dialogs, + Promise &&promise); void send_get_dialog_query(DialogId dialog_id, Promise &&promise, uint64 logevent_id = 0); void send_search_public_dialogs_query(const string &query, Promise &&promise); - vector get_pinned_dialogs() const; + vector get_pinned_dialogs(FolderId folder_id) const; - void reload_pinned_dialogs(Promise &&promise); + void reload_pinned_dialogs(FolderId folder_id, Promise &&promise); void update_dialogs_hints(const Dialog *d); void update_dialogs_hints_rating(const Dialog *d); + DialogList &get_dialog_list(FolderId folder_id); + const DialogList *get_dialog_list(FolderId folder_id) const; + std::pair> search_private_chat_participants(UserId my_user_id, UserId peer_user_id, const string &query, int32 limit, DialogParticipantsFilter filter) const; @@ -2052,7 +2090,7 @@ class MessagesManager : public Actor { bool set_dialog_order(Dialog *d, int64 new_order, bool need_send_update_chat_order, bool is_loaded_from_database, const char *source); - void update_last_dialog_date(); + void update_last_dialog_date(FolderId folder_id); void load_notification_settings(); @@ -2111,6 +2149,8 @@ class MessagesManager : public Actor { static void on_update_dialog_online_member_count_timeout_callback(void *messages_manager_ptr, int64 dialog_id_int); + static void on_preload_dialog_list_timeout_callback(void *messages_manager_ptr, int64 folder_id_int); + void load_secret_thumbnail(FileId thumbnail_file_id); static tl_object_ptr get_channel_admin_log_events_filter( @@ -2187,7 +2227,7 @@ class MessagesManager : public Actor { uint64 save_toggle_dialog_is_pinned_on_server_logevent(DialogId dialog_id, bool is_pinned); - uint64 save_reorder_pinned_dialogs_on_server_logevent(const vector &dialog_ids); + uint64 save_reorder_pinned_dialogs_on_server_logevent(FolderId folder_id, const vector &dialog_ids); uint64 save_toggle_dialog_is_marked_as_unread_on_server_logevent(DialogId dialog_id, bool is_marked_as_unread); @@ -2393,39 +2433,16 @@ class MessagesManager : public Actor { std::unordered_map notification_group_id_to_dialog_id_; + uint64 current_message_edit_generation_ = 0; + bool include_sponsored_dialog_to_unread_count_ = false; - bool have_postponed_unread_message_count_update_ = false; - bool have_postponed_unread_chat_count_update_ = false; - bool is_message_unread_count_inited_ = false; - bool is_dialog_unread_count_inited_ = false; - bool need_unread_count_recalc_ = true; - int32 unread_message_total_count_ = 0; - int32 unread_message_muted_count_ = 0; - int32 unread_dialog_total_count_ = 0; - int32 unread_dialog_muted_count_ = 0; - int32 unread_dialog_marked_count_ = 0; - int32 unread_dialog_muted_marked_count_ = 0; + + std::unordered_set postponed_unread_message_count_updates_; + std::unordered_set postponed_unread_chat_count_updates_; int64 current_pinned_dialog_order_ = DEFAULT_ORDER; - uint64 current_message_edit_generation_ = 0; - - std::set ordered_dialogs_; - std::set ordered_server_dialogs_; - - // date of last dialog in the dialog list - // last_dialog_date_ == min(last_server_dialog_date_, last_secret_chat_dialog_date_) - DialogDate last_dialog_date_ = MIN_DIALOG_DATE; // in memory - - // date of last known user/group/channel dialog in the right order - DialogDate last_server_dialog_date_ = MIN_DIALOG_DATE; - DialogDate last_loaded_database_dialog_date_ = MIN_DIALOG_DATE; - DialogDate last_database_server_dialog_date_ = MIN_DIALOG_DATE; - - MultiPromiseActor load_dialog_list_multipromise_{ - "LoadDialogListMultiPromiseActor"}; // should be defined before pending_on_get_dialogs_ - int32 load_dialog_list_limit_max_ = 0; - Timeout preload_dialog_list_timeout_; + std::unordered_map dialog_lists_; std::unordered_map active_get_channel_differencies_; std::unordered_map get_channel_difference_to_logevent_id_; @@ -2441,6 +2458,7 @@ class MessagesManager : public Actor { MultiTimeout pending_send_dialog_action_timeout_{"PendingSendDialogActionTimeout"}; MultiTimeout active_dialog_action_timeout_{"ActiveDialogActionTimeout"}; MultiTimeout update_dialog_online_member_count_timeout_{"UpdateDialogOnlineMemberCountTimeout"}; + MultiTimeout preload_dialog_list_timeout_{"PreloadDialogListTimeout"}; Hints dialogs_hints_; // search dialogs by title and username @@ -2457,6 +2475,7 @@ class MessagesManager : public Actor { std::unordered_map inaccessible_resolved_usernames_; struct PendingOnGetDialogs { + FolderId folder_id; vector> dialogs; int32 total_count; vector> messages; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index da88c3a5..be799316 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -32,6 +32,7 @@ #include "td/telegram/files/FileManager.h" #include "td/telegram/files/FileSourceId.h" #include "td/telegram/files/FileType.h" +#include "td/telegram/FolderId.h" #include "td/telegram/Global.h" #include "td/telegram/HashtagHints.h" #include "td/telegram/InlineQueriesManager.h" @@ -898,13 +899,14 @@ class GetChatRequest : public RequestActor<> { }; class GetChatsRequest : public RequestActor<> { + FolderId folder_id_; DialogDate offset_; int32 limit_; vector dialog_ids_; void do_run(Promise &&promise) override { - dialog_ids_ = td->messages_manager_->get_dialogs(offset_, limit_, get_tries() < 2, std::move(promise)); + dialog_ids_ = td->messages_manager_->get_dialogs(folder_id_, offset_, limit_, get_tries() < 2, std::move(promise)); } void do_send_result() override { @@ -912,8 +914,12 @@ class GetChatsRequest : public RequestActor<> { } public: - GetChatsRequest(ActorShared td, uint64 request_id, int64 offset_order, int64 offset_dialog_id, int32 limit) - : RequestActor(std::move(td), request_id), offset_(offset_order, DialogId(offset_dialog_id)), limit_(limit) { + GetChatsRequest(ActorShared td, uint64 request_id, FolderId folder_id, int64 offset_order, int64 offset_dialog_id, + int32 limit) + : RequestActor(std::move(td), request_id) + , folder_id_(folder_id) + , offset_(offset_order, DialogId(offset_dialog_id)) + , limit_(limit) { // 1 for database + 1 for server request + 1 for server request at the end + 1 for return + 1 just in case set_tries(5); } @@ -5459,7 +5465,8 @@ void Td::on_request(uint64 id, const td_api::removeTopChat &request) { void Td::on_request(uint64 id, const td_api::getChats &request) { CHECK_IS_USER(); - CREATE_REQUEST(GetChatsRequest, request.offset_order_, request.offset_chat_id_, request.limit_); + CREATE_REQUEST(GetChatsRequest, FolderId(request.chat_list_), request.offset_order_, request.offset_chat_id_, + request.limit_); } void Td::on_request(uint64 id, td_api::searchPublicChat &request) { @@ -6010,6 +6017,7 @@ void Td::on_request(uint64 id, const td_api::toggleChatDefaultDisableNotificatio void Td::on_request(uint64 id, const td_api::setPinnedChats &request) { CHECK_IS_USER(); answer_ok_query(id, messages_manager_->set_pinned_dialogs( + FolderId(request.chat_list_), transform(request.chat_ids_, [](int64 chat_id) { return DialogId(chat_id); }))); } diff --git a/td/telegram/TdDb.cpp b/td/telegram/TdDb.cpp index 3c60237b..afa2c782 100644 --- a/td/telegram/TdDb.cpp +++ b/td/telegram/TdDb.cpp @@ -342,9 +342,9 @@ Status TdDb::init_sqlite(int32 scheduler_id, const TdParameters ¶meters, DbK } if (dialog_db_was_created) { - binlog_pmc.erase("unread_message_count"); - binlog_pmc.erase("unread_dialog_count"); - binlog_pmc.erase("last_server_dialog_date"); + binlog_pmc.erase_by_prefix("last_server_dialog_date"); + binlog_pmc.erase_by_prefix("unread_message_count"); + binlog_pmc.erase_by_prefix("unread_dialog_count"); binlog_pmc.erase("promoted_dialog_id"); binlog_pmc.erase("sponsored_dialog_id"); binlog_pmc.erase_by_prefix("top_dialogs"); diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 4f60976f..dc6f648b 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -1724,12 +1724,14 @@ void UpdatesManager::on_update(tl_object_ptr u } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { + FolderId folder_id(update->flags_ & telegram_api::updateDialogPinned::FOLDER_ID_MASK ? update->folder_id_ : 0); td_->messages_manager_->on_update_dialog_is_pinned( - DialogId(update->peer_), (update->flags_ & telegram_api::updateDialogPinned::PINNED_MASK) != 0); + folder_id, DialogId(update->peer_), (update->flags_ & telegram_api::updateDialogPinned::PINNED_MASK) != 0); } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { - td_->messages_manager_->on_update_pinned_dialogs(); // TODO use update->order_ + FolderId folder_id(update->flags_ & telegram_api::updatePinnedDialogs::FOLDER_ID_MASK ? update->folder_id_ : 0); + td_->messages_manager_->on_update_pinned_dialogs(folder_id); // TODO use update->order_ } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { diff --git a/td/telegram/Version.h b/td/telegram/Version.h index 915b8c40..8997f053 100644 --- a/td/telegram/Version.h +++ b/td/telegram/Version.h @@ -34,6 +34,7 @@ enum class Version : int32 { SupportMinithumbnails, AddVideoCallsSupport, AddPhotoSizeSource, + SupportFolders, Next }; diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 82f0f304..99da366d 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -401,7 +401,7 @@ class CliClient final : public Actor { case td_api::authorizationStateWaitTdlibParameters::ID: { auto parameters = td_api::make_object(); parameters->use_test_dc_ = use_test_dc_; - parameters->use_message_database_ = false; + parameters->use_message_database_ = true; parameters->use_chat_info_database_ = true; parameters->use_secret_chats_ = true; parameters->api_id_ = api_id_; @@ -861,7 +861,7 @@ class CliClient final : public Actor { Scheduler::subscribe(stdin_.get_poll_info().extract_pollable_fd(this), PollFlags::Read()); if (get_chat_list_) { - send_request(td_api::make_object(std::numeric_limits::max(), 0, 100)); + send_request(td_api::make_object(nullptr, std::numeric_limits::max(), 0, 100)); } if (disable_network_) { send_request(td_api::make_object(td_api::make_object())); @@ -1602,7 +1602,7 @@ class CliClient final : public Actor { op_not_found_count++; } - if (op == "gc" || op == "GetChats") { + if (op == "gc" || op == "GetChats" || op == "gca") { string offset_order_string; string offset_chat_id; string limit; @@ -1610,6 +1610,10 @@ class CliClient final : public Actor { std::tie(limit, args) = split(args); std::tie(offset_order_string, offset_chat_id) = split(args); + td_api::object_ptr chat_list; + if (op == "gca") { + chat_list = td_api::make_object(); + } if (limit.empty()) { limit = "10000"; } @@ -1619,12 +1623,12 @@ class CliClient final : public Actor { } else { offset_order = to_integer(offset_order_string); } - send_request( - td_api::make_object(offset_order, as_chat_id(offset_chat_id), to_integer(limit))); + send_request(td_api::make_object(std::move(chat_list), offset_order, as_chat_id(offset_chat_id), + to_integer(limit))); } else if (op == "gctest") { - send_request(td_api::make_object(std::numeric_limits::max(), 0, 1)); - send_request(td_api::make_object(std::numeric_limits::max(), 0, 10)); - send_request(td_api::make_object(std::numeric_limits::max(), 0, 5)); + send_request(td_api::make_object(nullptr, std::numeric_limits::max(), 0, 1)); + send_request(td_api::make_object(nullptr, std::numeric_limits::max(), 0, 10)); + send_request(td_api::make_object(nullptr, std::numeric_limits::max(), 0, 5)); } else if (op == "gcc" || op == "GetCommonChats") { string user_id; string offset_chat_id; @@ -2648,13 +2652,17 @@ class CliClient final : public Actor { std::tie(chat_id, default_disable_notification) = split(args); send_request(td_api::make_object( as_chat_id(chat_id), as_bool(default_disable_notification))); - } else if (op == "spchats") { + } else if (op == "spchats" || op == "spchatsa") { vector chat_ids_str = full_split(args, ' '); vector chat_ids; for (auto &chat_id_str : chat_ids_str) { chat_ids.push_back(as_chat_id(chat_id_str)); } - send_request(td_api::make_object(std::move(chat_ids))); + td_api::object_ptr chat_list = td_api::make_object(); + if (op == "spchatsa") { + chat_list = td_api::make_object(); + } + send_request(td_api::make_object(std::move(chat_list), std::move(chat_ids))); } else if (op == "sca") { string chat_id; string action; diff --git a/tdnet/td/net/TcpListener.cpp b/tdnet/td/net/TcpListener.cpp index bca52b58..176b2eb4 100644 --- a/tdnet/td/net/TcpListener.cpp +++ b/tdnet/td/net/TcpListener.cpp @@ -30,7 +30,6 @@ void TcpListener::start_up() { } void TcpListener::tear_down() { - LOG(ERROR) << "TcpListener closed"; if (!server_fd_.empty()) { Scheduler::unsubscribe_before_close(server_fd_.get_poll_info().get_pollable_fd_ref()); server_fd_.close(); @@ -53,7 +52,6 @@ void TcpListener::loop() { } if (can_close(server_fd_)) { - LOG(ERROR) << "HELLO!"; stop(); } }