1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #pragma once
9 
10 #include "data/data_types.h"
11 #include "data/data_peer.h"
12 #include "data/data_drafts.h"
13 #include "dialogs/dialogs_entry.h"
14 #include "dialogs/ui/dialogs_message_view.h"
15 #include "history/view/history_view_send_action.h"
16 #include "base/observer.h"
17 #include "base/timer.h"
18 #include "base/variant.h"
19 #include "base/flat_set.h"
20 #include "base/flags.h"
21 
22 class History;
23 class HistoryBlock;
24 class HistoryItem;
25 class HistoryMessage;
26 class HistoryService;
27 struct HistoryMessageMarkupData;
28 
29 namespace Main {
30 class Session;
31 } // namespace Main
32 
33 namespace Data {
34 struct Draft;
35 class Session;
36 class Folder;
37 class ChatFilter;
38 
39 enum class ForwardOptions {
40 	PreserveInfo,
41 	NoSenderNames,
42 	NoNamesAndCaptions,
43 };
44 
45 struct ForwardDraft {
46 	MessageIdsList ids;
47 	ForwardOptions options = ForwardOptions::PreserveInfo;
48 };
49 
50 struct ResolvedForwardDraft {
51 	HistoryItemsList items;
52 	ForwardOptions options = ForwardOptions::PreserveInfo;
53 };
54 
55 } // namespace Data
56 
57 namespace Dialogs {
58 class Row;
59 class IndexedList;
60 } // namespace Dialogs
61 
62 namespace HistoryView {
63 class Element;
64 } // namespace HistoryView
65 
66 enum class NewMessageType {
67 	Unread,
68 	Last,
69 	Existing,
70 };
71 
72 enum class UnreadMentionType {
73 	New, // when new message is added to history
74 	Existing, // when some messages slice was received
75 };
76 
77 class History final : public Dialogs::Entry {
78 public:
79 	using Element = HistoryView::Element;
80 
81 	History(not_null<Data::Session*> owner, PeerId peerId);
82 	History(const History &) = delete;
83 	History &operator=(const History &) = delete;
84 	~History();
85 
86 	ChannelId channelId() const;
87 	bool isChannel() const;
88 	bool isMegagroup() const;
89 	not_null<History*> migrateToOrMe() const;
90 	History *migrateFrom() const;
91 	MsgRange rangeForDifferenceRequest() const;
92 	void checkLocalMessages();
93 	void removeJoinedMessage();
94 
95 
96 	bool isEmpty() const;
97 	bool isDisplayedEmpty() const;
98 	Element *findFirstNonEmpty() const;
99 	Element *findFirstDisplayed() const;
100 	Element *findLastNonEmpty() const;
101 	Element *findLastDisplayed() const;
102 	bool hasOrphanMediaGroupPart() const;
103 	bool removeOrphanMediaGroupPart();
104 	QVector<MsgId> collectMessagesFromUserToDelete(
105 		not_null<UserData*> user) const;
106 
107 	enum class ClearType {
108 		Unload,
109 		DeleteChat,
110 		ClearHistory,
111 	};
112 	void clear(ClearType type);
113 	void clearUpTill(MsgId availableMinId);
114 
115 	void applyGroupAdminChanges(const base::flat_set<UserId> &changes);
116 
117 	template <typename ...Args>
makeMessage(Args &&...args)118 	not_null<HistoryMessage*> makeMessage(Args &&...args) {
119 		return static_cast<HistoryMessage*>(
120 			insertItem(
121 				std::make_unique<HistoryMessage>(
122 					this,
123 					std::forward<Args>(args)...)).get());
124 	}
125 
126 	template <typename ...Args>
makeServiceMessage(Args &&...args)127 	not_null<HistoryService*> makeServiceMessage(Args &&...args) {
128 		return static_cast<HistoryService*>(
129 			insertItem(
130 				std::make_unique<HistoryService>(
131 					this,
132 					std::forward<Args>(args)...)).get());
133 	}
134 	void destroyMessage(not_null<HistoryItem*> item);
135 
136 	void unpinAllMessages();
137 
138 	not_null<HistoryItem*> addNewMessage(
139 		MsgId id,
140 		const MTPMessage &msg,
141 		MessageFlags localFlags,
142 		NewMessageType type);
143 	not_null<HistoryItem*> addNewLocalMessage(
144 		MsgId id,
145 		MessageFlags flags,
146 		UserId viaBotId,
147 		MsgId replyTo,
148 		TimeId date,
149 		PeerId from,
150 		const QString &postAuthor,
151 		const TextWithEntities &text,
152 		const MTPMessageMedia &media,
153 		HistoryMessageMarkupData &&markup,
154 		uint64 groupedId = 0);
155 	not_null<HistoryItem*> addNewLocalMessage(
156 		MsgId id,
157 		MessageFlags flags,
158 		TimeId date,
159 		PeerId from,
160 		const QString &postAuthor,
161 		not_null<HistoryItem*> forwardOriginal);
162 	not_null<HistoryItem*> addNewLocalMessage(
163 		MsgId id,
164 		MessageFlags flags,
165 		UserId viaBotId,
166 		MsgId replyTo,
167 		TimeId date,
168 		PeerId from,
169 		const QString &postAuthor,
170 		not_null<DocumentData*> document,
171 		const TextWithEntities &caption,
172 		HistoryMessageMarkupData &&markup);
173 	not_null<HistoryItem*> addNewLocalMessage(
174 		MsgId id,
175 		MessageFlags flags,
176 		UserId viaBotId,
177 		MsgId replyTo,
178 		TimeId date,
179 		PeerId from,
180 		const QString &postAuthor,
181 		not_null<PhotoData*> photo,
182 		const TextWithEntities &caption,
183 		HistoryMessageMarkupData &&markup);
184 	not_null<HistoryItem*> addNewLocalMessage(
185 		MsgId id,
186 		MessageFlags flags,
187 		UserId viaBotId,
188 		MsgId replyTo,
189 		TimeId date,
190 		PeerId from,
191 		const QString &postAuthor,
192 		not_null<GameData*> game,
193 		HistoryMessageMarkupData &&markup);
194 
195 	// Used only internally and for channel admin log.
196 	not_null<HistoryItem*> createItem(
197 		MsgId id,
198 		const MTPMessage &message,
199 		MessageFlags localFlags,
200 		bool detachExistingItem);
201 	std::vector<not_null<HistoryItem*>> createItems(
202 		const QVector<MTPMessage> &data);
203 
204 	void addOlderSlice(const QVector<MTPMessage> &slice);
205 	void addNewerSlice(const QVector<MTPMessage> &slice);
206 
207 	void newItemAdded(not_null<HistoryItem*> item);
208 
209 	void registerClientSideMessage(not_null<HistoryItem*> item);
210 	void unregisterClientSideMessage(not_null<HistoryItem*> item);
211 	[[nodiscard]] auto clientSideMessages()
212 		-> const base::flat_set<not_null<HistoryItem*>> &;
213 	[[nodiscard]] HistoryItem *latestSendingMessage() const;
214 
215 	[[nodiscard]] bool readInboxTillNeedsRequest(MsgId tillId);
216 	void applyInboxReadUpdate(
217 		FolderId folderId,
218 		MsgId upTo,
219 		int stillUnread,
220 		int32 channelPts = 0);
221 	void inboxRead(MsgId upTo, std::optional<int> stillUnread = {});
222 	void inboxRead(not_null<const HistoryItem*> wasRead);
223 	void outboxRead(MsgId upTo);
224 	void outboxRead(not_null<const HistoryItem*> wasRead);
225 	[[nodiscard]] bool isServerSideUnread(
226 		not_null<const HistoryItem*> item) const;
227 	[[nodiscard]] MsgId loadAroundId() const;
228 	[[nodiscard]] MsgId inboxReadTillId() const;
229 	[[nodiscard]] MsgId outboxReadTillId() const;
230 
231 	[[nodiscard]] bool trackUnreadMessages() const;
232 	[[nodiscard]] int unreadCount() const;
233 	[[nodiscard]] bool unreadCountKnown() const;
234 
235 	// Some old unread count is known, but we read history till some place.
236 	[[nodiscard]] bool unreadCountRefreshNeeded(MsgId readTillId) const;
237 
238 	void setUnreadCount(int newUnreadCount);
239 	void setUnreadMark(bool unread);
240 	[[nodiscard]] bool unreadMark() const;
241 	void setFakeUnreadWhileOpened(bool enabled);
242 	[[nodiscard]] bool fakeUnreadWhileOpened() const;
243 	[[nodiscard]] int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0.
244 	[[nodiscard]] bool mute() const;
245 	bool changeMute(bool newMute);
246 	void addUnreadBar();
247 	void destroyUnreadBar();
248 	[[nodiscard]] Element *unreadBar() const;
249 	void calculateFirstUnreadMessage();
250 	void unsetFirstUnreadMessage();
251 	[[nodiscard]] Element *firstUnreadMessage() const;
252 	void clearNotifications();
253 	void clearIncomingNotifications();
254 
255 	[[nodiscard]] bool loadedAtBottom() const; // last message is in the list
256 	void setNotLoadedAtBottom();
257 	[[nodiscard]] bool loadedAtTop() const; // nothing was added after loading history back
258 	[[nodiscard]] bool isReadyFor(MsgId msgId); // has messages for showing history at msgId
259 	void getReadyFor(MsgId msgId);
260 
261 	[[nodiscard]] HistoryItem *lastMessage() const;
262 	[[nodiscard]] HistoryItem *lastServerMessage() const;
263 	[[nodiscard]] bool lastMessageKnown() const;
264 	[[nodiscard]] bool lastServerMessageKnown() const;
265 	void unknownMessageDeleted(MsgId messageId);
266 	void applyDialogTopMessage(MsgId topMessageId);
267 	void applyDialog(Data::Folder *requestFolder, const MTPDdialog &data);
268 	void applyPinnedUpdate(const MTPDupdateDialogPinned &data);
269 	void applyDialogFields(
270 		Data::Folder *folder,
271 		int unreadCount,
272 		MsgId maxInboxRead,
273 		MsgId maxOutboxRead);
274 	void dialogEntryApplied();
275 
276 	void cacheTopPromotion(
277 		bool promoted,
278 		const QString &type,
279 		const QString &message);
280 	[[nodiscard]] QStringView topPromotionType() const;
281 	[[nodiscard]] QString topPromotionMessage() const;
282 	[[nodiscard]] bool topPromotionAboutShown() const;
283 	void markTopPromotionAboutShown();
284 
285 	MsgId minMsgId() const;
286 	MsgId maxMsgId() const;
287 	MsgId msgIdForRead() const;
288 	HistoryItem *lastEditableMessage() const;
289 
290 	void resizeToWidth(int newWidth);
291 	void forceFullResize();
292 	int height() const;
293 
294 	void itemRemoved(not_null<HistoryItem*> item);
295 	void itemVanished(not_null<HistoryItem*> item);
296 
297 	HistoryItem *currentNotification();
298 	bool hasNotification() const;
299 	void skipNotification();
300 	void popNotification(HistoryItem *item);
301 
302 	bool hasPendingResizedItems() const;
303 	void setHasPendingResizedItems();
304 
305 	[[nodiscard]] auto sendActionPainter()
306 	-> not_null<HistoryView::SendActionPainter*> {
307 		return &_sendActionPainter;
308 	}
309 
310 	void clearLastKeyboard();
311 
getUnreadMentionsLoadedCount()312 	int getUnreadMentionsLoadedCount() const {
313 		return _unreadMentions.size();
314 	}
getMinLoadedUnreadMention()315 	MsgId getMinLoadedUnreadMention() const {
316 		return _unreadMentions.empty() ? 0 : _unreadMentions.front();
317 	}
getMaxLoadedUnreadMention()318 	MsgId getMaxLoadedUnreadMention() const {
319 		return _unreadMentions.empty() ? 0 : _unreadMentions.back();
320 	}
321 	int getUnreadMentionsCount(int notLoadedValue = -1) const {
322 		return _unreadMentionsCount ? *_unreadMentionsCount : notLoadedValue;
323 	}
hasUnreadMentions()324 	bool hasUnreadMentions() const {
325 		return (getUnreadMentionsCount() > 0);
326 	}
327 	void setUnreadMentionsCount(int count);
328 	bool addToUnreadMentions(MsgId msgId, UnreadMentionType type);
329 	void eraseFromUnreadMentions(MsgId msgId);
330 	void addUnreadMentionsSlice(const MTPmessages_Messages &result);
331 
332 	Data::Draft *draft(Data::DraftKey key) const;
333 	void setDraft(Data::DraftKey key, std::unique_ptr<Data::Draft> &&draft);
334 	void clearDraft(Data::DraftKey key);
335 
336 	[[nodiscard]] const Data::HistoryDrafts &draftsMap() const;
337 	void setDraftsMap(Data::HistoryDrafts &&map);
338 
localDraft()339 	Data::Draft *localDraft() const {
340 		return draft(Data::DraftKey::Local());
341 	}
localEditDraft()342 	Data::Draft *localEditDraft() const {
343 		return draft(Data::DraftKey::LocalEdit());
344 	}
cloudDraft()345 	Data::Draft *cloudDraft() const {
346 		return draft(Data::DraftKey::Cloud());
347 	}
setLocalDraft(std::unique_ptr<Data::Draft> && draft)348 	void setLocalDraft(std::unique_ptr<Data::Draft> &&draft) {
349 		setDraft(Data::DraftKey::Local(), std::move(draft));
350 	}
setLocalEditDraft(std::unique_ptr<Data::Draft> && draft)351 	void setLocalEditDraft(std::unique_ptr<Data::Draft> &&draft) {
352 		setDraft(Data::DraftKey::LocalEdit(), std::move(draft));
353 	}
setCloudDraft(std::unique_ptr<Data::Draft> && draft)354 	void setCloudDraft(std::unique_ptr<Data::Draft> &&draft) {
355 		setDraft(Data::DraftKey::Cloud(), std::move(draft));
356 	}
clearLocalDraft()357 	void clearLocalDraft() {
358 		clearDraft(Data::DraftKey::Local());
359 	}
clearCloudDraft()360 	void clearCloudDraft() {
361 		clearDraft(Data::DraftKey::Cloud());
362 	}
clearLocalEditDraft()363 	void clearLocalEditDraft() {
364 		clearDraft(Data::DraftKey::LocalEdit());
365 	}
366 	void clearDrafts();
367 	Data::Draft *createCloudDraft(const Data::Draft *fromDraft);
368 	bool skipCloudDraftUpdate(TimeId date) const;
369 	void startSavingCloudDraft();
370 	void finishSavingCloudDraft(TimeId savedAt);
371 	void takeLocalDraft(not_null<History*> from);
372 	void applyCloudDraft();
373 	void draftSavedToCloud();
374 
forwardDraft()375 	[[nodiscard]] const Data::ForwardDraft &forwardDraft() const {
376 		return _forwardDraft;
377 	}
378 	[[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft(
379 		const Data::ForwardDraft &draft) const;
380 	[[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft();
381 	void setForwardDraft(Data::ForwardDraft &&draft);
382 
383 	History *migrateSibling() const;
384 	[[nodiscard]] bool useTopPromotion() const;
385 	int fixedOnTopIndex() const override;
386 	void updateChatListExistence() override;
387 	bool shouldBeInChatList() const override;
388 	int chatListUnreadCount() const override;
389 	bool chatListUnreadMark() const override;
390 	bool chatListMutedBadge() const override;
391 	Dialogs::UnreadState chatListUnreadState() const override;
392 	HistoryItem *chatListMessage() const override;
393 	bool chatListMessageKnown() const override;
394 	void requestChatListMessage() override;
395 	const QString &chatListName() const override;
396 	const QString &chatListNameSortKey() const override;
397 	const base::flat_set<QString> &chatListNameWords() const override;
398 	const base::flat_set<QChar> &chatListFirstLetters() const override;
399 	void loadUserpic() override;
400 	void paintUserpic(
401 		Painter &p,
402 		std::shared_ptr<Data::CloudImageView> &view,
403 		int x,
404 		int y,
405 		int size) const override;
406 
407 	void refreshChatListNameSortKey();
408 
409 	void setFakeChatListMessageFrom(const MTPmessages_Messages &data);
410 	void checkChatListMessageRemoved(not_null<HistoryItem*> item);
411 
412 	void applyChatListGroup(
413 		ChannelId channelId,
414 		const MTPmessages_Messages &data);
415 
forgetScrollState()416 	void forgetScrollState() {
417 		scrollTopItem = nullptr;
418 	}
419 
420 	// find the correct scrollTopItem and scrollTopOffset using given top
421 	// of the displayed window relative to the history start coordinate
422 	void countScrollState(int top);
423 
424 	[[nodiscard]] std::pair<Element*, int> findItemAndOffset(int top) const;
425 
426 	[[nodiscard]] MsgId nextNonHistoryEntryId();
427 
428 	bool folderKnown() const override;
429 	Data::Folder *folder() const override;
430 	void setFolder(
431 		not_null<Data::Folder*> folder,
432 		HistoryItem *folderDialogItem = nullptr);
433 	void clearFolder();
434 
435 	// Interface for Data::Histories.
436 	void setInboxReadTill(MsgId upTo);
437 	std::optional<int> countStillUnreadLocal(MsgId readTillId) const;
438 
439 	[[nodiscard]] bool hasPinnedMessages() const;
440 	void setHasPinnedMessages(bool has);
441 
442 	// Still public data.
443 	std::deque<std::unique_ptr<HistoryBlock>> blocks;
444 
445 	not_null<PeerData*> peer;
446 
447 	// we save the last showAtMsgId to restore the state when switching
448 	// between different conversation histories
449 	MsgId showAtMsgId = ShowAtUnreadMsgId;
450 
451 	// we save a pointer of the history item at the top of the displayed window
452 	// together with an offset from the window top to the top of this message
453 	// resulting scrollTop = top(scrollTopItem) + scrollTopOffset
454 	Element *scrollTopItem = nullptr;
455 	int scrollTopOffset = 0;
456 
457 	bool lastKeyboardInited = false;
458 	bool lastKeyboardUsed = false;
459 	MsgId lastKeyboardId = 0;
460 	MsgId lastKeyboardHiddenId = 0;
461 	PeerId lastKeyboardFrom = 0;
462 
463 	mtpRequestId sendRequestId = 0;
464 
465 	Ui::Text::String cloudDraftTextCache;
466 	Dialogs::Ui::MessageView lastItemDialogsView;
467 
468 private:
469 	friend class HistoryBlock;
470 
471 	enum class Flag {
472 		f_has_pending_resized_items = (1 << 0),
473 	};
474 	using Flags = base::flags<Flag>;
is_flag_type(Flag)475 	friend inline constexpr auto is_flag_type(Flag) {
476 		return true;
477 	};
478 
479 	// when this item is destroyed scrollTopItem just points to the next one
480 	// and scrollTopOffset remains the same
481 	// if we are at the bottom of the window scrollTopItem == nullptr and
482 	// scrollTopOffset is undefined
483 	void getNextScrollTopItem(HistoryBlock *block, int32 i);
484 
485 	// helper method for countScrollState(int top)
486 	[[nodiscard]] Element *findScrollTopItem(int top) const;
487 
488 	// this method just removes a block from the blocks list
489 	// when the last item from this block was detached and
490 	// calls the required previousItemChanged()
491 	void removeBlock(not_null<HistoryBlock*> block);
492 	void clearSharedMedia();
493 
494 	not_null<HistoryItem*> insertItem(std::unique_ptr<HistoryItem> item);
495 	not_null<HistoryItem*> addNewItem(
496 		not_null<HistoryItem*> item,
497 		bool unread);
498 	not_null<HistoryItem*> addNewToBack(
499 		not_null<HistoryItem*> item,
500 		bool unread);
501 	not_null<HistoryItem*> addNewInTheMiddle(
502 		not_null<HistoryItem*> item,
503 		int blockIndex,
504 		int itemIndex);
505 
506 	// All this methods add a new item to the first or last block
507 	// depending on if we are in isBuildingFronBlock() state.
508 	// The last block is created on the go if it is needed.
509 
510 	// Adds the item to the back or front block, depending on
511 	// isBuildingFrontBlock(), creating the block if necessary.
512 	void addItemToBlock(not_null<HistoryItem*> item);
513 
514 	// Usually all new items are added to the last block.
515 	// Only when we scroll up and add a new slice to the
516 	// front we want to create a new front block.
517 	void startBuildingFrontBlock(int expectedItemsCount = 1);
518 	void finishBuildingFrontBlock();
isBuildingFrontBlock()519 	bool isBuildingFrontBlock() const {
520 		return _buildingFrontBlock != nullptr;
521 	}
522 
523 	void addCreatedOlderSlice(
524 		const std::vector<not_null<HistoryItem*>> &items);
525 
526 	void checkForLoadedAtTop(not_null<HistoryItem*> added);
527 	void mainViewRemoved(
528 		not_null<HistoryBlock*> block,
529 		not_null<Element*> view);
530 	void removeNotification(not_null<HistoryItem*> item);
531 
532 	TimeId adjustedChatListTimeId() const override;
533 	void changedChatListPinHook() override;
534 
535 	void setOutboxReadTill(MsgId upTo);
536 	void readClientSideMessages();
537 
538 	void applyMessageChanges(
539 		not_null<HistoryItem*> item,
540 		const MTPMessage &original);
541 	void applyServiceChanges(
542 		not_null<HistoryItem*> item,
543 		const MTPDmessageService &data);
544 
545 	// After adding a new history slice check lastMessage / loadedAtBottom.
546 	void checkLastMessage();
547 	void setLastMessage(HistoryItem *item);
548 	void setLastServerMessage(HistoryItem *item);
549 
550 	void refreshChatListMessage();
551 	void setChatListMessage(HistoryItem *item);
552 	std::optional<HistoryItem*> computeChatListMessageFromLast() const;
553 	void setChatListMessageFromLast();
554 	void setChatListMessageUnknown();
555 	void setFakeChatListMessage();
556 
557 	// Add all items to the unread mentions if we were not loaded at bottom and now are.
558 	void checkAddAllToUnreadMentions();
559 
560 	void addToSharedMedia(const std::vector<not_null<HistoryItem*>> &items);
561 	void addEdgesToSharedMedia();
562 
563 	void addItemsToLists(const std::vector<not_null<HistoryItem*>> &items);
564 	bool clearUnreadOnClientSide() const;
565 	bool skipUnreadUpdate() const;
566 
567 	HistoryItem *lastAvailableMessage() const;
568 	void getNextFirstUnreadMessage();
569 	bool nonEmptyCountMoreThan(int count) const;
570 
571 	// Creates if necessary a new block for adding item.
572 	// Depending on isBuildingFrontBlock() gets front or back block.
573 	HistoryBlock *prepareBlockForAddingItem();
574 
575 	void viewReplaced(not_null<const Element*> was, Element *now);
576 
577 	void createLocalDraftFromCloud();
578 
579 	HistoryService *insertJoinedMessage();
580 	void insertMessageToBlocks(not_null<HistoryItem*> item);
581 
582 	void setFolderPointer(Data::Folder *folder);
583 
584 	Flags _flags = 0;
585 	bool _mute = false;
586 	int _width = 0;
587 	int _height = 0;
588 	Element *_unreadBarView = nullptr;
589 	Element *_firstUnreadView = nullptr;
590 	HistoryService *_joinedMessage = nullptr;
591 	bool _loadedAtTop = false;
592 	bool _loadedAtBottom = true;
593 
594 	std::optional<Data::Folder*> _folder;
595 
596 	std::optional<MsgId> _inboxReadBefore;
597 	std::optional<MsgId> _outboxReadBefore;
598 	std::optional<int> _unreadCount;
599 	std::optional<int> _unreadMentionsCount;
600 	base::flat_set<MsgId> _unreadMentions;
601 	std::optional<HistoryItem*> _lastMessage;
602 	std::optional<HistoryItem*> _lastServerMessage;
603 	base::flat_set<not_null<HistoryItem*>> _clientSideMessages;
604 	std::unordered_set<std::unique_ptr<HistoryItem>> _messages;
605 
606 	// This almost always is equal to _lastMessage. The only difference is
607 	// for a group that migrated to a supergroup. Then _lastMessage can
608 	// be a migrate message, but _chatListMessage should be the one before.
609 	std::optional<HistoryItem*> _chatListMessage;
610 
611 	QString _chatListNameSortKey;
612 
613 	bool _unreadMark = false;
614 	bool _fakeUnreadWhileOpened = false;
615 	bool _hasPinnedMessages = false;
616 
617 	// A pointer to the block that is currently being built.
618 	// We hold this pointer so we can destroy it while building
619 	// and then create a new one if it is necessary.
620 	struct BuildingBlock {
621 		int expectedItemsCount = 0; // optimization for block->items.reserve() call
622 		HistoryBlock *block = nullptr;
623 	};
624 	std::unique_ptr<BuildingBlock> _buildingFrontBlock;
625 
626 	Data::HistoryDrafts _drafts;
627 	TimeId _acceptCloudDraftsAfter = 0;
628 	int _savingCloudDraftRequests = 0;
629 	Data::ForwardDraft _forwardDraft;
630 
631 	QString _topPromotedMessage;
632 	QString _topPromotedType;
633 
634 	HistoryView::SendActionPainter _sendActionPainter;
635 
636 	std::deque<not_null<HistoryItem*>> _notifications;
637 
638  };
639 
640 class HistoryBlock {
641 public:
642 	using Element = HistoryView::Element;
643 
644 	HistoryBlock(not_null<History*> history);
645 	HistoryBlock(const HistoryBlock &) = delete;
646 	HistoryBlock &operator=(const HistoryBlock &) = delete;
647 	~HistoryBlock();
648 
649 	std::vector<std::unique_ptr<Element>> messages;
650 
651 	void remove(not_null<Element*> view);
652 	void refreshView(not_null<Element*> view);
653 
654 	int resizeGetHeight(int newWidth, bool resizeAllItems);
y()655 	int y() const {
656 		return _y;
657 	}
setY(int y)658 	void setY(int y) {
659 		_y = y;
660 	}
height()661 	int height() const {
662 		return _height;
663 	}
history()664 	not_null<History*> history() const {
665 		return _history;
666 	}
667 
previousBlock()668 	HistoryBlock *previousBlock() const {
669 		Expects(_indexInHistory >= 0);
670 
671 		return (_indexInHistory > 0)
672 			? _history->blocks[_indexInHistory - 1].get()
673 			: nullptr;
674 	}
nextBlock()675 	HistoryBlock *nextBlock() const {
676 		Expects(_indexInHistory >= 0);
677 
678 		return (_indexInHistory + 1 < _history->blocks.size())
679 			? _history->blocks[_indexInHistory + 1].get()
680 			: nullptr;
681 	}
setIndexInHistory(int index)682 	void setIndexInHistory(int index) {
683 		_indexInHistory = index;
684 	}
indexInHistory()685 	int indexInHistory() const {
686 		Expects(_indexInHistory >= 0);
687 		Expects(_indexInHistory < _history->blocks.size());
688 		Expects(_history->blocks[_indexInHistory].get() == this);
689 
690 		return _indexInHistory;
691 	}
692 
693 protected:
694 	const not_null<History*> _history;
695 
696 	int _y = 0;
697 	int _height = 0;
698 	int _indexInHistory = -1;
699 
700 };
701