history.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #pragma once
  8. #include "data/data_types.h"
  9. #include "data/data_peer.h"
  10. #include "data/data_drafts.h"
  11. #include "data/data_thread.h"
  12. #include "history/view/history_view_send_action.h"
  13. #include "base/variant.h"
  14. #include "base/flat_set.h"
  15. #include "base/flags.h"
  16. class History;
  17. class HistoryBlock;
  18. class HistoryTranslation;
  19. class HistoryItem;
  20. struct HistoryItemCommonFields;
  21. struct HistoryMessageMarkupData;
  22. class HistoryMainElementDelegateMixin;
  23. struct LanguageId;
  24. namespace Data {
  25. struct Draft;
  26. class Session;
  27. class Folder;
  28. class ChatFilter;
  29. struct SponsoredFrom;
  30. class SponsoredMessages;
  31. class HistoryMessages;
  32. } // namespace Data
  33. namespace Dialogs {
  34. class Row;
  35. class IndexedList;
  36. } // namespace Dialogs
  37. namespace HistoryView {
  38. class Element;
  39. } // namespace HistoryView
  40. enum class NewMessageType {
  41. Unread,
  42. Last,
  43. Existing,
  44. };
  45. class History final : public Data::Thread {
  46. public:
  47. using Element = HistoryView::Element;
  48. History(not_null<Data::Session*> owner, PeerId peerId);
  49. ~History();
  50. [[nodiscard]] not_null<History*> owningHistory() override {
  51. return this;
  52. }
  53. [[nodiscard]] Data::Thread *threadFor(MsgId topicRootId);
  54. [[nodiscard]] const Data::Thread *threadFor(MsgId topicRootId) const;
  55. [[nodiscard]] auto delegateMixin() const
  56. -> not_null<HistoryMainElementDelegateMixin*> {
  57. return _delegateMixin.get();
  58. }
  59. void forumChanged(Data::Forum *old);
  60. [[nodiscard]] bool isForum() const;
  61. [[nodiscard]] not_null<History*> migrateToOrMe() const;
  62. [[nodiscard]] History *migrateFrom() const;
  63. [[nodiscard]] MsgRange rangeForDifferenceRequest() const;
  64. [[nodiscard]] Data::HistoryMessages &messages();
  65. [[nodiscard]] const Data::HistoryMessages &messages() const;
  66. [[nodiscard]] Data::HistoryMessages *maybeMessages();
  67. [[nodiscard]] HistoryItem *joinedMessageInstance() const;
  68. void checkLocalMessages();
  69. void removeJoinedMessage();
  70. void removeNewPeerMessages();
  71. void reactionsEnabledChanged(bool enabled);
  72. [[nodiscard]] bool isEmpty() const;
  73. [[nodiscard]] bool isDisplayedEmpty() const;
  74. [[nodiscard]] Element *findFirstNonEmpty() const;
  75. [[nodiscard]] Element *findFirstDisplayed() const;
  76. [[nodiscard]] Element *findLastNonEmpty() const;
  77. [[nodiscard]] Element *findLastDisplayed() const;
  78. [[nodiscard]] bool hasOrphanMediaGroupPart() const;
  79. [[nodiscard]] std::vector<MsgId> collectMessagesFromParticipantToDelete(
  80. not_null<PeerData*> participant) const;
  81. enum class ClearType {
  82. Unload,
  83. DeleteChat,
  84. ClearHistory,
  85. };
  86. void clear(ClearType type);
  87. void clearUpTill(MsgId availableMinId);
  88. void applyGroupAdminChanges(const base::flat_set<UserId> &changes);
  89. template <typename ...Args>
  90. not_null<HistoryItem*> makeMessage(MsgId id, Args &&...args) {
  91. return static_cast<HistoryItem*>(
  92. insertItem(
  93. std::make_unique<HistoryItem>(
  94. this,
  95. id,
  96. std::forward<Args>(args)...)).get());
  97. }
  98. template <typename ...Args>
  99. not_null<HistoryItem*> makeMessage(
  100. HistoryItemCommonFields &&fields,
  101. Args &&...args) {
  102. return static_cast<HistoryItem*>(
  103. insertItem(
  104. std::make_unique<HistoryItem>(
  105. this,
  106. std::move(fields),
  107. std::forward<Args>(args)...)).get());
  108. }
  109. void destroyMessage(not_null<HistoryItem*> item);
  110. void destroyMessagesByDates(TimeId minDate, TimeId maxDate);
  111. void destroyMessagesByTopic(MsgId topicRootId);
  112. void unpinMessagesFor(MsgId topicRootId);
  113. not_null<HistoryItem*> addNewMessage(
  114. MsgId id,
  115. const MTPMessage &message,
  116. MessageFlags localFlags,
  117. NewMessageType type);
  118. not_null<HistoryItem*> addNewLocalMessage(
  119. HistoryItemCommonFields &&fields,
  120. const TextWithEntities &text,
  121. const MTPMessageMedia &media);
  122. not_null<HistoryItem*> addNewLocalMessage(
  123. HistoryItemCommonFields &&fields,
  124. not_null<HistoryItem*> forwardOriginal);
  125. not_null<HistoryItem*> addNewLocalMessage(
  126. HistoryItemCommonFields &&fields,
  127. not_null<DocumentData*> document,
  128. const TextWithEntities &caption);
  129. not_null<HistoryItem*> addNewLocalMessage(
  130. HistoryItemCommonFields &&fields,
  131. not_null<PhotoData*> photo,
  132. const TextWithEntities &caption);
  133. not_null<HistoryItem*> addNewLocalMessage(
  134. HistoryItemCommonFields &&fields,
  135. not_null<GameData*> game);
  136. not_null<HistoryItem*> addNewLocalMessage(not_null<HistoryItem*> item);
  137. not_null<HistoryItem*> addSponsoredMessage(
  138. MsgId id,
  139. Data::SponsoredFrom from,
  140. const TextWithEntities &textWithEntities); // sponsored
  141. // Used only internally and for channel admin log.
  142. not_null<HistoryItem*> createItem(
  143. MsgId id,
  144. const MTPMessage &message,
  145. MessageFlags localFlags,
  146. bool detachExistingItem = false,
  147. bool newMessage = false);
  148. std::vector<not_null<HistoryItem*>> createItems(
  149. const QVector<MTPMessage> &data);
  150. void addOlderSlice(const QVector<MTPMessage> &slice);
  151. void addNewerSlice(const QVector<MTPMessage> &slice);
  152. void newItemAdded(not_null<HistoryItem*> item);
  153. void registerClientSideMessage(not_null<HistoryItem*> item);
  154. void unregisterClientSideMessage(not_null<HistoryItem*> item);
  155. [[nodiscard]] auto clientSideMessages()
  156. -> const base::flat_set<not_null<HistoryItem*>> &;
  157. [[nodiscard]] HistoryItem *latestSendingMessage() const;
  158. [[nodiscard]] bool readInboxTillNeedsRequest(MsgId tillId);
  159. void applyInboxReadUpdate(
  160. FolderId folderId,
  161. MsgId upTo,
  162. int stillUnread,
  163. int32 channelPts = 0);
  164. void inboxRead(MsgId upTo, std::optional<int> stillUnread = {});
  165. void inboxRead(not_null<const HistoryItem*> wasRead);
  166. void outboxRead(MsgId upTo);
  167. void outboxRead(not_null<const HistoryItem*> wasRead);
  168. [[nodiscard]] MsgId loadAroundId() const;
  169. [[nodiscard]] bool inboxReadTillKnown() const;
  170. [[nodiscard]] MsgId inboxReadTillId() const;
  171. [[nodiscard]] MsgId outboxReadTillId() const;
  172. [[nodiscard]] bool isServerSideUnread(
  173. not_null<const HistoryItem*> item) const override;
  174. [[nodiscard]] bool trackUnreadMessages() const;
  175. [[nodiscard]] int unreadCount() const;
  176. [[nodiscard]] bool unreadCountKnown() const;
  177. // Some old unread count is known, but we read history till some place.
  178. [[nodiscard]] bool unreadCountRefreshNeeded(MsgId readTillId) const;
  179. void setUnreadCount(int newUnreadCount);
  180. void setUnreadMark(bool unread);
  181. void setFakeUnreadWhileOpened(bool enabled);
  182. [[nodiscard]] bool fakeUnreadWhileOpened() const;
  183. void setMuted(bool muted) override;
  184. void addUnreadBar();
  185. void destroyUnreadBar();
  186. [[nodiscard]] Element *unreadBar() const;
  187. void calculateFirstUnreadMessage();
  188. void unsetFirstUnreadMessage();
  189. [[nodiscard]] Element *firstUnreadMessage() const;
  190. [[nodiscard]] bool loadedAtBottom() const; // last message is in the list
  191. void setNotLoadedAtBottom();
  192. [[nodiscard]] bool loadedAtTop() const; // nothing was added after loading history back
  193. [[nodiscard]] bool isReadyFor(MsgId msgId); // has messages for showing history at msgId
  194. void getReadyFor(MsgId msgId);
  195. [[nodiscard]] HistoryItem *lastMessage() const;
  196. [[nodiscard]] HistoryItem *lastServerMessage() const;
  197. [[nodiscard]] bool lastMessageKnown() const;
  198. [[nodiscard]] bool lastServerMessageKnown() const;
  199. void unknownMessageDeleted(MsgId messageId);
  200. void applyDialogTopMessage(MsgId topMessageId);
  201. void applyDialog(Data::Folder *requestFolder, const MTPDdialog &data);
  202. void applyPinnedUpdate(const MTPDupdateDialogPinned &data);
  203. void applyDialogFields(
  204. Data::Folder *folder,
  205. int unreadCount,
  206. MsgId maxInboxRead,
  207. MsgId maxOutboxRead);
  208. void dialogEntryApplied();
  209. void cacheTopPromotion(
  210. bool promoted,
  211. const QString &type,
  212. const QString &message);
  213. [[nodiscard]] QStringView topPromotionType() const;
  214. [[nodiscard]] QString topPromotionMessage() const;
  215. [[nodiscard]] bool topPromotionAboutShown() const;
  216. void markTopPromotionAboutShown();
  217. MsgId minMsgId() const;
  218. MsgId maxMsgId() const;
  219. MsgId msgIdForRead() const;
  220. HistoryItem *lastEditableMessage() const;
  221. void resizeToWidth(int newWidth);
  222. void forceFullResize();
  223. int height() const;
  224. void itemRemoved(not_null<HistoryItem*> item);
  225. void itemVanished(not_null<HistoryItem*> item);
  226. bool hasPendingResizedItems() const;
  227. void setHasPendingResizedItems();
  228. [[nodiscard]] auto sendActionPainter()
  229. -> not_null<HistoryView::SendActionPainter*> override {
  230. return &_sendActionPainter;
  231. }
  232. void clearLastKeyboard();
  233. void clearUnreadMentionsFor(MsgId topicRootId);
  234. void clearUnreadReactionsFor(MsgId topicRootId);
  235. Data::Draft *draft(Data::DraftKey key) const;
  236. void setDraft(Data::DraftKey key, std::unique_ptr<Data::Draft> &&draft);
  237. void clearDraft(Data::DraftKey key);
  238. [[nodiscard]] const Data::HistoryDrafts &draftsMap() const;
  239. void setDraftsMap(Data::HistoryDrafts &&map);
  240. Data::Draft *localDraft(MsgId topicRootId) const {
  241. return draft(Data::DraftKey::Local(topicRootId));
  242. }
  243. Data::Draft *localEditDraft(MsgId topicRootId) const {
  244. return draft(Data::DraftKey::LocalEdit(topicRootId));
  245. }
  246. Data::Draft *cloudDraft(MsgId topicRootId) const {
  247. return draft(Data::DraftKey::Cloud(topicRootId));
  248. }
  249. void setLocalDraft(std::unique_ptr<Data::Draft> &&draft) {
  250. setDraft(
  251. Data::DraftKey::Local(draft->reply.topicRootId),
  252. std::move(draft));
  253. }
  254. void setLocalEditDraft(std::unique_ptr<Data::Draft> &&draft) {
  255. setDraft(
  256. Data::DraftKey::LocalEdit(draft->reply.topicRootId),
  257. std::move(draft));
  258. }
  259. void setCloudDraft(std::unique_ptr<Data::Draft> &&draft) {
  260. setDraft(
  261. Data::DraftKey::Cloud(draft->reply.topicRootId),
  262. std::move(draft));
  263. }
  264. void clearLocalDraft(MsgId topicRootId) {
  265. clearDraft(Data::DraftKey::Local(topicRootId));
  266. }
  267. void clearCloudDraft(MsgId topicRootId) {
  268. clearDraft(Data::DraftKey::Cloud(topicRootId));
  269. }
  270. void clearLocalEditDraft(MsgId topicRootId) {
  271. clearDraft(Data::DraftKey::LocalEdit(topicRootId));
  272. }
  273. void clearDrafts();
  274. Data::Draft *createCloudDraft(
  275. MsgId topicRootId,
  276. const Data::Draft *fromDraft);
  277. [[nodiscard]] bool skipCloudDraftUpdate(
  278. MsgId topicRootId,
  279. TimeId date) const;
  280. void startSavingCloudDraft(MsgId topicRootId);
  281. void finishSavingCloudDraft(MsgId topicRootId, TimeId savedAt);
  282. void takeLocalDraft(not_null<History*> from);
  283. void applyCloudDraft(MsgId topicRootId);
  284. void draftSavedToCloud(MsgId topicRootId);
  285. void requestChatListMessage();
  286. [[nodiscard]] const Data::ForwardDraft &forwardDraft(
  287. MsgId topicRootId) const;
  288. [[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft(
  289. const Data::ForwardDraft &draft) const;
  290. [[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft(
  291. MsgId topicRootId);
  292. void setForwardDraft(MsgId topicRootId, Data::ForwardDraft &&draft);
  293. History *migrateSibling() const;
  294. [[nodiscard]] bool useTopPromotion() const;
  295. int fixedOnTopIndex() const override;
  296. void updateChatListExistence() override;
  297. bool shouldBeInChatList() const override;
  298. Dialogs::UnreadState chatListUnreadState() const override;
  299. Dialogs::BadgesState chatListBadgesState() const override;
  300. HistoryItem *chatListMessage() const override;
  301. bool chatListMessageKnown() const override;
  302. const QString &chatListName() const override;
  303. const QString &chatListNameSortKey() const override;
  304. int chatListNameVersion() const override;
  305. const base::flat_set<QString> &chatListNameWords() const override;
  306. const base::flat_set<QChar> &chatListFirstLetters() const override;
  307. void chatListPreloadData() override;
  308. void paintUserpic(
  309. Painter &p,
  310. Ui::PeerUserpicView &view,
  311. const Dialogs::Ui::PaintContext &context) const override;
  312. void refreshChatListNameSortKey();
  313. void setFakeChatListMessageFrom(const MTPmessages_Messages &data);
  314. void checkChatListMessageRemoved(not_null<HistoryItem*> item);
  315. void applyChatListGroup(
  316. PeerId dataPeerId,
  317. const MTPmessages_Messages &data);
  318. void forgetScrollState() {
  319. scrollTopItem = nullptr;
  320. }
  321. // find the correct scrollTopItem and scrollTopOffset using given top
  322. // of the displayed window relative to the history start coordinate
  323. void countScrollState(int top);
  324. [[nodiscard]] std::pair<Element*, int> findItemAndOffset(int top) const;
  325. [[nodiscard]] MsgId nextNonHistoryEntryId();
  326. bool folderKnown() const override;
  327. Data::Folder *folder() const override;
  328. void setFolder(
  329. not_null<Data::Folder*> folder,
  330. HistoryItem *folderDialogItem = nullptr);
  331. void clearFolder();
  332. // Interface for Data::Histories.
  333. void setInboxReadTill(MsgId upTo);
  334. std::optional<int> countStillUnreadLocal(MsgId readTillId) const;
  335. [[nodiscard]] bool isTopPromoted() const;
  336. void translateOfferFrom(LanguageId id);
  337. [[nodiscard]] LanguageId translateOfferedFrom() const;
  338. void translateTo(LanguageId id);
  339. [[nodiscard]] LanguageId translatedTo() const;
  340. [[nodiscard]] HistoryTranslation *translation() const;
  341. const not_null<PeerData*> peer;
  342. // Still public data.
  343. std::deque<std::unique_ptr<HistoryBlock>> blocks;
  344. // we save the last showAtMsgId to restore the state when switching
  345. // between different conversation histories
  346. MsgId showAtMsgId = ShowAtUnreadMsgId;
  347. // we save a pointer of the history item at the top of the displayed window
  348. // together with an offset from the window top to the top of this message
  349. // resulting scrollTop = top(scrollTopItem) + scrollTopOffset
  350. Element *scrollTopItem = nullptr;
  351. int scrollTopOffset = 0;
  352. bool lastKeyboardInited = false;
  353. bool lastKeyboardUsed = false;
  354. MsgId lastKeyboardId = 0;
  355. MsgId lastKeyboardHiddenId = 0;
  356. PeerId lastKeyboardFrom = 0;
  357. mtpRequestId sendRequestId = 0;
  358. private:
  359. friend class HistoryBlock;
  360. enum class Flag : uchar {
  361. HasPendingResizedItems = (1 << 0),
  362. PendingAllItemsResize = (1 << 1),
  363. IsTopPromoted = (1 << 2),
  364. IsForum = (1 << 3),
  365. FakeUnreadWhileOpened = (1 << 4),
  366. HasPinnedMessages = (1 << 5),
  367. ResolveChatListMessage = (1 << 6),
  368. };
  369. using Flags = base::flags<Flag>;
  370. friend inline constexpr auto is_flag_type(Flag) {
  371. return true;
  372. };
  373. void cacheTopPromoted(bool promoted);
  374. // when this item is destroyed scrollTopItem just points to the next one
  375. // and scrollTopOffset remains the same
  376. // if we are at the bottom of the window scrollTopItem == nullptr and
  377. // scrollTopOffset is undefined
  378. void getNextScrollTopItem(HistoryBlock *block, int32 i);
  379. // helper method for countScrollState(int top)
  380. [[nodiscard]] Element *findScrollTopItem(int top) const;
  381. // this method just removes a block from the blocks list
  382. // when the last item from this block was detached and
  383. // calls the required previousItemChanged()
  384. void removeBlock(not_null<HistoryBlock*> block);
  385. void clearSharedMedia();
  386. not_null<HistoryItem*> insertItem(std::unique_ptr<HistoryItem> item);
  387. not_null<HistoryItem*> addNewItem(
  388. not_null<HistoryItem*> item,
  389. bool unread);
  390. not_null<HistoryItem*> addNewToBack(
  391. not_null<HistoryItem*> item,
  392. bool unread);
  393. friend class Data::SponsoredMessages;
  394. not_null<HistoryItem*> addNewInTheMiddle(
  395. not_null<HistoryItem*> item,
  396. int blockIndex,
  397. int itemIndex);
  398. // All this methods add a new item to the first or last block
  399. // depending on if we are in isBuildingFronBlock() state.
  400. // The last block is created on the go if it is needed.
  401. // Adds the item to the back or front block, depending on
  402. // isBuildingFrontBlock(), creating the block if necessary.
  403. void addItemToBlock(not_null<HistoryItem*> item);
  404. // Usually all new items are added to the last block.
  405. // Only when we scroll up and add a new slice to the
  406. // front we want to create a new front block.
  407. void startBuildingFrontBlock(int expectedItemsCount = 1);
  408. void finishBuildingFrontBlock();
  409. bool isBuildingFrontBlock() const {
  410. return _buildingFrontBlock != nullptr;
  411. }
  412. void addCreatedOlderSlice(
  413. const std::vector<not_null<HistoryItem*>> &items);
  414. void checkForLoadedAtTop(not_null<HistoryItem*> added);
  415. void mainViewRemoved(
  416. not_null<HistoryBlock*> block,
  417. not_null<Element*> view);
  418. TimeId adjustedChatListTimeId() const override;
  419. void changedChatListPinHook() override;
  420. void setOutboxReadTill(MsgId upTo);
  421. void readClientSideMessages();
  422. void applyMessageChanges(
  423. not_null<HistoryItem*> item,
  424. const MTPMessage &original);
  425. void applyServiceChanges(
  426. not_null<HistoryItem*> item,
  427. const MTPDmessageService &data);
  428. // After adding a new history slice check lastMessage / loadedAtBottom.
  429. void checkLastMessage();
  430. void setLastMessage(HistoryItem *item);
  431. void setLastServerMessage(HistoryItem *item);
  432. void refreshChatListMessage();
  433. void setChatListMessage(HistoryItem *item);
  434. std::optional<HistoryItem*> computeChatListMessageFromLast() const;
  435. void setChatListMessageFromLast();
  436. void setChatListMessageUnknown();
  437. void setFakeChatListMessage();
  438. void allowChatListMessageResolve();
  439. void resolveChatListMessageGroup();
  440. // Add all items to the unread mentions if we were not loaded at bottom and now are.
  441. void checkAddAllToUnreadMentions();
  442. void addToSharedMedia(const std::vector<not_null<HistoryItem*>> &items);
  443. void addEdgesToSharedMedia();
  444. void addItemsToLists(const std::vector<not_null<HistoryItem*>> &items);
  445. bool clearUnreadOnClientSide() const;
  446. bool skipUnreadUpdate() const;
  447. HistoryItem *lastAvailableMessage() const;
  448. void getNextFirstUnreadMessage();
  449. bool nonEmptyCountMoreThan(int count) const;
  450. // Creates if necessary a new block for adding item.
  451. // Depending on isBuildingFrontBlock() gets front or back block.
  452. HistoryBlock *prepareBlockForAddingItem();
  453. void viewReplaced(not_null<const Element*> was, Element *now);
  454. void createLocalDraftFromCloud(MsgId topicRootId);
  455. HistoryItem *insertJoinedMessage();
  456. void insertMessageToBlocks(not_null<HistoryItem*> item);
  457. void checkNewPeerMessages();
  458. [[nodiscard]] Dialogs::BadgesState computeBadgesState() const;
  459. [[nodiscard]] Dialogs::BadgesState adjustBadgesStateByFolder(
  460. Dialogs::BadgesState state) const;
  461. [[nodiscard]] Dialogs::UnreadState computeUnreadState() const;
  462. void setFolderPointer(Data::Folder *folder);
  463. void hasUnreadMentionChanged(bool has) override;
  464. void hasUnreadReactionChanged(bool has) override;
  465. const std::unique_ptr<HistoryMainElementDelegateMixin> _delegateMixin;
  466. Flags _flags = 0;
  467. int _width = 0;
  468. int _height = 0;
  469. Element *_unreadBarView = nullptr;
  470. Element *_firstUnreadView = nullptr;
  471. HistoryItem *_joinedMessage = nullptr;
  472. HistoryItem *_newPeerNameChange = nullptr;
  473. HistoryItem *_newPeerPhotoChange = nullptr;
  474. bool _loadedAtTop = false;
  475. bool _loadedAtBottom = true;
  476. std::optional<Data::Folder*> _folder;
  477. std::optional<MsgId> _inboxReadBefore;
  478. std::optional<MsgId> _outboxReadBefore;
  479. std::optional<int> _unreadCount;
  480. std::optional<HistoryItem*> _lastMessage;
  481. std::optional<HistoryItem*> _lastServerMessage;
  482. base::flat_set<not_null<HistoryItem*>> _clientSideMessages;
  483. std::unordered_set<std::unique_ptr<HistoryItem>> _items;
  484. std::unique_ptr<Data::HistoryMessages> _messages;
  485. // This almost always is equal to _lastMessage. The only difference is
  486. // for a group that migrated to a supergroup. Then _lastMessage can
  487. // be a migrate message, but _chatListMessage should be the one before.
  488. std::optional<HistoryItem*> _chatListMessage;
  489. QString _chatListNameSortKey;
  490. // A pointer to the block that is currently being built.
  491. // We hold this pointer so we can destroy it while building
  492. // and then create a new one if it is necessary.
  493. struct BuildingBlock {
  494. int expectedItemsCount = 0; // optimization for block->items.reserve() call
  495. HistoryBlock *block = nullptr;
  496. };
  497. std::unique_ptr<BuildingBlock> _buildingFrontBlock;
  498. std::unique_ptr<HistoryTranslation> _translation;
  499. Data::HistoryDrafts _drafts;
  500. base::flat_map<MsgId, TimeId> _acceptCloudDraftsAfter;
  501. base::flat_map<MsgId, int> _savingCloudDraftRequests;
  502. Data::ForwardDrafts _forwardDrafts;
  503. QString _topPromotedMessage;
  504. QString _topPromotedType;
  505. HistoryView::SendActionPainter _sendActionPainter;
  506. };
  507. class HistoryBlock {
  508. public:
  509. using Element = HistoryView::Element;
  510. enum class ResizeRequest {
  511. ReinitAll = 0,
  512. ResizeAll = 1,
  513. ResizePending = 2,
  514. };
  515. HistoryBlock(not_null<History*> history);
  516. HistoryBlock(const HistoryBlock &) = delete;
  517. HistoryBlock &operator=(const HistoryBlock &) = delete;
  518. ~HistoryBlock();
  519. std::vector<std::unique_ptr<Element>> messages;
  520. void remove(not_null<Element*> view);
  521. void refreshView(not_null<Element*> view);
  522. int resizeGetHeight(int newWidth, ResizeRequest request);
  523. int y() const {
  524. return _y;
  525. }
  526. void setY(int y) {
  527. _y = y;
  528. }
  529. int height() const {
  530. return _height;
  531. }
  532. not_null<History*> history() const {
  533. return _history;
  534. }
  535. HistoryBlock *previousBlock() const {
  536. Expects(_indexInHistory >= 0);
  537. return (_indexInHistory > 0)
  538. ? _history->blocks[_indexInHistory - 1].get()
  539. : nullptr;
  540. }
  541. HistoryBlock *nextBlock() const {
  542. Expects(_indexInHistory >= 0);
  543. return (_indexInHistory + 1 < _history->blocks.size())
  544. ? _history->blocks[_indexInHistory + 1].get()
  545. : nullptr;
  546. }
  547. void setIndexInHistory(int index) {
  548. _indexInHistory = index;
  549. }
  550. int indexInHistory() const {
  551. Expects(_indexInHistory >= 0);
  552. Expects(_indexInHistory < _history->blocks.size());
  553. Expects(_history->blocks[_indexInHistory].get() == this);
  554. return _indexInHistory;
  555. }
  556. protected:
  557. const not_null<History*> _history;
  558. int _y = 0;
  559. int _height = 0;
  560. int _indexInHistory = -1;
  561. };