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 #include "history/history.h"
9 
10 #include "history/view/history_view_element.h"
11 #include "history/history_message.h"
12 #include "history/history_service.h"
13 #include "history/history_item_components.h"
14 #include "history/history_inner_widget.h"
15 #include "dialogs/dialogs_indexed_list.h"
16 #include "data/stickers/data_stickers.h"
17 #include "data/data_drafts.h"
18 #include "data/data_session.h"
19 #include "data/data_media_types.h"
20 #include "data/data_channel_admins.h"
21 #include "data/data_changes.h"
22 #include "data/data_chat_filters.h"
23 #include "data/data_scheduled_messages.h"
24 #include "data/data_send_action.h"
25 #include "data/data_folder.h"
26 #include "data/data_photo.h"
27 #include "data/data_channel.h"
28 #include "data/data_chat.h"
29 #include "data/data_user.h"
30 #include "data/data_document.h"
31 #include "data/data_histories.h"
32 #include "lang/lang_keys.h"
33 #include "apiwrap.h"
34 #include "mainwidget.h"
35 #include "mainwindow.h"
36 #include "main/main_session.h"
37 #include "window/notifications_manager.h"
38 #include "calls/calls_instance.h"
39 #include "storage/localstorage.h"
40 #include "storage/storage_facade.h"
41 #include "storage/storage_shared_media.h"
42 #include "storage/storage_account.h"
43 #include "support/support_helper.h"
44 #include "ui/image/image.h"
45 #include "ui/text/text_options.h"
46 #include "ui/toasts/common_toasts.h"
47 #include "ui/text/text_utilities.h"
48 #include "payments/payments_checkout_process.h"
49 #include "core/crash_reports.h"
50 #include "core/application.h"
51 #include "base/unixtime.h"
52 #include "base/qt_adapters.h"
53 #include "styles/style_dialogs.h"
54 
55 namespace {
56 
57 constexpr auto kNewBlockEachMessage = 50;
58 constexpr auto kSkipCloudDraftsFor = TimeId(2);
59 
60 using UpdateFlag = Data::HistoryUpdate::Flag;
61 
62 } // namespace
63 
History(not_null<Data::Session * > owner,PeerId peerId)64 History::History(not_null<Data::Session*> owner, PeerId peerId)
65 : Entry(owner, Type::History)
66 , peer(owner->peer(peerId))
67 , cloudDraftTextCache(st::dialogsTextWidthMin)
68 , _mute(owner->notifyIsMuted(peer))
69 , _chatListNameSortKey(owner->nameSortKey(peer->name))
70 , _sendActionPainter(this) {
71 	if (const auto user = peer->asUser()) {
72 		if (user->isBot()) {
73 			_outboxReadBefore = std::numeric_limits<MsgId>::max();
74 		}
75 	}
76 }
77 
clearLastKeyboard()78 void History::clearLastKeyboard() {
79 	if (lastKeyboardId) {
80 		if (lastKeyboardId == lastKeyboardHiddenId) {
81 			lastKeyboardHiddenId = 0;
82 		}
83 		lastKeyboardId = 0;
84 		session().changes().historyUpdated(this, UpdateFlag::BotKeyboard);
85 	}
86 	lastKeyboardInited = true;
87 	lastKeyboardFrom = 0;
88 }
89 
height() const90 int History::height() const {
91 	return _height;
92 }
93 
removeNotification(not_null<HistoryItem * > item)94 void History::removeNotification(not_null<HistoryItem*> item) {
95 	_notifications.erase(
96 		ranges::remove(_notifications, item),
97 		end(_notifications));
98 }
99 
currentNotification()100 HistoryItem *History::currentNotification() {
101 	return empty(_notifications)
102 		? nullptr
103 		: _notifications.front().get();
104 }
105 
hasNotification() const106 bool History::hasNotification() const {
107 	return !empty(_notifications);
108 }
109 
skipNotification()110 void History::skipNotification() {
111 	if (!empty(_notifications)) {
112 		_notifications.pop_front();
113 	}
114 }
115 
popNotification(HistoryItem * item)116 void History::popNotification(HistoryItem *item) {
117 	if (!empty(_notifications) && (_notifications.back() == item)) {
118 		_notifications.pop_back();
119 	}
120 }
121 
hasPendingResizedItems() const122 bool History::hasPendingResizedItems() const {
123 	return _flags & Flag::f_has_pending_resized_items;
124 }
125 
setHasPendingResizedItems()126 void History::setHasPendingResizedItems() {
127 	_flags |= Flag::f_has_pending_resized_items;
128 }
129 
itemRemoved(not_null<HistoryItem * > item)130 void History::itemRemoved(not_null<HistoryItem*> item) {
131 	if (item == _joinedMessage) {
132 		_joinedMessage = nullptr;
133 	}
134 	item->removeMainView();
135 	if (_lastServerMessage == item) {
136 		_lastServerMessage = std::nullopt;
137 	}
138 	if (lastMessage() == item) {
139 		_lastMessage = std::nullopt;
140 		if (loadedAtBottom()) {
141 			if (const auto last = lastAvailableMessage()) {
142 				setLastMessage(last);
143 			}
144 		}
145 	}
146 	checkChatListMessageRemoved(item);
147 	itemVanished(item);
148 	if (IsClientMsgId(item->id)) {
149 		unregisterClientSideMessage(item);
150 	}
151 	if (const auto chat = peer->asChat()) {
152 		if (const auto to = chat->getMigrateToChannel()) {
153 			if (const auto history = owner().historyLoaded(to)) {
154 				history->checkChatListMessageRemoved(item);
155 			}
156 		}
157 	}
158 }
159 
checkChatListMessageRemoved(not_null<HistoryItem * > item)160 void History::checkChatListMessageRemoved(not_null<HistoryItem*> item) {
161 	if (chatListMessage() != item) {
162 		return;
163 	}
164 	setChatListMessageUnknown();
165 	refreshChatListMessage();
166 }
167 
itemVanished(not_null<HistoryItem * > item)168 void History::itemVanished(not_null<HistoryItem*> item) {
169 	removeNotification(item);
170 	if (lastKeyboardId == item->id) {
171 		clearLastKeyboard();
172 	}
173 	if ((!item->out() || item->isPost())
174 		&& item->unread()
175 		&& unreadCount() > 0) {
176 		setUnreadCount(unreadCount() - 1);
177 	}
178 }
179 
takeLocalDraft(not_null<History * > from)180 void History::takeLocalDraft(not_null<History*> from) {
181 	const auto i = from->_drafts.find(Data::DraftKey::Local());
182 	if (i == end(from->_drafts)) {
183 		return;
184 	}
185 	auto &draft = i->second;
186 	if (!draft->textWithTags.text.isEmpty()
187 		&& !_drafts.contains(Data::DraftKey::Local())) {
188 		// Edit and reply to drafts can't migrate.
189 		// Cloud drafts do not migrate automatically.
190 		draft->msgId = 0;
191 
192 		setLocalDraft(std::move(draft));
193 	}
194 	from->clearLocalDraft();
195 	session().api().saveDraftToCloudDelayed(from);
196 }
197 
createLocalDraftFromCloud()198 void History::createLocalDraftFromCloud() {
199 	const auto draft = cloudDraft();
200 	if (!draft) {
201 		clearLocalDraft();
202 		return;
203 	} else if (Data::draftIsNull(draft) || !draft->date) {
204 		return;
205 	}
206 
207 	auto existing = localDraft();
208 	if (Data::draftIsNull(existing)
209 		|| !existing->date
210 		|| draft->date >= existing->date) {
211 		if (!existing) {
212 			setLocalDraft(std::make_unique<Data::Draft>(
213 				draft->textWithTags,
214 				draft->msgId,
215 				draft->cursor,
216 				draft->previewState));
217 			existing = localDraft();
218 		} else if (existing != draft) {
219 			existing->textWithTags = draft->textWithTags;
220 			existing->msgId = draft->msgId;
221 			existing->cursor = draft->cursor;
222 			existing->previewState = draft->previewState;
223 		}
224 		existing->date = draft->date;
225 	}
226 }
227 
draft(Data::DraftKey key) const228 Data::Draft *History::draft(Data::DraftKey key) const {
229 	if (!key) {
230 		return nullptr;
231 	}
232 	const auto i = _drafts.find(key);
233 	return (i != _drafts.end()) ? i->second.get() : nullptr;
234 }
235 
setDraft(Data::DraftKey key,std::unique_ptr<Data::Draft> && draft)236 void History::setDraft(Data::DraftKey key, std::unique_ptr<Data::Draft> &&draft) {
237 	if (!key) {
238 		return;
239 	}
240 	const auto changingCloudDraft = (key == Data::DraftKey::Cloud());
241 	if (changingCloudDraft) {
242 		cloudDraftTextCache.clear();
243 	}
244 	if (draft) {
245 		_drafts[key] = std::move(draft);
246 	} else if (_drafts.remove(key) && changingCloudDraft) {
247 		updateChatListSortPosition();
248 	}
249 }
250 
draftsMap() const251 const Data::HistoryDrafts &History::draftsMap() const {
252 	return _drafts;
253 }
254 
setDraftsMap(Data::HistoryDrafts && map)255 void History::setDraftsMap(Data::HistoryDrafts &&map) {
256 	for (auto &[key, draft] : _drafts) {
257 		map[key] = std::move(draft);
258 	}
259 	_drafts = std::move(map);
260 }
261 
clearDraft(Data::DraftKey key)262 void History::clearDraft(Data::DraftKey key) {
263 	setDraft(key, nullptr);
264 }
265 
clearDrafts()266 void History::clearDrafts() {
267 	const auto changingCloudDraft = _drafts.contains(Data::DraftKey::Cloud());
268 	_drafts.clear();
269 	if (changingCloudDraft) {
270 		cloudDraftTextCache.clear();
271 		updateChatListSortPosition();
272 	}
273 }
274 
createCloudDraft(const Data::Draft * fromDraft)275 Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) {
276 	if (Data::draftIsNull(fromDraft)) {
277 		setCloudDraft(std::make_unique<Data::Draft>(
278 			TextWithTags(),
279 			0,
280 			MessageCursor(),
281 			Data::PreviewState::Allowed));
282 		cloudDraft()->date = TimeId(0);
283 	} else {
284 		auto existing = cloudDraft();
285 		if (!existing) {
286 			setCloudDraft(std::make_unique<Data::Draft>(
287 				fromDraft->textWithTags,
288 				fromDraft->msgId,
289 				fromDraft->cursor,
290 				fromDraft->previewState));
291 			existing = cloudDraft();
292 		} else if (existing != fromDraft) {
293 			existing->textWithTags = fromDraft->textWithTags;
294 			existing->msgId = fromDraft->msgId;
295 			existing->cursor = fromDraft->cursor;
296 			existing->previewState = fromDraft->previewState;
297 		}
298 		existing->date = base::unixtime::now();
299 	}
300 
301 	cloudDraftTextCache.clear();
302 	updateChatListSortPosition();
303 
304 	return cloudDraft();
305 }
306 
skipCloudDraftUpdate(TimeId date) const307 bool History::skipCloudDraftUpdate(TimeId date) const {
308 	return (_savingCloudDraftRequests > 0)
309 		|| (date < _acceptCloudDraftsAfter);
310 }
311 
startSavingCloudDraft()312 void History::startSavingCloudDraft() {
313 	++_savingCloudDraftRequests;
314 }
315 
finishSavingCloudDraft(TimeId savedAt)316 void History::finishSavingCloudDraft(TimeId savedAt) {
317 	if (_savingCloudDraftRequests > 0) {
318 		--_savingCloudDraftRequests;
319 	}
320 	const auto acceptAfter = savedAt + kSkipCloudDraftsFor;
321 	_acceptCloudDraftsAfter = std::max(_acceptCloudDraftsAfter, acceptAfter);
322 }
323 
applyCloudDraft()324 void History::applyCloudDraft() {
325 	if (session().supportMode()) {
326 		updateChatListEntry();
327 		session().supportHelper().cloudDraftChanged(this);
328 	} else {
329 		createLocalDraftFromCloud();
330 		updateChatListSortPosition();
331 		session().changes().historyUpdated(this, UpdateFlag::CloudDraft);
332 	}
333 }
334 
draftSavedToCloud()335 void History::draftSavedToCloud() {
336 	updateChatListEntry();
337 	session().local().writeDrafts(this);
338 }
339 
resolveForwardDraft(const Data::ForwardDraft & draft) const340 Data::ResolvedForwardDraft History::resolveForwardDraft(
341 		const Data::ForwardDraft &draft) const {
342 	return Data::ResolvedForwardDraft{
343 		.items = owner().idsToItems(draft.ids),
344 		.options = draft.options,
345 	};
346 }
347 
resolveForwardDraft()348 Data::ResolvedForwardDraft History::resolveForwardDraft() {
349 	auto result = resolveForwardDraft(_forwardDraft);
350 	if (result.items.size() != _forwardDraft.ids.size()) {
351 		setForwardDraft({
352 			.ids = owner().itemsToIds(result.items),
353 			.options = result.options,
354 		});
355 	}
356 	return result;
357 }
358 
setForwardDraft(Data::ForwardDraft && draft)359 void History::setForwardDraft(Data::ForwardDraft &&draft) {
360 	_forwardDraft = std::move(draft);
361 }
362 
createItem(MsgId id,const MTPMessage & message,MessageFlags localFlags,bool detachExistingItem)363 not_null<HistoryItem*> History::createItem(
364 		MsgId id,
365 		const MTPMessage &message,
366 		MessageFlags localFlags,
367 		bool detachExistingItem) {
368 	if (const auto result = owner().message(channelId(), id)) {
369 		if (detachExistingItem) {
370 			result->removeMainView();
371 		}
372 		return result;
373 	}
374 	return HistoryItem::Create(this, id, message, localFlags);
375 }
376 
createItems(const QVector<MTPMessage> & data)377 std::vector<not_null<HistoryItem*>> History::createItems(
378 		const QVector<MTPMessage> &data) {
379 	auto result = std::vector<not_null<HistoryItem*>>();
380 	result.reserve(data.size());
381 	const auto localFlags = MessageFlags();
382 	const auto detachExistingItem = true;
383 	for (auto i = data.cend(), e = data.cbegin(); i != e;) {
384 		const auto &data = *--i;
385 		result.emplace_back(createItem(
386 			IdFromMessage(data),
387 			data,
388 			localFlags,
389 			detachExistingItem));
390 	}
391 	return result;
392 }
393 
addNewMessage(MsgId id,const MTPMessage & msg,MessageFlags localFlags,NewMessageType type)394 not_null<HistoryItem*> History::addNewMessage(
395 		MsgId id,
396 		const MTPMessage &msg,
397 		MessageFlags localFlags,
398 		NewMessageType type) {
399 	const auto detachExistingItem = (type == NewMessageType::Unread);
400 	const auto item = createItem(id, msg, localFlags, detachExistingItem);
401 	if (type == NewMessageType::Existing || item->mainView()) {
402 		return item;
403 	}
404 	const auto unread = (type == NewMessageType::Unread);
405 	if (unread && item->isHistoryEntry()) {
406 		applyMessageChanges(item, msg);
407 	}
408 	return addNewItem(item, unread);
409 }
410 
insertItem(std::unique_ptr<HistoryItem> item)411 not_null<HistoryItem*> History::insertItem(
412 		std::unique_ptr<HistoryItem> item) {
413 	Expects(item != nullptr);
414 
415 	const auto [i, ok] = _messages.insert(std::move(item));
416 
417 	const auto result = i->get();
418 	owner().registerMessage(result);
419 
420 	Ensures(ok);
421 	return result;
422 }
423 
destroyMessage(not_null<HistoryItem * > item)424 void History::destroyMessage(not_null<HistoryItem*> item) {
425 	Expects(item->isHistoryEntry() || !item->mainView());
426 
427 	const auto peerId = peer->id;
428 	if (item->isHistoryEntry()) {
429 		// All this must be done for all items manually in History::clear()!
430 		item->destroyHistoryEntry();
431 		if (item->isRegular()) {
432 			if (const auto types = item->sharedMediaTypes()) {
433 				session().storage().remove(Storage::SharedMediaRemoveOne(
434 					peerId,
435 					types,
436 					item->id));
437 			}
438 		}
439 		itemRemoved(item);
440 	}
441 	if (item->isSending()) {
442 		session().api().cancelLocalItem(item);
443 	}
444 
445 	const auto document = [&] {
446 		const auto media = item->media();
447 		return media ? media->document() : nullptr;
448 	}();
449 
450 	owner().unregisterMessage(item);
451 	Core::App().notifications().clearFromItem(item);
452 
453 	auto hack = std::unique_ptr<HistoryItem>(item.get());
454 	const auto i = _messages.find(hack);
455 	hack.release();
456 
457 	Assert(i != end(_messages));
458 	_messages.erase(i);
459 
460 	if (document) {
461 		session().data().documentMessageRemoved(document);
462 	}
463 }
464 
unpinAllMessages()465 void History::unpinAllMessages() {
466 	session().storage().remove(
467 		Storage::SharedMediaRemoveAll(
468 			peer->id,
469 			Storage::SharedMediaType::Pinned));
470 	setHasPinnedMessages(false);
471 	for (const auto &message : _messages) {
472 		if (message->isPinned()) {
473 			message->setIsPinned(false);
474 		}
475 	}
476 }
477 
addNewItem(not_null<HistoryItem * > item,bool unread)478 not_null<HistoryItem*> History::addNewItem(
479 		not_null<HistoryItem*> item,
480 		bool unread) {
481 	if (item->isScheduled()) {
482 		owner().scheduledMessages().appendSending(item);
483 		return item;
484 	} else if (!item->isHistoryEntry()) {
485 		return item;
486 	}
487 
488 	// In case we've loaded a new 'last' message
489 	// and it is not in blocks and we think that
490 	// we have all the messages till the bottom
491 	// we should unload known history or mark
492 	// currently loaded slice as not reaching bottom.
493 	const auto shouldMarkBottomNotLoaded = loadedAtBottom()
494 		&& !unread
495 		&& !isEmpty();
496 	if (shouldMarkBottomNotLoaded) {
497 		setNotLoadedAtBottom();
498 	}
499 
500 	if (!loadedAtBottom() || peer->migrateTo()) {
501 		setLastMessage(item);
502 		if (unread) {
503 			newItemAdded(item);
504 		}
505 	} else {
506 		addNewToBack(item, unread);
507 		checkForLoadedAtTop(item);
508 		if (!unread) {
509 			// When we add just one last item, like we do while loading dialogs,
510 			// we want to remove a single added grouped media, otherwise it will
511 			// jump once we open the message history (first we show only that
512 			// media, then we load the rest of the group and show the group).
513 			//
514 			// That way when we open the message history we show nothing until a
515 			// whole history part is loaded, it certainly will contain the group.
516 			removeOrphanMediaGroupPart();
517 		}
518 	}
519 	return item;
520 }
521 
checkForLoadedAtTop(not_null<HistoryItem * > added)522 void History::checkForLoadedAtTop(not_null<HistoryItem*> added) {
523 	if (peer->isChat()) {
524 		if (added->isGroupEssential() && !added->isGroupMigrate()) {
525 			// We added the first message about group creation.
526 			_loadedAtTop = true;
527 			addEdgesToSharedMedia();
528 		}
529 	} else if (peer->isChannel()) {
530 		if (added->id == 1) {
531 			_loadedAtTop = true;
532 			checkLocalMessages();
533 			addEdgesToSharedMedia();
534 		}
535 	}
536 }
537 
addNewLocalMessage(MsgId id,MessageFlags flags,UserId viaBotId,MsgId replyTo,TimeId date,PeerId from,const QString & postAuthor,const TextWithEntities & text,const MTPMessageMedia & media,HistoryMessageMarkupData && markup,uint64 groupedId)538 not_null<HistoryItem*> History::addNewLocalMessage(
539 		MsgId id,
540 		MessageFlags flags,
541 		UserId viaBotId,
542 		MsgId replyTo,
543 		TimeId date,
544 		PeerId from,
545 		const QString &postAuthor,
546 		const TextWithEntities &text,
547 		const MTPMessageMedia &media,
548 		HistoryMessageMarkupData &&markup,
549 		uint64 groupedId) {
550 	return addNewItem(
551 		makeMessage(
552 			id,
553 			flags | MessageFlag::Local,
554 			replyTo,
555 			viaBotId,
556 			date,
557 			from,
558 			postAuthor,
559 			text,
560 			media,
561 			std::move(markup),
562 			groupedId),
563 		true);
564 }
565 
addNewLocalMessage(MsgId id,MessageFlags flags,TimeId date,PeerId from,const QString & postAuthor,not_null<HistoryItem * > forwardOriginal)566 not_null<HistoryItem*> History::addNewLocalMessage(
567 		MsgId id,
568 		MessageFlags flags,
569 		TimeId date,
570 		PeerId from,
571 		const QString &postAuthor,
572 		not_null<HistoryItem*> forwardOriginal) {
573 	return addNewItem(
574 		makeMessage(
575 			id,
576 			flags | MessageFlag::Local,
577 			date,
578 			from,
579 			postAuthor,
580 			forwardOriginal),
581 		true);
582 }
583 
addNewLocalMessage(MsgId id,MessageFlags flags,UserId viaBotId,MsgId replyTo,TimeId date,PeerId from,const QString & postAuthor,not_null<DocumentData * > document,const TextWithEntities & caption,HistoryMessageMarkupData && markup)584 not_null<HistoryItem*> History::addNewLocalMessage(
585 		MsgId id,
586 		MessageFlags flags,
587 		UserId viaBotId,
588 		MsgId replyTo,
589 		TimeId date,
590 		PeerId from,
591 		const QString &postAuthor,
592 		not_null<DocumentData*> document,
593 		const TextWithEntities &caption,
594 		HistoryMessageMarkupData &&markup) {
595 	return addNewItem(
596 		makeMessage(
597 			id,
598 			flags | MessageFlag::Local,
599 			replyTo,
600 			viaBotId,
601 			date,
602 			from,
603 			postAuthor,
604 			document,
605 			caption,
606 			std::move(markup)),
607 		true);
608 }
609 
addNewLocalMessage(MsgId id,MessageFlags flags,UserId viaBotId,MsgId replyTo,TimeId date,PeerId from,const QString & postAuthor,not_null<PhotoData * > photo,const TextWithEntities & caption,HistoryMessageMarkupData && markup)610 not_null<HistoryItem*> History::addNewLocalMessage(
611 		MsgId id,
612 		MessageFlags flags,
613 		UserId viaBotId,
614 		MsgId replyTo,
615 		TimeId date,
616 		PeerId from,
617 		const QString &postAuthor,
618 		not_null<PhotoData*> photo,
619 		const TextWithEntities &caption,
620 		HistoryMessageMarkupData &&markup) {
621 	return addNewItem(
622 		makeMessage(
623 			id,
624 			flags | MessageFlag::Local,
625 			replyTo,
626 			viaBotId,
627 			date,
628 			from,
629 			postAuthor,
630 			photo,
631 			caption,
632 			std::move(markup)),
633 		true);
634 }
635 
addNewLocalMessage(MsgId id,MessageFlags flags,UserId viaBotId,MsgId replyTo,TimeId date,PeerId from,const QString & postAuthor,not_null<GameData * > game,HistoryMessageMarkupData && markup)636 not_null<HistoryItem*> History::addNewLocalMessage(
637 		MsgId id,
638 		MessageFlags flags,
639 		UserId viaBotId,
640 		MsgId replyTo,
641 		TimeId date,
642 		PeerId from,
643 		const QString &postAuthor,
644 		not_null<GameData*> game,
645 		HistoryMessageMarkupData &&markup) {
646 	return addNewItem(
647 		makeMessage(
648 			id,
649 			flags | MessageFlag::Local,
650 			replyTo,
651 			viaBotId,
652 			date,
653 			from,
654 			postAuthor,
655 			game,
656 			std::move(markup)),
657 		true);
658 }
659 
setUnreadMentionsCount(int count)660 void History::setUnreadMentionsCount(int count) {
661 	const auto had = _unreadMentionsCount && (*_unreadMentionsCount > 0);
662 	if (_unreadMentions.size() > count) {
663 		LOG(("API Warning: real mentions count is greater than received mentions count"));
664 		count = _unreadMentions.size();
665 	}
666 	_unreadMentionsCount = count;
667 	const auto has = (count > 0);
668 	if (has != had) {
669 		owner().chatsFilters().refreshHistory(this);
670 		updateChatListEntry();
671 	}
672 }
673 
addToUnreadMentions(MsgId msgId,UnreadMentionType type)674 bool History::addToUnreadMentions(
675 		MsgId msgId,
676 		UnreadMentionType type) {
677 	if (peer->isChannel() && !peer->isMegagroup()) {
678 		return false;
679 	}
680 	auto allLoaded = _unreadMentionsCount
681 		? (_unreadMentions.size() >= *_unreadMentionsCount)
682 		: false;
683 	if (allLoaded) {
684 		if (type == UnreadMentionType::New) {
685 			_unreadMentions.insert(msgId);
686 			setUnreadMentionsCount(*_unreadMentionsCount + 1);
687 			return true;
688 		}
689 	} else if (!_unreadMentions.empty() && type != UnreadMentionType::New) {
690 		_unreadMentions.insert(msgId);
691 		return true;
692 	}
693 	return false;
694 }
695 
eraseFromUnreadMentions(MsgId msgId)696 void History::eraseFromUnreadMentions(MsgId msgId) {
697 	_unreadMentions.remove(msgId);
698 	if (_unreadMentionsCount && *_unreadMentionsCount > 0) {
699 		setUnreadMentionsCount(*_unreadMentionsCount - 1);
700 	}
701 	session().changes().historyUpdated(this, UpdateFlag::UnreadMentions);
702 }
703 
addUnreadMentionsSlice(const MTPmessages_Messages & result)704 void History::addUnreadMentionsSlice(const MTPmessages_Messages &result) {
705 	auto count = 0;
706 	auto messages = (const QVector<MTPMessage>*)nullptr;
707 	auto getMessages = [&](auto &list) {
708 		owner().processUsers(list.vusers());
709 		owner().processChats(list.vchats());
710 		return &list.vmessages().v;
711 	};
712 	switch (result.type()) {
713 	case mtpc_messages_messages: {
714 		auto &d = result.c_messages_messages();
715 		messages = getMessages(d);
716 		count = messages->size();
717 	} break;
718 
719 	case mtpc_messages_messagesSlice: {
720 		auto &d = result.c_messages_messagesSlice();
721 		messages = getMessages(d);
722 		count = d.vcount().v;
723 	} break;
724 
725 	case mtpc_messages_channelMessages: {
726 		LOG(("API Error: unexpected messages.channelMessages! (History::addUnreadMentionsSlice)"));
727 		auto &d = result.c_messages_channelMessages();
728 		messages = getMessages(d);
729 		count = d.vcount().v;
730 	} break;
731 
732 	case mtpc_messages_messagesNotModified: {
733 		LOG(("API Error: received messages.messagesNotModified! (History::addUnreadMentionsSlice)"));
734 	} break;
735 
736 	default: Unexpected("type in History::addUnreadMentionsSlice");
737 	}
738 
739 	auto added = false;
740 	if (messages) {
741 		const auto localFlags = MessageFlags();
742 		const auto type = NewMessageType::Existing;
743 		for (const auto &message : *messages) {
744 			const auto item = addNewMessage(
745 				IdFromMessage(message),
746 				message,
747 				localFlags,
748 				type);
749 			if (item && item->isUnreadMention()) {
750 				_unreadMentions.insert(item->id);
751 				added = true;
752 			}
753 		}
754 	}
755 	if (!added) {
756 		count = _unreadMentions.size();
757 	}
758 	setUnreadMentionsCount(count);
759 	session().changes().historyUpdated(this, UpdateFlag::UnreadMentions);
760 }
761 
addNewToBack(not_null<HistoryItem * > item,bool unread)762 not_null<HistoryItem*> History::addNewToBack(
763 		not_null<HistoryItem*> item,
764 		bool unread) {
765 	Expects(!isBuildingFrontBlock());
766 
767 	addItemToBlock(item);
768 
769 	if (!unread && item->isRegular()) {
770 		if (const auto sharedMediaTypes = item->sharedMediaTypes()) {
771 			auto from = loadedAtTop() ? 0 : minMsgId();
772 			auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId();
773 			session().storage().add(Storage::SharedMediaAddExisting(
774 				peer->id,
775 				sharedMediaTypes,
776 				item->id,
777 				{ from, till }));
778 			if (sharedMediaTypes.test(Storage::SharedMediaType::Pinned)) {
779 				setHasPinnedMessages(true);
780 			}
781 		}
782 	}
783 	if (item->from()->id) {
784 		if (auto user = item->from()->asUser()) {
785 			auto getLastAuthors = [this]() -> std::deque<not_null<UserData*>>* {
786 				if (auto chat = peer->asChat()) {
787 					return &chat->lastAuthors;
788 				} else if (auto channel = peer->asMegagroup()) {
789 					return &channel->mgInfo->lastParticipants;
790 				}
791 				return nullptr;
792 			};
793 			if (auto megagroup = peer->asMegagroup()) {
794 				if (user->isBot()) {
795 					auto mgInfo = megagroup->mgInfo.get();
796 					Assert(mgInfo != nullptr);
797 					mgInfo->bots.insert(user);
798 					if (mgInfo->botStatus != 0 && mgInfo->botStatus < 2) {
799 						mgInfo->botStatus = 2;
800 					}
801 				}
802 			}
803 			if (auto lastAuthors = getLastAuthors()) {
804 				auto prev = ranges::find(
805 					*lastAuthors,
806 					user,
807 					[](not_null<UserData*> user) { return user.get(); });
808 				auto index = (prev != lastAuthors->end())
809 					? (lastAuthors->end() - prev)
810 					: -1;
811 				if (index > 0) {
812 					lastAuthors->erase(prev);
813 				} else if (index < 0 && peer->isMegagroup()) { // nothing is outdated if just reordering
814 					// admins information outdated
815 				}
816 				if (index) {
817 					lastAuthors->push_front(user);
818 				}
819 				if (auto megagroup = peer->asMegagroup()) {
820 					session().changes().peerUpdated(
821 						peer,
822 						Data::PeerUpdate::Flag::Members);
823 					owner().addNewMegagroupParticipant(megagroup, user);
824 				}
825 			}
826 		}
827 		if (item->definesReplyKeyboard()) {
828 			auto markupFlags = item->replyKeyboardFlags();
829 			if (!(markupFlags & ReplyMarkupFlag::Selective)
830 				|| item->mentionsMe()) {
831 				auto getMarkupSenders = [this]() -> base::flat_set<not_null<PeerData*>>* {
832 					if (auto chat = peer->asChat()) {
833 						return &chat->markupSenders;
834 					} else if (auto channel = peer->asMegagroup()) {
835 						return &channel->mgInfo->markupSenders;
836 					}
837 					return nullptr;
838 				};
839 				if (auto markupSenders = getMarkupSenders()) {
840 					markupSenders->insert(item->from());
841 				}
842 				if (markupFlags & ReplyMarkupFlag::None) {
843 					// None markup means replyKeyboardHide.
844 					if (lastKeyboardFrom == item->from()->id
845 						|| (!lastKeyboardInited
846 							&& !peer->isChat()
847 							&& !peer->isMegagroup()
848 							&& !item->out())) {
849 						clearLastKeyboard();
850 					}
851 				} else {
852 					bool botNotInChat = false;
853 					if (peer->isChat()) {
854 						botNotInChat = item->from()->isUser()
855 							&& (!peer->asChat()->participants.empty()
856 								|| !peer->canWrite())
857 							&& !peer->asChat()->participants.contains(
858 								item->from()->asUser());
859 					} else if (peer->isMegagroup()) {
860 						botNotInChat = item->from()->isUser()
861 							&& (peer->asChannel()->mgInfo->botStatus != 0
862 								|| !peer->canWrite())
863 							&& !peer->asChannel()->mgInfo->bots.contains(
864 								item->from()->asUser());
865 					}
866 					if (botNotInChat) {
867 						clearLastKeyboard();
868 					} else {
869 						lastKeyboardInited = true;
870 						lastKeyboardId = item->id;
871 						lastKeyboardFrom = item->from()->id;
872 						lastKeyboardUsed = false;
873 					}
874 				}
875 			}
876 		}
877 	}
878 
879 	setLastMessage(item);
880 	if (unread) {
881 		newItemAdded(item);
882 	}
883 
884 	owner().notifyHistoryChangeDelayed(this);
885 	return item;
886 }
887 
applyMessageChanges(not_null<HistoryItem * > item,const MTPMessage & data)888 void History::applyMessageChanges(
889 		not_null<HistoryItem*> item,
890 		const MTPMessage &data) {
891 	if (data.type() == mtpc_messageService) {
892 		applyServiceChanges(item, data.c_messageService());
893 	}
894 	owner().stickers().checkSavedGif(item);
895 	session().changes().messageUpdated(
896 		item,
897 		Data::MessageUpdate::Flag::NewAdded);
898 }
899 
applyServiceChanges(not_null<HistoryItem * > item,const MTPDmessageService & data)900 void History::applyServiceChanges(
901 		not_null<HistoryItem*> item,
902 		const MTPDmessageService &data) {
903 	const auto replyTo = data.vreply_to();
904 	const auto processJoinedUser = [&](
905 			not_null<ChannelData*> megagroup,
906 			not_null<MegagroupInfo*> mgInfo,
907 			not_null<UserData*> user) {
908 		if (!base::contains(mgInfo->lastParticipants, user)) {
909 			mgInfo->lastParticipants.push_front(user);
910 			session().changes().peerUpdated(
911 				peer,
912 				Data::PeerUpdate::Flag::Members);
913 			owner().addNewMegagroupParticipant(megagroup, user);
914 		}
915 		if (user->isBot()) {
916 			mgInfo->bots.insert(user);
917 			if (mgInfo->botStatus != 0 && mgInfo->botStatus < 2) {
918 				mgInfo->botStatus = 2;
919 			}
920 		}
921 	};
922 	const auto processJoinedPeer = [&](not_null<PeerData*> joined) {
923 		if (const auto megagroup = peer->asMegagroup()) {
924 			const auto mgInfo = megagroup->mgInfo.get();
925 			Assert(mgInfo != nullptr);
926 			if (const auto user = joined->asUser()) {
927 				processJoinedUser(megagroup, mgInfo, user);
928 			}
929 		}
930 	};
931 	data.vaction().match([&](const MTPDmessageActionChatAddUser &data) {
932 		if (const auto megagroup = peer->asMegagroup()) {
933 			const auto mgInfo = megagroup->mgInfo.get();
934 			Assert(mgInfo != nullptr);
935 			for (const auto &userId : data.vusers().v) {
936 				if (const auto user = owner().userLoaded(userId.v)) {
937 					processJoinedUser(megagroup, mgInfo, user);
938 				}
939 			}
940 		}
941 	}, [&](const MTPDmessageActionChatJoinedByLink &data) {
942 		processJoinedPeer(item->from());
943 	}, [&](const MTPDmessageActionChatDeletePhoto &data) {
944 		if (const auto chat = peer->asChat()) {
945 			chat->setPhoto(MTP_chatPhotoEmpty());
946 		}
947 	}, [&](const MTPDmessageActionChatDeleteUser &data) {
948 		const auto uid = data.vuser_id().v;
949 		if (lastKeyboardFrom == peerFromUser(uid)) {
950 			clearLastKeyboard();
951 		}
952 		if (const auto megagroup = peer->asMegagroup()) {
953 			if (const auto user = owner().userLoaded(uid)) {
954 				const auto mgInfo = megagroup->mgInfo.get();
955 				Assert(mgInfo != nullptr);
956 				const auto i = ranges::find(
957 					mgInfo->lastParticipants,
958 					user,
959 					[](not_null<UserData*> user) { return user.get(); });
960 				if (i != mgInfo->lastParticipants.end()) {
961 					mgInfo->lastParticipants.erase(i);
962 					session().changes().peerUpdated(
963 						peer,
964 						Data::PeerUpdate::Flag::Members);
965 				}
966 				owner().removeMegagroupParticipant(megagroup, user);
967 				if (megagroup->membersCount() > 1) {
968 					megagroup->setMembersCount(
969 						megagroup->membersCount() - 1);
970 				} else {
971 					mgInfo->lastParticipantsStatus
972 						|= MegagroupInfo::LastParticipantsCountOutdated;
973 					mgInfo->lastParticipantsCount = 0;
974 				}
975 				if (mgInfo->lastAdmins.contains(user)) {
976 					mgInfo->lastAdmins.remove(user);
977 					if (megagroup->adminsCount() > 1) {
978 						megagroup->setAdminsCount(
979 							megagroup->adminsCount() - 1);
980 					}
981 					session().changes().peerUpdated(
982 						peer,
983 						Data::PeerUpdate::Flag::Admins);
984 				}
985 				mgInfo->bots.remove(user);
986 				if (mgInfo->bots.empty() && mgInfo->botStatus > 0) {
987 					mgInfo->botStatus = -1;
988 				}
989 			}
990 			Data::ChannelAdminChanges(megagroup).remove(uid);
991 		}
992 	}, [&](const MTPDmessageActionChatEditPhoto &data) {
993 		data.vphoto().match([&](const MTPDphoto &data) {
994 			using Flag = MTPDchatPhoto::Flag;
995 			const auto photo = owner().processPhoto(data);
996 			photo->peer = peer;
997 			const auto chatPhoto = MTP_chatPhoto(
998 				MTP_flags((photo->hasVideo() ? Flag::f_has_video : Flag(0))
999 					| (photo->inlineThumbnailBytes().isEmpty()
1000 						? Flag(0)
1001 						: Flag::f_stripped_thumb)),
1002 				MTP_long(photo->id),
1003 				MTP_bytes(photo->inlineThumbnailBytes()),
1004 				data.vdc_id());
1005 			if (const auto chat = peer->asChat()) {
1006 				chat->setPhoto(chatPhoto);
1007 			} else if (const auto channel = peer->asChannel()) {
1008 				channel->setPhoto(chatPhoto);
1009 			}
1010 			peer->loadUserpic();
1011 		}, [&](const MTPDphotoEmpty &data) {
1012 			if (const auto chat = peer->asChat()) {
1013 				chat->setPhoto(MTP_chatPhotoEmpty());
1014 			} else if (const auto channel = peer->asChannel()) {
1015 				channel->setPhoto(MTP_chatPhotoEmpty());
1016 			}
1017 		});
1018 	}, [&](const MTPDmessageActionChatEditTitle &data) {
1019 		if (const auto chat = peer->asChat()) {
1020 			chat->setName(qs(data.vtitle()));
1021 		}
1022 	}, [&](const MTPDmessageActionChatMigrateTo &data) {
1023 		if (const auto chat = peer->asChat()) {
1024 			chat->addFlags(ChatDataFlag::Deactivated);
1025 			if (const auto channel = owner().channelLoaded(
1026 					data.vchannel_id().v)) {
1027 				Data::ApplyMigration(chat, channel);
1028 			}
1029 		}
1030 	}, [&](const MTPDmessageActionChannelMigrateFrom &data) {
1031 		if (const auto channel = peer->asChannel()) {
1032 			channel->addFlags(ChannelDataFlag::Megagroup);
1033 			if (const auto chat = owner().chatLoaded(data.vchat_id().v)) {
1034 				Data::ApplyMigration(chat, channel);
1035 			}
1036 		}
1037 	}, [&](const MTPDmessageActionPinMessage &data) {
1038 		if (replyTo) {
1039 			replyTo->match([&](const MTPDmessageReplyHeader &data) {
1040 				const auto id = data.vreply_to_msg_id().v;
1041 				if (item) {
1042 					session().storage().add(Storage::SharedMediaAddSlice(
1043 						peer->id,
1044 						Storage::SharedMediaType::Pinned,
1045 						{ id },
1046 						{ id, ServerMaxMsgId }));
1047 					setHasPinnedMessages(true);
1048 				}
1049 			});
1050 		}
1051 	}, [&](const MTPDmessageActionGroupCall &data) {
1052 		if (const auto channel = peer->asChannel()) {
1053 			channel->setGroupCall(data.vcall());
1054 		} else if (const auto chat = peer->asChat()) {
1055 			chat->setGroupCall(data.vcall());
1056 		}
1057 	}, [&](const MTPDmessageActionGroupCallScheduled &data) {
1058 		if (const auto channel = peer->asChannel()) {
1059 			channel->setGroupCall(data.vcall(), data.vschedule_date().v);
1060 		} else if (const auto chat = peer->asChat()) {
1061 			chat->setGroupCall(data.vcall(), data.vschedule_date().v);
1062 		}
1063 	}, [&](const MTPDmessageActionPaymentSent &data) {
1064 		if (const auto payment = item->Get<HistoryServicePayment>()) {
1065 			if (const auto message = payment->msg) {
1066 				if (const auto media = message->media()) {
1067 					if (const auto invoice = media->invoice()) {
1068 						using Payments::CheckoutProcess;
1069 						if (CheckoutProcess::TakePaymentStarted(message)) {
1070 							// Toast on a current active window.
1071 							Ui::ShowMultilineToast({
1072 								.text = tr::lng_payments_success(
1073 									tr::now,
1074 									lt_amount,
1075 									Ui::Text::Bold(payment->amount),
1076 									lt_title,
1077 									Ui::Text::Bold(invoice->title),
1078 									Ui::Text::WithEntities),
1079 							});
1080 						}
1081 					}
1082 				}
1083 			}
1084 		}
1085 	}, [&](const MTPDmessageActionSetChatTheme &data) {
1086 		peer->setThemeEmoji(qs(data.vemoticon()));
1087 	}, [&](const MTPDmessageActionChatJoinedByRequest &data) {
1088 		processJoinedPeer(item->from());
1089 	}, [](const auto &) {
1090 	});
1091 }
1092 
mainViewRemoved(not_null<HistoryBlock * > block,not_null<HistoryView::Element * > view)1093 void History::mainViewRemoved(
1094 		not_null<HistoryBlock*> block,
1095 		not_null<HistoryView::Element*> view) {
1096 	Expects(_joinedMessage != view->data());
1097 
1098 	if (_firstUnreadView == view) {
1099 		getNextFirstUnreadMessage();
1100 	}
1101 	if (_unreadBarView == view) {
1102 		_unreadBarView = nullptr;
1103 	}
1104 	if (scrollTopItem == view) {
1105 		getNextScrollTopItem(block, view->indexInBlock());
1106 	}
1107 }
1108 
newItemAdded(not_null<HistoryItem * > item)1109 void History::newItemAdded(not_null<HistoryItem*> item) {
1110 	item->indexAsNewItem();
1111 	if (const auto from = item->from() ? item->from()->asUser() : nullptr) {
1112 		if (from == item->author()) {
1113 			_sendActionPainter.clear(from);
1114 			owner().sendActionManager().repliesPaintersClear(this, from);
1115 		}
1116 		from->madeAction(item->date());
1117 	}
1118 	item->contributeToSlowmode();
1119 	if (item->showNotification()) {
1120 		_notifications.push_back(item);
1121 	}
1122 	owner().notifyNewItemAdded(item);
1123 	const auto stillShow = item->showNotification(); // Could be read already.
1124 	if (stillShow) {
1125 		Core::App().notifications().schedule(item);
1126 		if (!item->out() && item->unread()) {
1127 			if (unreadCountKnown()) {
1128 				setUnreadCount(unreadCount() + 1);
1129 			} else {
1130 				owner().histories().requestDialogEntry(this);
1131 			}
1132 		}
1133 	} else if (item->out()) {
1134 		destroyUnreadBar();
1135 	} else if (!item->unread()) {
1136 		inboxRead(item);
1137 	}
1138 	if (item->out() && !item->unread()) {
1139 		outboxRead(item);
1140 	}
1141 	item->incrementReplyToTopCounter();
1142 	if (!folderKnown()) {
1143 		owner().histories().requestDialogEntry(this);
1144 	}
1145 }
1146 
registerClientSideMessage(not_null<HistoryItem * > item)1147 void History::registerClientSideMessage(not_null<HistoryItem*> item) {
1148 	Expects(item->isHistoryEntry());
1149 	Expects(IsClientMsgId(item->id));
1150 
1151 	_clientSideMessages.emplace(item);
1152 	session().changes().historyUpdated(this, UpdateFlag::ClientSideMessages);
1153 }
1154 
unregisterClientSideMessage(not_null<HistoryItem * > item)1155 void History::unregisterClientSideMessage(not_null<HistoryItem*> item) {
1156 	const auto removed = _clientSideMessages.remove(item);
1157 	Assert(removed);
1158 
1159 	session().changes().historyUpdated(this, UpdateFlag::ClientSideMessages);
1160 }
1161 
clientSideMessages()1162 const base::flat_set<not_null<HistoryItem*>> &History::clientSideMessages() {
1163 	return _clientSideMessages;
1164 }
1165 
latestSendingMessage() const1166 HistoryItem *History::latestSendingMessage() const {
1167 	auto sending = ranges::views::all(
1168 		_clientSideMessages
1169 	) | ranges::views::filter([](not_null<HistoryItem*> item) {
1170 		return item->isSending();
1171 	});
1172 	const auto i = ranges::max_element(sending, ranges::less(), [](
1173 			not_null<HistoryItem*> item) {
1174 		return std::pair(item->date(), item->id.bare);
1175 	});
1176 	return (i == sending.end()) ? nullptr : i->get();
1177 }
1178 
prepareBlockForAddingItem()1179 HistoryBlock *History::prepareBlockForAddingItem() {
1180 	if (isBuildingFrontBlock()) {
1181 		if (_buildingFrontBlock->block) {
1182 			return _buildingFrontBlock->block;
1183 		}
1184 
1185 		blocks.push_front(std::make_unique<HistoryBlock>(this));
1186 		for (auto i = 0, l = int(blocks.size()); i != l; ++i) {
1187 			blocks[i]->setIndexInHistory(i);
1188 		}
1189 		_buildingFrontBlock->block = blocks.front().get();
1190 		if (_buildingFrontBlock->expectedItemsCount > 0) {
1191 			_buildingFrontBlock->block->messages.reserve(
1192 				_buildingFrontBlock->expectedItemsCount + 1);
1193 		}
1194 		return _buildingFrontBlock->block;
1195 	}
1196 
1197 	const auto addNewBlock = blocks.empty()
1198 		|| (blocks.back()->messages.size() >= kNewBlockEachMessage);
1199 	if (addNewBlock) {
1200 		blocks.push_back(std::make_unique<HistoryBlock>(this));
1201 		blocks.back()->setIndexInHistory(blocks.size() - 1);
1202 		blocks.back()->messages.reserve(kNewBlockEachMessage);
1203 	}
1204 	return blocks.back().get();
1205 }
1206 
viewReplaced(not_null<const Element * > was,Element * now)1207 void History::viewReplaced(not_null<const Element*> was, Element *now) {
1208 	if (scrollTopItem == was) scrollTopItem = now;
1209 	if (_firstUnreadView == was) _firstUnreadView = now;
1210 	if (_unreadBarView == was) _unreadBarView = now;
1211 }
1212 
addItemToBlock(not_null<HistoryItem * > item)1213 void History::addItemToBlock(not_null<HistoryItem*> item) {
1214 	Expects(!item->mainView());
1215 
1216 	auto block = prepareBlockForAddingItem();
1217 
1218 	block->messages.push_back(item->createView(
1219 		HistoryInner::ElementDelegate()));
1220 	const auto view = block->messages.back().get();
1221 	view->attachToBlock(block, block->messages.size() - 1);
1222 
1223 	if (isBuildingFrontBlock() && _buildingFrontBlock->expectedItemsCount > 0) {
1224 		--_buildingFrontBlock->expectedItemsCount;
1225 	}
1226 }
1227 
addEdgesToSharedMedia()1228 void History::addEdgesToSharedMedia() {
1229 	auto from = loadedAtTop() ? 0 : minMsgId();
1230 	auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId();
1231 	for (auto i = 0; i != Storage::kSharedMediaTypeCount; ++i) {
1232 		const auto type = static_cast<Storage::SharedMediaType>(i);
1233 		session().storage().add(Storage::SharedMediaAddSlice(
1234 			peer->id,
1235 			type,
1236 			{},
1237 			{ from, till }));
1238 	}
1239 }
1240 
addOlderSlice(const QVector<MTPMessage> & slice)1241 void History::addOlderSlice(const QVector<MTPMessage> &slice) {
1242 	if (slice.isEmpty()) {
1243 		_loadedAtTop = true;
1244 		checkLocalMessages();
1245 		return;
1246 	}
1247 
1248 	if (const auto added = createItems(slice); !added.empty()) {
1249 		addCreatedOlderSlice(added);
1250 	} else {
1251 		// If no items were added it means we've loaded everything old.
1252 		_loadedAtTop = true;
1253 		addEdgesToSharedMedia();
1254 	}
1255 	checkLocalMessages();
1256 	checkLastMessage();
1257 }
1258 
addCreatedOlderSlice(const std::vector<not_null<HistoryItem * >> & items)1259 void History::addCreatedOlderSlice(
1260 		const std::vector<not_null<HistoryItem*>> &items) {
1261 	startBuildingFrontBlock(items.size());
1262 	for (const auto &item : items) {
1263 		addItemToBlock(item);
1264 	}
1265 	finishBuildingFrontBlock();
1266 
1267 	if (loadedAtBottom()) {
1268 		// Add photos to overview and authors to lastAuthors.
1269 		addItemsToLists(items);
1270 	}
1271 	addToSharedMedia(items);
1272 }
1273 
addNewerSlice(const QVector<MTPMessage> & slice)1274 void History::addNewerSlice(const QVector<MTPMessage> &slice) {
1275 	bool wasLoadedAtBottom = loadedAtBottom();
1276 
1277 	if (slice.isEmpty()) {
1278 		_loadedAtBottom = true;
1279 		if (!lastMessage()) {
1280 			setLastMessage(lastAvailableMessage());
1281 		}
1282 	}
1283 
1284 	if (const auto added = createItems(slice); !added.empty()) {
1285 		Assert(!isBuildingFrontBlock());
1286 
1287 		for (const auto &item : added) {
1288 			addItemToBlock(item);
1289 		}
1290 
1291 		addToSharedMedia(added);
1292 	} else {
1293 		_loadedAtBottom = true;
1294 		setLastMessage(lastAvailableMessage());
1295 		addEdgesToSharedMedia();
1296 	}
1297 
1298 	if (!wasLoadedAtBottom) {
1299 		checkAddAllToUnreadMentions();
1300 	}
1301 
1302 	checkLocalMessages();
1303 	checkLastMessage();
1304 }
1305 
checkLastMessage()1306 void History::checkLastMessage() {
1307 	if (const auto last = lastMessage()) {
1308 		if (!_loadedAtBottom && last->mainView()) {
1309 			_loadedAtBottom = true;
1310 			checkAddAllToUnreadMentions();
1311 		}
1312 	} else if (_loadedAtBottom) {
1313 		setLastMessage(lastAvailableMessage());
1314 	}
1315 }
1316 
addItemsToLists(const std::vector<not_null<HistoryItem * >> & items)1317 void History::addItemsToLists(
1318 		const std::vector<not_null<HistoryItem*>> &items) {
1319 	std::deque<not_null<UserData*>> *lastAuthors = nullptr;
1320 	base::flat_set<not_null<PeerData*>> *markupSenders = nullptr;
1321 	if (peer->isChat()) {
1322 		lastAuthors = &peer->asChat()->lastAuthors;
1323 		markupSenders = &peer->asChat()->markupSenders;
1324 	} else if (peer->isMegagroup()) {
1325 		// We don't add users to mgInfo->lastParticipants here.
1326 		// We're scrolling back and we see messages from users that
1327 		// could be gone from the megagroup already. It is fine for
1328 		// chat->lastAuthors, because they're used only for field
1329 		// autocomplete, but this is bad for megagroups, because its
1330 		// lastParticipants are displayed in Profile as members list.
1331 		markupSenders = &peer->asChannel()->mgInfo->markupSenders;
1332 	}
1333 	for (const auto &item : ranges::views::reverse(items)) {
1334 		item->addToUnreadMentions(UnreadMentionType::Existing);
1335 		if (item->from()->id) {
1336 			if (lastAuthors) { // chats
1337 				if (auto user = item->from()->asUser()) {
1338 					if (!base::contains(*lastAuthors, user)) {
1339 						lastAuthors->push_back(user);
1340 					}
1341 				}
1342 			}
1343 		}
1344 		if (item->author()->id) {
1345 			if (markupSenders) { // chats with bots
1346 				if (!lastKeyboardInited && item->definesReplyKeyboard() && !item->out()) {
1347 					const auto markupFlags = item->replyKeyboardFlags();
1348 					if (!(markupFlags & ReplyMarkupFlag::Selective) || item->mentionsMe()) {
1349 						bool wasKeyboardHide = markupSenders->contains(item->author());
1350 						if (!wasKeyboardHide) {
1351 							markupSenders->insert(item->author());
1352 						}
1353 						if (!(markupFlags & ReplyMarkupFlag::None)) {
1354 							if (!lastKeyboardInited) {
1355 								bool botNotInChat = false;
1356 								if (peer->isChat()) {
1357 									botNotInChat = (!peer->canWrite() || !peer->asChat()->participants.empty()) && item->author()->isUser() && !peer->asChat()->participants.contains(item->author()->asUser());
1358 								} else if (peer->isMegagroup()) {
1359 									botNotInChat = (!peer->canWrite() || peer->asChannel()->mgInfo->botStatus != 0) && item->author()->isUser() && !peer->asChannel()->mgInfo->bots.contains(item->author()->asUser());
1360 								}
1361 								if (wasKeyboardHide || botNotInChat) {
1362 									clearLastKeyboard();
1363 								} else {
1364 									lastKeyboardInited = true;
1365 									lastKeyboardId = item->id;
1366 									lastKeyboardFrom = item->author()->id;
1367 									lastKeyboardUsed = false;
1368 								}
1369 							}
1370 						}
1371 					}
1372 				}
1373 			} else if (!lastKeyboardInited && item->definesReplyKeyboard() && !item->out()) { // conversations with bots
1374 				const auto markupFlags = item->replyKeyboardFlags();
1375 				if (!(markupFlags & ReplyMarkupFlag::Selective) || item->mentionsMe()) {
1376 					if (markupFlags & ReplyMarkupFlag::None) {
1377 						clearLastKeyboard();
1378 					} else {
1379 						lastKeyboardInited = true;
1380 						lastKeyboardId = item->id;
1381 						lastKeyboardFrom = item->author()->id;
1382 						lastKeyboardUsed = false;
1383 					}
1384 				}
1385 			}
1386 		}
1387 	}
1388 
1389 }
1390 
checkAddAllToUnreadMentions()1391 void History::checkAddAllToUnreadMentions() {
1392 	if (!loadedAtBottom()) {
1393 		return;
1394 	}
1395 
1396 	for (const auto &block : blocks) {
1397 		for (const auto &message : block->messages) {
1398 			const auto item = message->data();
1399 			item->addToUnreadMentions(UnreadMentionType::Existing);
1400 		}
1401 	}
1402 }
1403 
addToSharedMedia(const std::vector<not_null<HistoryItem * >> & items)1404 void History::addToSharedMedia(
1405 		const std::vector<not_null<HistoryItem*>> &items) {
1406 	std::vector<MsgId> medias[Storage::kSharedMediaTypeCount];
1407 	for (const auto &item : items) {
1408 		if (const auto types = item->sharedMediaTypes()) {
1409 			for (auto i = 0; i != Storage::kSharedMediaTypeCount; ++i) {
1410 				const auto type = static_cast<Storage::SharedMediaType>(i);
1411 				if (types.test(type)) {
1412 					if (medias[i].empty()) {
1413 						medias[i].reserve(items.size());
1414 					}
1415 					medias[i].push_back(item->id);
1416 				}
1417 			}
1418 		}
1419 	}
1420 	const auto from = loadedAtTop() ? 0 : minMsgId();
1421 	const auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId();
1422 	for (auto i = 0; i != Storage::kSharedMediaTypeCount; ++i) {
1423 		if (!medias[i].empty()) {
1424 			const auto type = static_cast<Storage::SharedMediaType>(i);
1425 			session().storage().add(Storage::SharedMediaAddSlice(
1426 				peer->id,
1427 				type,
1428 				std::move(medias[i]),
1429 				{ from, till }));
1430 			if (type == Storage::SharedMediaType::Pinned) {
1431 				setHasPinnedMessages(true);
1432 			}
1433 		}
1434 	}
1435 }
1436 
calculateFirstUnreadMessage()1437 void History::calculateFirstUnreadMessage() {
1438 	if (!_inboxReadBefore) {
1439 		return;
1440 	}
1441 
1442 	_firstUnreadView = nullptr;
1443 	if (!unreadCount() || !trackUnreadMessages()) {
1444 		return;
1445 	}
1446 	for (const auto &block : ranges::views::reverse(blocks)) {
1447 		for (const auto &message : ranges::views::reverse(block->messages)) {
1448 			const auto item = message->data();
1449 			if (!item->isRegular()) {
1450 				continue;
1451 			} else if (!item->out()) {
1452 				if (item->id >= *_inboxReadBefore) {
1453 					_firstUnreadView = message.get();
1454 				} else {
1455 					return;
1456 				}
1457 			}
1458 		}
1459 	}
1460 }
1461 
readInboxTillNeedsRequest(MsgId tillId)1462 bool History::readInboxTillNeedsRequest(MsgId tillId) {
1463 	Expects(!tillId || IsServerMsgId(tillId));
1464 
1465 	readClientSideMessages();
1466 	if (unreadMark()) {
1467 		owner().histories().changeDialogUnreadMark(this, false);
1468 	}
1469 	DEBUG_LOG(("Reading: readInboxTillNeedsRequest is_server %1, before %2."
1470 		).arg(Logs::b(IsServerMsgId(tillId))
1471 		).arg(_inboxReadBefore.value_or(-666).bare));
1472 	return IsServerMsgId(tillId) && (_inboxReadBefore.value_or(1) <= tillId);
1473 }
1474 
readClientSideMessages()1475 void History::readClientSideMessages() {
1476 	auto &histories = owner().histories();
1477 	for (const auto &item : _clientSideMessages) {
1478 		histories.readClientSideMessage(item);
1479 	}
1480 }
1481 
unreadCountRefreshNeeded(MsgId readTillId) const1482 bool History::unreadCountRefreshNeeded(MsgId readTillId) const {
1483 	return !unreadCountKnown()
1484 		|| ((readTillId + 1) > _inboxReadBefore.value_or(0));
1485 }
1486 
countStillUnreadLocal(MsgId readTillId) const1487 std::optional<int> History::countStillUnreadLocal(MsgId readTillId) const {
1488 	if (isEmpty() || !folderKnown()) {
1489 		DEBUG_LOG(("Reading: countStillUnreadLocal unknown %1 and %2.").arg(
1490 			Logs::b(isEmpty()),
1491 			Logs::b(folderKnown())));
1492 		return std::nullopt;
1493 	}
1494 	if (_inboxReadBefore) {
1495 		const auto before = *_inboxReadBefore;
1496 		DEBUG_LOG(("Reading: check before %1 with min %2 and max %3."
1497 			).arg(before.bare
1498 			).arg(minMsgId().bare
1499 			).arg(maxMsgId().bare));
1500 		if (minMsgId() <= before && maxMsgId() >= readTillId) {
1501 			auto result = 0;
1502 			for (const auto &block : blocks) {
1503 				for (const auto &message : block->messages) {
1504 					const auto item = message->data();
1505 					if (!item->isRegular()
1506 						|| (item->out() && !item->isFromScheduled())) {
1507 						continue;
1508 					} else if (item->id > readTillId) {
1509 						break;
1510 					} else if (item->id >= before) {
1511 						++result;
1512 					}
1513 				}
1514 			}
1515 			DEBUG_LOG(("Reading: check before result %1 with existing %2"
1516 				).arg(result
1517 				).arg(_unreadCount.value_or(-666)));
1518 			if (_unreadCount) {
1519 				return std::max(*_unreadCount - result, 0);
1520 			}
1521 		}
1522 	}
1523 	const auto minimalServerId = minMsgId();
1524 	DEBUG_LOG(("Reading: check at end loaded from %1 loaded %2 - %3").arg(
1525 		QString::number(minimalServerId.bare),
1526 		Logs::b(loadedAtBottom()),
1527 		Logs::b(loadedAtTop())));
1528 	if (!loadedAtBottom()
1529 		|| (!loadedAtTop() && !minimalServerId)
1530 		|| minimalServerId > readTillId) {
1531 		return std::nullopt;
1532 	}
1533 	auto result = 0;
1534 	for (const auto &block : ranges::views::reverse(blocks)) {
1535 		for (const auto &message : ranges::views::reverse(block->messages)) {
1536 			const auto item = message->data();
1537 			if (item->isRegular()) {
1538 				if (item->id <= readTillId) {
1539 					return result;
1540 				} else if (!item->out()) {
1541 					++result;
1542 				}
1543 			}
1544 		}
1545 	}
1546 	DEBUG_LOG(("Reading: check at end counted %1").arg(result));
1547 	return result;
1548 }
1549 
applyInboxReadUpdate(FolderId folderId,MsgId upTo,int stillUnread,int32 channelPts)1550 void History::applyInboxReadUpdate(
1551 		FolderId folderId,
1552 		MsgId upTo,
1553 		int stillUnread,
1554 		int32 channelPts) {
1555 	const auto folder = folderId ? owner().folderLoaded(folderId) : nullptr;
1556 	if (folder && this->folder() != folder) {
1557 		// If history folder is unknown or not synced, request both.
1558 		owner().histories().requestDialogEntry(this);
1559 		owner().histories().requestDialogEntry(folder);
1560 	}
1561 	if (_inboxReadBefore.value_or(1) <= upTo) {
1562 		if (!peer->isChannel() || peer->asChannel()->pts() == channelPts) {
1563 			inboxRead(upTo, stillUnread);
1564 		} else {
1565 			inboxRead(upTo);
1566 		}
1567 	}
1568 }
1569 
inboxRead(MsgId upTo,std::optional<int> stillUnread)1570 void History::inboxRead(MsgId upTo, std::optional<int> stillUnread) {
1571 	if (stillUnread.has_value() && folderKnown()) {
1572 		setUnreadCount(*stillUnread);
1573 	} else if (const auto still = countStillUnreadLocal(upTo)) {
1574 		setUnreadCount(*still);
1575 	} else {
1576 		owner().histories().requestDialogEntry(this);
1577 	}
1578 	setInboxReadTill(upTo);
1579 	updateChatListEntry();
1580 	if (const auto to = peer->migrateTo()) {
1581 		if (const auto migrated = peer->owner().historyLoaded(to->id)) {
1582 			migrated->updateChatListEntry();
1583 		}
1584 	}
1585 
1586 	_firstUnreadView = nullptr;
1587 	Core::App().notifications().clearIncomingFromHistory(this);
1588 }
1589 
inboxRead(not_null<const HistoryItem * > wasRead)1590 void History::inboxRead(not_null<const HistoryItem*> wasRead) {
1591 	if (wasRead->isRegular()) {
1592 		inboxRead(wasRead->id);
1593 	}
1594 }
1595 
outboxRead(MsgId upTo)1596 void History::outboxRead(MsgId upTo) {
1597 	setOutboxReadTill(upTo);
1598 	if (const auto last = chatListMessage()) {
1599 		if (last->out() && last->isRegular() && last->id <= upTo) {
1600 			session().changes().messageUpdated(
1601 				last,
1602 				Data::MessageUpdate::Flag::DialogRowRepaint);
1603 		}
1604 	}
1605 	updateChatListEntry();
1606 	session().changes().historyUpdated(this, UpdateFlag::OutboxRead);
1607 }
1608 
outboxRead(not_null<const HistoryItem * > wasRead)1609 void History::outboxRead(not_null<const HistoryItem*> wasRead) {
1610 	if (wasRead->isRegular()) {
1611 		outboxRead(wasRead->id);
1612 	}
1613 }
1614 
loadAroundId() const1615 MsgId History::loadAroundId() const {
1616 	if (_unreadCount && *_unreadCount > 0 && _inboxReadBefore) {
1617 		return *_inboxReadBefore;
1618 	}
1619 	return MsgId(0);
1620 }
1621 
inboxReadTillId() const1622 MsgId History::inboxReadTillId() const {
1623 	return _inboxReadBefore.value_or(1) - 1;
1624 }
1625 
outboxReadTillId() const1626 MsgId History::outboxReadTillId() const {
1627 	return _outboxReadBefore.value_or(1) - 1;
1628 }
1629 
lastAvailableMessage() const1630 HistoryItem *History::lastAvailableMessage() const {
1631 	return isEmpty() ? nullptr : blocks.back()->messages.back()->data().get();
1632 }
1633 
unreadCount() const1634 int History::unreadCount() const {
1635 	return _unreadCount ? *_unreadCount : 0;
1636 }
1637 
unreadCountForBadge() const1638 int History::unreadCountForBadge() const {
1639 	const auto result = unreadCount();
1640 	return (!result && unreadMark()) ? 1 : result;
1641 }
1642 
unreadCountKnown() const1643 bool History::unreadCountKnown() const {
1644 	return _unreadCount.has_value();
1645 }
1646 
setUnreadCount(int newUnreadCount)1647 void History::setUnreadCount(int newUnreadCount) {
1648 	Expects(folderKnown());
1649 
1650 	if (_unreadCount == newUnreadCount) {
1651 		return;
1652 	}
1653 	const auto wasForBadge = (unreadCountForBadge() > 0);
1654 	const auto refresher = gsl::finally([&] {
1655 		if (wasForBadge != (unreadCountForBadge() > 0)) {
1656 			owner().chatsFilters().refreshHistory(this);
1657 		}
1658 		session().changes().historyUpdated(this, UpdateFlag::UnreadView);
1659 	});
1660 	const auto notifier = unreadStateChangeNotifier(true);
1661 	_unreadCount = newUnreadCount;
1662 
1663 	const auto lastOutgoing = [&] {
1664 		const auto last = lastMessage();
1665 		return last
1666 			&& last->isRegular()
1667 			&& loadedAtBottom()
1668 			&& !isEmpty()
1669 			&& blocks.back()->messages.back()->data() == last
1670 			&& last->out();
1671 	}();
1672 	if (newUnreadCount == 1 && !lastOutgoing) {
1673 		if (loadedAtBottom()) {
1674 			_firstUnreadView = !isEmpty()
1675 				? blocks.back()->messages.back().get()
1676 				: nullptr;
1677 		}
1678 		if (const auto last = msgIdForRead()) {
1679 			setInboxReadTill(last - 1);
1680 		}
1681 	} else if (!newUnreadCount) {
1682 		_firstUnreadView = nullptr;
1683 		if (const auto last = msgIdForRead()) {
1684 			setInboxReadTill(last);
1685 		}
1686 	} else if (!_firstUnreadView && !_unreadBarView && loadedAtBottom()) {
1687 		calculateFirstUnreadMessage();
1688 	}
1689 }
1690 
setUnreadMark(bool unread)1691 void History::setUnreadMark(bool unread) {
1692 	if (clearUnreadOnClientSide()) {
1693 		unread = false;
1694 	}
1695 	if (_unreadMark == unread) {
1696 		return;
1697 	}
1698 	const auto noUnreadMessages = !unreadCount();
1699 	const auto refresher = gsl::finally([&] {
1700 		if (inChatList() && noUnreadMessages) {
1701 			owner().chatsFilters().refreshHistory(this);
1702 			updateChatListEntry();
1703 		}
1704 		session().changes().historyUpdated(this, UpdateFlag::UnreadView);
1705 	});
1706 	const auto notifier = unreadStateChangeNotifier(noUnreadMessages);
1707 	_unreadMark = unread;
1708 }
1709 
unreadMark() const1710 bool History::unreadMark() const {
1711 	return _unreadMark;
1712 }
1713 
setFakeUnreadWhileOpened(bool enabled)1714 void History::setFakeUnreadWhileOpened(bool enabled) {
1715 	if (_fakeUnreadWhileOpened == enabled
1716 		|| (enabled
1717 			&& (!inChatList()
1718 				|| (!unreadCount()
1719 					&& !unreadMark()
1720 					&& !hasUnreadMentions())))) {
1721 		return;
1722 	}
1723 	_fakeUnreadWhileOpened = enabled;
1724 	owner().chatsFilters().refreshHistory(this);
1725 }
1726 
fakeUnreadWhileOpened() const1727 [[nodiscard]] bool History::fakeUnreadWhileOpened() const {
1728 	return _fakeUnreadWhileOpened;
1729 }
1730 
mute() const1731 bool History::mute() const {
1732 	return _mute;
1733 }
1734 
changeMute(bool newMute)1735 bool History::changeMute(bool newMute) {
1736 	if (_mute == newMute) {
1737 		return false;
1738 	}
1739 	const auto refresher = gsl::finally([&] {
1740 		if (inChatList()) {
1741 			owner().chatsFilters().refreshHistory(this);
1742 			updateChatListEntry();
1743 		}
1744 		session().changes().peerUpdated(
1745 			peer,
1746 			Data::PeerUpdate::Flag::Notifications);
1747 	});
1748 	const auto notify = (unreadCountForBadge() > 0);
1749 	const auto notifier = unreadStateChangeNotifier(notify);
1750 	_mute = newMute;
1751 	return true;
1752 }
1753 
getNextFirstUnreadMessage()1754 void History::getNextFirstUnreadMessage() {
1755 	Expects(_firstUnreadView != nullptr);
1756 
1757 	const auto block = _firstUnreadView->block();
1758 	const auto index = _firstUnreadView->indexInBlock();
1759 	const auto setFromMessage = [&](const auto &view) {
1760 		if (view->data()->isRegular()) {
1761 			_firstUnreadView = view.get();
1762 			return true;
1763 		}
1764 		return false;
1765 	};
1766 	if (index >= 0) {
1767 		const auto count = int(block->messages.size());
1768 		for (auto i = index + 1; i != count; ++i) {
1769 			const auto &message = block->messages[i];
1770 			if (setFromMessage(message)) {
1771 				return;
1772 			}
1773 		}
1774 	}
1775 
1776 	const auto count = int(blocks.size());
1777 	for (auto j = block->indexInHistory() + 1; j != count; ++j) {
1778 		for (const auto &message : blocks[j]->messages) {
1779 			if (setFromMessage(message)) {
1780 				return;
1781 			}
1782 		}
1783 	}
1784 	_firstUnreadView = nullptr;
1785 }
1786 
nextNonHistoryEntryId()1787 MsgId History::nextNonHistoryEntryId() {
1788 	return owner().nextNonHistoryEntryId();
1789 }
1790 
folderKnown() const1791 bool History::folderKnown() const {
1792 	return _folder.has_value();
1793 }
1794 
folder() const1795 Data::Folder *History::folder() const {
1796 	return _folder.value_or(nullptr);
1797 }
1798 
setFolder(not_null<Data::Folder * > folder,HistoryItem * folderDialogItem)1799 void History::setFolder(
1800 		not_null<Data::Folder*> folder,
1801 		HistoryItem *folderDialogItem) {
1802 	setFolderPointer(folder);
1803 	if (folderDialogItem) {
1804 		setLastServerMessage(folderDialogItem);
1805 	}
1806 }
1807 
clearFolder()1808 void History::clearFolder() {
1809 	setFolderPointer(nullptr);
1810 }
1811 
setFolderPointer(Data::Folder * folder)1812 void History::setFolderPointer(Data::Folder *folder) {
1813 	if (_folder == folder) {
1814 		return;
1815 	}
1816 	if (isPinnedDialog(FilterId())) {
1817 		owner().setChatPinned(this, FilterId(), false);
1818 	}
1819 	const auto wasKnown = folderKnown();
1820 	const auto wasInList = inChatList();
1821 	if (wasInList) {
1822 		removeFromChatList(0, owner().chatsList(this->folder()));
1823 	}
1824 	const auto was = _folder.value_or(nullptr);
1825 	_folder = folder;
1826 	if (was) {
1827 		was->unregisterOne(this);
1828 	}
1829 	if (wasInList) {
1830 		addToChatList(0, owner().chatsList(folder));
1831 
1832 		owner().chatsFilters().refreshHistory(this);
1833 		updateChatListEntry();
1834 
1835 		owner().chatsListChanged(was);
1836 		owner().chatsListChanged(folder);
1837 	} else if (!wasKnown) {
1838 		updateChatListSortPosition();
1839 	}
1840 	if (folder) {
1841 		folder->registerOne(this);
1842 	}
1843 	session().changes().historyUpdated(this, UpdateFlag::Folder);
1844 }
1845 
applyPinnedUpdate(const MTPDupdateDialogPinned & data)1846 void History::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
1847 	const auto folderId = data.vfolder_id().value_or_empty();
1848 	if (!folderKnown()) {
1849 		if (folderId) {
1850 			setFolder(owner().folder(folderId));
1851 		} else {
1852 			clearFolder();
1853 		}
1854 	}
1855 	owner().setChatPinned(this, FilterId(), data.is_pinned());
1856 }
1857 
adjustedChatListTimeId() const1858 TimeId History::adjustedChatListTimeId() const {
1859 	const auto result = chatListTimeId();
1860 	if (const auto draft = cloudDraft()) {
1861 		if (!Data::draftIsNull(draft) && !session().supportMode()) {
1862 			return std::max(result, draft->date);
1863 		}
1864 	}
1865 	return result;
1866 }
1867 
countScrollState(int top)1868 void History::countScrollState(int top) {
1869 	std::tie(scrollTopItem, scrollTopOffset) = findItemAndOffset(top);
1870 }
1871 
findItemAndOffset(int top) const1872 auto History::findItemAndOffset(int top) const -> std::pair<Element*, int> {
1873 	if (const auto element = findScrollTopItem(top)) {
1874 		return { element, (top - element->block()->y() - element->y()) };
1875 	}
1876 	return {};
1877 }
1878 
findScrollTopItem(int top) const1879 auto History::findScrollTopItem(int top) const -> Element* {
1880 	if (isEmpty()) {
1881 		return nullptr;
1882 	}
1883 
1884 	auto itemIndex = 0;
1885 	auto blockIndex = 0;
1886 	auto itemTop = 0;
1887 	if (scrollTopItem) {
1888 		itemIndex = scrollTopItem->indexInBlock();
1889 		blockIndex = scrollTopItem->block()->indexInHistory();
1890 		itemTop = blocks[blockIndex]->y() + scrollTopItem->y();
1891 	}
1892 	if (itemTop > top) {
1893 		// go backward through history while we don't find an item that starts above
1894 		do {
1895 			const auto &block = blocks[blockIndex];
1896 			for (--itemIndex; itemIndex >= 0; --itemIndex) {
1897 				const auto view = block->messages[itemIndex].get();
1898 				itemTop = block->y() + view->y();
1899 				if (itemTop <= top) {
1900 					return view;
1901 				}
1902 			}
1903 			if (--blockIndex >= 0) {
1904 				itemIndex = blocks[blockIndex]->messages.size();
1905 			} else {
1906 				break;
1907 			}
1908 		} while (true);
1909 
1910 		return blocks.front()->messages.front().get();
1911 	}
1912 	// go forward through history while we don't find the last item that starts above
1913 	for (auto blocksCount = int(blocks.size()); blockIndex < blocksCount; ++blockIndex) {
1914 		const auto &block = blocks[blockIndex];
1915 		for (auto itemsCount = int(block->messages.size()); itemIndex < itemsCount; ++itemIndex) {
1916 			itemTop = block->y() + block->messages[itemIndex]->y();
1917 			if (itemTop > top) {
1918 				Assert(itemIndex > 0 || blockIndex > 0);
1919 				if (itemIndex > 0) {
1920 					return block->messages[itemIndex - 1].get();
1921 				}
1922 				return blocks[blockIndex - 1]->messages.back().get();
1923 			}
1924 		}
1925 		itemIndex = 0;
1926 	}
1927 	return blocks.back()->messages.back().get();
1928 }
1929 
getNextScrollTopItem(HistoryBlock * block,int32 i)1930 void History::getNextScrollTopItem(HistoryBlock *block, int32 i) {
1931 	++i;
1932 	if (i > 0 && i < block->messages.size()) {
1933 		scrollTopItem = block->messages[i].get();
1934 		return;
1935 	}
1936 	int j = block->indexInHistory() + 1;
1937 	if (j > 0 && j < blocks.size()) {
1938 		scrollTopItem = blocks[j]->messages.front().get();
1939 		return;
1940 	}
1941 	scrollTopItem = nullptr;
1942 }
1943 
addUnreadBar()1944 void History::addUnreadBar() {
1945 	if (_unreadBarView || !_firstUnreadView || !unreadCount()) {
1946 		return;
1947 	}
1948 	if (const auto count = chatListUnreadCount()) {
1949 		_unreadBarView = _firstUnreadView;
1950 		_unreadBarView->createUnreadBar(tr::lng_unread_bar_some());
1951 	}
1952 }
1953 
destroyUnreadBar()1954 void History::destroyUnreadBar() {
1955 	if (const auto view = base::take(_unreadBarView)) {
1956 		view->destroyUnreadBar();
1957 	}
1958 }
1959 
unsetFirstUnreadMessage()1960 void History::unsetFirstUnreadMessage() {
1961 	_firstUnreadView = nullptr;
1962 }
1963 
unreadBar() const1964 HistoryView::Element *History::unreadBar() const {
1965 	return _unreadBarView;
1966 }
1967 
firstUnreadMessage() const1968 HistoryView::Element *History::firstUnreadMessage() const {
1969 	return _firstUnreadView;
1970 }
1971 
addNewInTheMiddle(not_null<HistoryItem * > item,int blockIndex,int itemIndex)1972 not_null<HistoryItem*> History::addNewInTheMiddle(
1973 		not_null<HistoryItem*> item,
1974 		int blockIndex,
1975 		int itemIndex) {
1976 	Expects(blockIndex >= 0);
1977 	Expects(blockIndex < blocks.size());
1978 	Expects(itemIndex >= 0);
1979 	Expects(itemIndex <= blocks[blockIndex]->messages.size());
1980 
1981 	const auto &block = blocks[blockIndex];
1982 
1983 	const auto it = block->messages.insert(
1984 		block->messages.begin() + itemIndex,
1985 		item->createView(
1986 			HistoryInner::ElementDelegate()));
1987 	(*it)->attachToBlock(block.get(), itemIndex);
1988 	if (itemIndex + 1 < block->messages.size()) {
1989 		for (auto i = itemIndex + 1, l = int(block->messages.size()); i != l; ++i) {
1990 			block->messages[i]->setIndexInBlock(i);
1991 		}
1992 		block->messages[itemIndex + 1]->previousInBlocksChanged();
1993 	} else if (blockIndex + 1 < blocks.size() && !blocks[blockIndex + 1]->messages.empty()) {
1994 		blocks[blockIndex + 1]->messages.front()->previousInBlocksChanged();
1995 	} else {
1996 		(*it)->nextInBlocksRemoved();
1997 	}
1998 
1999 	return item;
2000 }
2001 
migrateSibling() const2002 History *History::migrateSibling() const {
2003 	const auto addFromId = [&] {
2004 		if (const auto from = peer->migrateFrom()) {
2005 			return from->id;
2006 		} else if (const auto to = peer->migrateTo()) {
2007 			return to->id;
2008 		}
2009 		return PeerId(0);
2010 	}();
2011 	return owner().historyLoaded(addFromId);
2012 }
2013 
chatListUnreadCount() const2014 int History::chatListUnreadCount() const {
2015 	const auto result = unreadCount();
2016 	if (const auto migrated = migrateSibling()) {
2017 		return result + migrated->unreadCount();
2018 	}
2019 	return result;
2020 }
2021 
chatListUnreadMark() const2022 bool History::chatListUnreadMark() const {
2023 	if (unreadMark()) {
2024 		return true;
2025 	} else if (const auto migrated = migrateSibling()) {
2026 		return migrated->unreadMark();
2027 	}
2028 	return false;
2029 }
2030 
chatListMutedBadge() const2031 bool History::chatListMutedBadge() const {
2032 	return mute();
2033 }
2034 
chatListUnreadState() const2035 Dialogs::UnreadState History::chatListUnreadState() const {
2036 	auto result = Dialogs::UnreadState();
2037 	const auto count = _unreadCount.value_or(0);
2038 	const auto mark = !count && _unreadMark;
2039 	result.messages = count;
2040 	result.messagesMuted = mute() ? count : 0;
2041 	result.chats = count ? 1 : 0;
2042 	result.chatsMuted = (count && mute()) ? 1 : 0;
2043 	result.marks = mark ? 1 : 0;
2044 	result.marksMuted = (mark && mute()) ? 1 : 0;
2045 	result.known = _unreadCount.has_value();
2046 	return result;
2047 }
2048 
chatListMessage() const2049 HistoryItem *History::chatListMessage() const {
2050 	return _chatListMessage.value_or(nullptr);
2051 }
2052 
chatListMessageKnown() const2053 bool History::chatListMessageKnown() const {
2054 	return _chatListMessage.has_value();
2055 }
2056 
chatListName() const2057 const QString &History::chatListName() const {
2058 	return peer->name;
2059 }
2060 
chatListNameSortKey() const2061 const QString &History::chatListNameSortKey() const {
2062 	return _chatListNameSortKey;
2063 }
2064 
refreshChatListNameSortKey()2065 void History::refreshChatListNameSortKey() {
2066 	_chatListNameSortKey = owner().nameSortKey(peer->name);
2067 }
2068 
chatListNameWords() const2069 const base::flat_set<QString> &History::chatListNameWords() const {
2070 	return peer->nameWords();
2071 }
2072 
chatListFirstLetters() const2073 const base::flat_set<QChar> &History::chatListFirstLetters() const {
2074 	return peer->nameFirstLetters();
2075 }
2076 
loadUserpic()2077 void History::loadUserpic() {
2078 	peer->loadUserpic();
2079 }
2080 
paintUserpic(Painter & p,std::shared_ptr<Data::CloudImageView> & view,int x,int y,int size) const2081 void History::paintUserpic(
2082 		Painter &p,
2083 		std::shared_ptr<Data::CloudImageView> &view,
2084 		int x,
2085 		int y,
2086 		int size) const {
2087 	peer->paintUserpic(p, view, x, y, size);
2088 }
2089 
startBuildingFrontBlock(int expectedItemsCount)2090 void History::startBuildingFrontBlock(int expectedItemsCount) {
2091 	Assert(!isBuildingFrontBlock());
2092 	Assert(expectedItemsCount > 0);
2093 
2094 	_buildingFrontBlock = std::make_unique<BuildingBlock>();
2095 	_buildingFrontBlock->expectedItemsCount = expectedItemsCount;
2096 }
2097 
finishBuildingFrontBlock()2098 void History::finishBuildingFrontBlock() {
2099 	Expects(isBuildingFrontBlock());
2100 
2101 	// Some checks if there was some message history already
2102 	if (const auto block = base::take(_buildingFrontBlock)->block) {
2103 		if (blocks.size() > 1) {
2104 			// ... item, item, item, last ], [ first, item, item ...
2105 			const auto first = blocks[1]->messages.front().get();
2106 
2107 			// we've added a new front block, so previous item for
2108 			// the old first item of a first block was changed
2109 			first->previousInBlocksChanged();
2110 		} else {
2111 			block->messages.back()->nextInBlocksRemoved();
2112 		}
2113 	}
2114 }
2115 
clearNotifications()2116 void History::clearNotifications() {
2117 	_notifications.clear();
2118 }
2119 
clearIncomingNotifications()2120 void History::clearIncomingNotifications() {
2121 	if (!peer->isSelf()) {
2122 		_notifications.erase(
2123 			ranges::remove(_notifications, false, &HistoryItem::out),
2124 			end(_notifications));
2125 	}
2126 }
2127 
loadedAtBottom() const2128 bool History::loadedAtBottom() const {
2129 	return _loadedAtBottom;
2130 }
2131 
loadedAtTop() const2132 bool History::loadedAtTop() const {
2133 	return _loadedAtTop;
2134 }
2135 
isReadyFor(MsgId msgId)2136 bool History::isReadyFor(MsgId msgId) {
2137 	if (msgId < 0 && -msgId < ServerMaxMsgId && peer->migrateFrom()) {
2138 		// Old group history.
2139 		return owner().history(peer->migrateFrom()->id)->isReadyFor(-msgId);
2140 	}
2141 
2142 	if (msgId == ShowAtTheEndMsgId) {
2143 		return loadedAtBottom();
2144 	}
2145 	if (msgId == ShowAtUnreadMsgId) {
2146 		if (const auto migratePeer = peer->migrateFrom()) {
2147 			if (const auto migrated = owner().historyLoaded(migratePeer)) {
2148 				if (migrated->unreadCount()) {
2149 					return migrated->isReadyFor(msgId);
2150 				}
2151 			}
2152 		}
2153 		if (unreadCount() && _inboxReadBefore) {
2154 			if (!isEmpty()) {
2155 				return (loadedAtTop() || minMsgId() <= *_inboxReadBefore)
2156 					&& (loadedAtBottom() || maxMsgId() >= *_inboxReadBefore);
2157 			}
2158 			return false;
2159 		}
2160 		return loadedAtBottom();
2161 	}
2162 	const auto item = owner().message(channelId(), msgId);
2163 	return item && (item->history() == this) && item->mainView();
2164 }
2165 
getReadyFor(MsgId msgId)2166 void History::getReadyFor(MsgId msgId) {
2167 	if (msgId < 0 && -msgId < ServerMaxMsgId && peer->migrateFrom()) {
2168 		const auto migrated = owner().history(peer->migrateFrom()->id);
2169 		migrated->getReadyFor(-msgId);
2170 		if (migrated->isEmpty()) {
2171 			clear(ClearType::Unload);
2172 		}
2173 		return;
2174 	}
2175 	if (msgId == ShowAtUnreadMsgId) {
2176 		if (const auto migratePeer = peer->migrateFrom()) {
2177 			if (const auto migrated = owner().historyLoaded(migratePeer)) {
2178 				if (migrated->unreadCount()) {
2179 					clear(ClearType::Unload);
2180 					migrated->getReadyFor(msgId);
2181 					return;
2182 				}
2183 			}
2184 		}
2185 	}
2186 	if (!isReadyFor(msgId)) {
2187 		clear(ClearType::Unload);
2188 		if (const auto migratePeer = peer->migrateFrom()) {
2189 			if (const auto migrated = owner().historyLoaded(migratePeer)) {
2190 				migrated->clear(ClearType::Unload);
2191 			}
2192 		}
2193 		if ((msgId == ShowAtTheEndMsgId)
2194 			|| (msgId == ShowAtUnreadMsgId && !unreadCount())) {
2195 			_loadedAtBottom = true;
2196 		}
2197 	}
2198 }
2199 
setNotLoadedAtBottom()2200 void History::setNotLoadedAtBottom() {
2201 	_loadedAtBottom = false;
2202 
2203 	session().storage().invalidate(
2204 		Storage::SharedMediaInvalidateBottom(peer->id));
2205 }
2206 
clearSharedMedia()2207 void History::clearSharedMedia() {
2208 	session().storage().remove(
2209 		Storage::SharedMediaRemoveAll(peer->id));
2210 }
2211 
setLastServerMessage(HistoryItem * item)2212 void History::setLastServerMessage(HistoryItem *item) {
2213 	_lastServerMessage = item;
2214 	if (_lastMessage
2215 		&& *_lastMessage
2216 		&& !(*_lastMessage)->isRegular()
2217 		&& (!item || (*_lastMessage)->date() > item->date())) {
2218 		return;
2219 	}
2220 	setLastMessage(item);
2221 }
2222 
setLastMessage(HistoryItem * item)2223 void History::setLastMessage(HistoryItem *item) {
2224 	if (_lastMessage && *_lastMessage == item) {
2225 		return;
2226 	}
2227 	_lastMessage = item;
2228 	if (!item || item->isRegular()) {
2229 		_lastServerMessage = item;
2230 	}
2231 	if (peer->migrateTo()) {
2232 		// We don't want to request last message for all deactivated chats.
2233 		// This is a heavy request for them, because we need to get last
2234 		// two items by messages.getHistory to skip the migration message.
2235 		setChatListMessageUnknown();
2236 	} else {
2237 		setChatListMessageFromLast();
2238 		if (!chatListMessageKnown()) {
2239 			setFakeChatListMessage();
2240 		}
2241 	}
2242 }
2243 
refreshChatListMessage()2244 void History::refreshChatListMessage() {
2245 	const auto known = chatListMessageKnown();
2246 	setChatListMessageFromLast();
2247 	if (known && !_chatListMessage) {
2248 		requestChatListMessage();
2249 	}
2250 }
2251 
setChatListMessage(HistoryItem * item)2252 void History::setChatListMessage(HistoryItem *item) {
2253 	if (_chatListMessage && *_chatListMessage == item) {
2254 		return;
2255 	}
2256 	const auto was = _chatListMessage.value_or(nullptr);
2257 	if (item) {
2258 		if (item->isSponsored()) {
2259 			return;
2260 		}
2261 		if (_chatListMessage
2262 			&& *_chatListMessage
2263 			&& !(*_chatListMessage)->isRegular()
2264 			&& (*_chatListMessage)->date() > item->date()) {
2265 			return;
2266 		}
2267 		_chatListMessage = item;
2268 		setChatListTimeId(item->date());
2269 
2270 		// If we have a single message from a group, request the full album.
2271 		if (hasOrphanMediaGroupPart()
2272 			&& !item->toPreview({
2273 				.hideSender = true,
2274 				.hideCaption = true }).images.empty()) {
2275 			owner().histories().requestGroupAround(item);
2276 		}
2277 	} else if (!_chatListMessage || *_chatListMessage) {
2278 		_chatListMessage = nullptr;
2279 		updateChatListEntry();
2280 	}
2281 	if (const auto folder = this->folder()) {
2282 		folder->oneListMessageChanged(was, item);
2283 	}
2284 	if (const auto to = peer->migrateTo()) {
2285 		if (const auto history = owner().historyLoaded(to)) {
2286 			if (!history->chatListMessageKnown()) {
2287 				history->requestChatListMessage();
2288 			}
2289 		}
2290 	}
2291 }
2292 
computeChatListMessageFromLast() const2293 auto History::computeChatListMessageFromLast() const
2294 -> std::optional<HistoryItem*> {
2295 	if (!_lastMessage) {
2296 		return _lastMessage;
2297 	}
2298 
2299 	// In migrated groups we want to skip essential message
2300 	// about migration in the chats list and display the last
2301 	// non-migration message from the original legacy group.
2302 	const auto last = lastMessage();
2303 	if (!last || !last->isGroupMigrate()) {
2304 		return _lastMessage;
2305 	}
2306 	if (const auto chat = peer->asChat()) {
2307 		// In chats we try to take the item before the 'last', which
2308 		// is the empty-displayed migration message.
2309 		if (!loadedAtBottom()) {
2310 			// We don't know the tail of the history.
2311 			return std::nullopt;
2312 		}
2313 		const auto before = [&]() -> HistoryItem* {
2314 			for (const auto &block : ranges::views::reverse(blocks)) {
2315 				const auto &messages = block->messages;
2316 				for (const auto &item : ranges::views::reverse(messages)) {
2317 					if (item->data() != last) {
2318 						return item->data();
2319 					}
2320 				}
2321 			}
2322 			return nullptr;
2323 		}();
2324 		if (before) {
2325 			// We found a message that is not the migration one.
2326 			return before;
2327 		} else if (loadedAtTop()) {
2328 			// No other messages in this history.
2329 			return _lastMessage;
2330 		}
2331 		return std::nullopt;
2332 	} else if (const auto from = migrateFrom()) {
2333 		// In megagroups we just try to use
2334 		// the message from the original group.
2335 		return from->chatListMessageKnown()
2336 			? std::make_optional(from->chatListMessage())
2337 			: std::nullopt;
2338 	}
2339 	return _lastMessage;
2340 }
2341 
setChatListMessageFromLast()2342 void History::setChatListMessageFromLast() {
2343 	if (const auto good = computeChatListMessageFromLast()) {
2344 		setChatListMessage(*good);
2345 	} else {
2346 		setChatListMessageUnknown();
2347 	}
2348 }
2349 
setChatListMessageUnknown()2350 void History::setChatListMessageUnknown() {
2351 	if (!_chatListMessage.has_value()) {
2352 		return;
2353 	}
2354 	const auto was = *_chatListMessage;
2355 	_chatListMessage = std::nullopt;
2356 	if (const auto folder = this->folder()) {
2357 		folder->oneListMessageChanged(was, nullptr);
2358 	}
2359 }
2360 
requestChatListMessage()2361 void History::requestChatListMessage() {
2362 	if (!lastMessageKnown()) {
2363 		owner().histories().requestDialogEntry(this, [=] {
2364 			requestChatListMessage();
2365 		});
2366 		return;
2367 	} else if (chatListMessageKnown()) {
2368 		return;
2369 	}
2370 	setChatListMessageFromLast();
2371 	if (!chatListMessageKnown()) {
2372 		setFakeChatListMessage();
2373 	}
2374 }
2375 
setFakeChatListMessage()2376 void History::setFakeChatListMessage() {
2377 	if (const auto chat = peer->asChat()) {
2378 		// In chats we try to take the item before the 'last', which
2379 		// is the empty-displayed migration message.
2380 		owner().histories().requestFakeChatListMessage(this);
2381 	} else if (const auto from = migrateFrom()) {
2382 		// In megagroups we just try to use
2383 		// the message from the original group.
2384 		from->requestChatListMessage();
2385 	}
2386 }
2387 
setFakeChatListMessageFrom(const MTPmessages_Messages & data)2388 void History::setFakeChatListMessageFrom(const MTPmessages_Messages &data) {
2389 	if (!lastMessageKnown()) {
2390 		requestChatListMessage();
2391 		return;
2392 	}
2393 	const auto finalize = gsl::finally([&] {
2394 		// Make sure that we have chatListMessage when we get out of here.
2395 		if (!chatListMessageKnown()) {
2396 			setChatListMessage(lastMessage());
2397 		}
2398 	});
2399 	const auto last = lastMessage();
2400 	if (!last || !last->isGroupMigrate()) {
2401 		// Last message is good enough.
2402 		return;
2403 	}
2404 	const auto other = data.match([&](
2405 			const MTPDmessages_messagesNotModified &) {
2406 		return static_cast<const MTPMessage*>(nullptr);
2407 	}, [&](const auto &data) {
2408 		for (const auto &message : data.vmessages().v) {
2409 			const auto id = message.match([](const auto &data) {
2410 				return data.vid().v;
2411 			});
2412 			if (id != last->id) {
2413 				return &message;
2414 			}
2415 		}
2416 		return static_cast<const MTPMessage*>(nullptr);
2417 	});
2418 	if (!other) {
2419 		// Other (non equal to the last one) message not found.
2420 		return;
2421 	}
2422 	const auto item = owner().addNewMessage(
2423 		*other,
2424 		MessageFlags(),
2425 		NewMessageType::Existing);
2426 	if (!item || item->isGroupMigrate()) {
2427 		// Not better than the last one.
2428 		return;
2429 	}
2430 	setChatListMessage(item);
2431 }
2432 
applyChatListGroup(ChannelId channelId,const MTPmessages_Messages & data)2433 void History::applyChatListGroup(
2434 		ChannelId channelId,
2435 		const MTPmessages_Messages &data) {
2436 	if (!isEmpty()
2437 		|| !_chatListMessage
2438 		|| !*_chatListMessage
2439 		|| (*_chatListMessage)->history()->channelId() != channelId
2440 		|| (*_chatListMessage)->history() != this
2441 		|| !_lastMessage
2442 		|| !*_lastMessage) {
2443 		return;
2444 	}
2445 	// Apply loaded album as a last slice.
2446 	const auto processMessages = [&](const MTPVector<MTPMessage> &messages) {
2447 		auto items = std::vector<not_null<HistoryItem*>>();
2448 		items.reserve(messages.v.size());
2449 		for (const auto &message : messages.v) {
2450 			const auto id = IdFromMessage(message);
2451 			if (const auto message = owner().message(channelId, id)) {
2452 				items.push_back(message);
2453 			}
2454 		}
2455 		if (!ranges::contains(items, not_null(*_lastMessage))
2456 			|| !ranges::contains(items, not_null(*_chatListMessage))) {
2457 			return;
2458 		}
2459 		_loadedAtBottom = true;
2460 		ranges::sort(items, ranges::less{}, &HistoryItem::id);
2461 		addCreatedOlderSlice(items);
2462 		checkLocalMessages();
2463 		checkLastMessage();
2464 	};
2465 	data.match([&](const MTPDmessages_messagesNotModified &) {
2466 	}, [&](const auto &data) {
2467 		processMessages(data.vmessages());
2468 	});
2469 }
2470 
lastMessage() const2471 HistoryItem *History::lastMessage() const {
2472 	return _lastMessage.value_or(nullptr);
2473 }
2474 
lastMessageKnown() const2475 bool History::lastMessageKnown() const {
2476 	return _lastMessage.has_value();
2477 }
2478 
lastServerMessage() const2479 HistoryItem *History::lastServerMessage() const {
2480 	return _lastServerMessage.value_or(nullptr);
2481 }
2482 
lastServerMessageKnown() const2483 bool History::lastServerMessageKnown() const {
2484 	return _lastServerMessage.has_value();
2485 }
2486 
updateChatListExistence()2487 void History::updateChatListExistence() {
2488 	Entry::updateChatListExistence();
2489 }
2490 
useTopPromotion() const2491 bool History::useTopPromotion() const {
2492 	if (!isTopPromoted()) {
2493 		return false;
2494 	} else if (const auto channel = peer->asChannel()) {
2495 		return !isPinnedDialog(FilterId()) && !channel->amIn();
2496 	} else if (const auto user = peer->asUser()) {
2497 		return !isPinnedDialog(FilterId()) && user->isBot() && isEmpty();
2498 	}
2499 	return false;
2500 }
2501 
fixedOnTopIndex() const2502 int History::fixedOnTopIndex() const {
2503 	return useTopPromotion() ? kTopPromotionFixOnTopIndex : 0;
2504 }
2505 
trackUnreadMessages() const2506 bool History::trackUnreadMessages() const {
2507 	if (const auto channel = peer->asChannel()) {
2508 		return channel->amIn();
2509 	}
2510 	return true;
2511 }
2512 
shouldBeInChatList() const2513 bool History::shouldBeInChatList() const {
2514 	if (peer->migrateTo() || !folderKnown()) {
2515 		return false;
2516 	} else if (isPinnedDialog(FilterId())) {
2517 		return true;
2518 	} else if (const auto channel = peer->asChannel()) {
2519 		if (!channel->amIn()) {
2520 			return isTopPromoted();
2521 		}
2522 	} else if (const auto chat = peer->asChat()) {
2523 		return chat->amIn()
2524 			|| !lastMessageKnown()
2525 			|| (lastMessage() != nullptr);
2526 	} else if (const auto user = peer->asUser()) {
2527 		if (user->isBot() && isTopPromoted()) {
2528 			return true;
2529 		}
2530 	}
2531 	return !lastMessageKnown()
2532 		|| (lastMessage() != nullptr);
2533 }
2534 
unknownMessageDeleted(MsgId messageId)2535 void History::unknownMessageDeleted(MsgId messageId) {
2536 	if (_inboxReadBefore && messageId >= *_inboxReadBefore) {
2537 		owner().histories().requestDialogEntry(this);
2538 	}
2539 }
2540 
isServerSideUnread(not_null<const HistoryItem * > item) const2541 bool History::isServerSideUnread(not_null<const HistoryItem*> item) const {
2542 	Expects(item->isRegular());
2543 
2544 	return item->out()
2545 		? (!_outboxReadBefore || (item->id >= *_outboxReadBefore))
2546 		: (!_inboxReadBefore || (item->id >= *_inboxReadBefore));
2547 }
2548 
applyDialog(Data::Folder * requestFolder,const MTPDdialog & data)2549 void History::applyDialog(
2550 		Data::Folder *requestFolder,
2551 		const MTPDdialog &data) {
2552 	const auto folderId = data.vfolder_id();
2553 	const auto folder = !folderId
2554 		? requestFolder
2555 		: folderId->v
2556 		? owner().folder(folderId->v).get()
2557 		: nullptr;
2558 	applyDialogFields(
2559 		folder,
2560 		data.vunread_count().v,
2561 		data.vread_inbox_max_id().v,
2562 		data.vread_outbox_max_id().v);
2563 	applyDialogTopMessage(data.vtop_message().v);
2564 	setUnreadMark(data.is_unread_mark());
2565 	setUnreadMentionsCount(data.vunread_mentions_count().v);
2566 	if (const auto channel = peer->asChannel()) {
2567 		if (const auto pts = data.vpts()) {
2568 			channel->ptsReceived(pts->v);
2569 		}
2570 		if (!channel->amCreator()) {
2571 			const auto topMessageId = FullMsgId(
2572 				peerToChannel(channel->id),
2573 				data.vtop_message().v);
2574 			if (const auto item = owner().message(topMessageId)) {
2575 				if (item->date() <= channel->date) {
2576 					session().api().requestSelfParticipant(channel);
2577 				}
2578 			}
2579 		}
2580 	}
2581 	owner().applyNotifySetting(
2582 		MTP_notifyPeer(data.vpeer()),
2583 		data.vnotify_settings());
2584 
2585 	const auto draft = data.vdraft();
2586 	if (draft && draft->type() == mtpc_draftMessage) {
2587 		Data::ApplyPeerCloudDraft(
2588 			&session(),
2589 			peer->id,
2590 			draft->c_draftMessage());
2591 	}
2592 	owner().histories().dialogEntryApplied(this);
2593 }
2594 
dialogEntryApplied()2595 void History::dialogEntryApplied() {
2596 	if (!lastServerMessageKnown()) {
2597 		setLastServerMessage(nullptr);
2598 	} else if (!lastMessageKnown()) {
2599 		setLastMessage(nullptr);
2600 	}
2601 	if (peer->migrateTo()) {
2602 		return;
2603 	} else if (!chatListMessageKnown()) {
2604 		requestChatListMessage();
2605 		return;
2606 	}
2607 	if (!chatListMessage()) {
2608 		clear(ClearType::Unload);
2609 		addNewerSlice(QVector<MTPMessage>());
2610 		addOlderSlice(QVector<MTPMessage>());
2611 		if (const auto channel = peer->asChannel()) {
2612 			const auto inviter = channel->inviter;
2613 			if (inviter && channel->amIn()) {
2614 				if (const auto from = owner().userLoaded(inviter)) {
2615 					insertJoinedMessage();
2616 				}
2617 			}
2618 		}
2619 		return;
2620 	}
2621 
2622 	if (chatListTimeId() != 0 && loadedAtBottom()) {
2623 		if (const auto channel = peer->asChannel()) {
2624 			const auto inviter = channel->inviter;
2625 			if (inviter
2626 				&& chatListTimeId() <= channel->inviteDate
2627 				&& channel->amIn()) {
2628 				if (const auto from = owner().userLoaded(inviter)) {
2629 					insertJoinedMessage();
2630 				}
2631 			}
2632 		}
2633 	}
2634 }
2635 
cacheTopPromotion(bool promoted,const QString & type,const QString & message)2636 void History::cacheTopPromotion(
2637 		bool promoted,
2638 		const QString &type,
2639 		const QString &message) {
2640 	const auto changed = (isTopPromoted() != promoted);
2641 	cacheTopPromoted(promoted);
2642 	if (topPromotionType() != type || _topPromotedMessage != message) {
2643 		_topPromotedType = type;
2644 		_topPromotedMessage = message;
2645 		cloudDraftTextCache.clear();
2646 	} else if (changed) {
2647 		cloudDraftTextCache.clear();
2648 	}
2649 }
2650 
topPromotionType() const2651 QStringView History::topPromotionType() const {
2652 	return topPromotionAboutShown()
2653 		? base::StringViewMid(_topPromotedType, 5)
2654 		: QStringView(_topPromotedType);
2655 }
2656 
topPromotionAboutShown() const2657 bool History::topPromotionAboutShown() const {
2658 	return _topPromotedType.startsWith("seen^");
2659 }
2660 
markTopPromotionAboutShown()2661 void History::markTopPromotionAboutShown() {
2662 	if (!topPromotionAboutShown()) {
2663 		_topPromotedType = "seen^" + _topPromotedType;
2664 	}
2665 }
2666 
topPromotionMessage() const2667 QString History::topPromotionMessage() const {
2668 	return _topPromotedMessage;
2669 }
2670 
clearUnreadOnClientSide() const2671 bool History::clearUnreadOnClientSide() const {
2672 	if (!session().supportMode()) {
2673 		return false;
2674 	}
2675 	if (const auto user = peer->asUser()) {
2676 		if (user->isInaccessible()) {
2677 			return true;
2678 		}
2679 	}
2680 	return false;
2681 }
2682 
skipUnreadUpdate() const2683 bool History::skipUnreadUpdate() const {
2684 	return clearUnreadOnClientSide();
2685 }
2686 
applyDialogFields(Data::Folder * folder,int unreadCount,MsgId maxInboxRead,MsgId maxOutboxRead)2687 void History::applyDialogFields(
2688 		Data::Folder *folder,
2689 		int unreadCount,
2690 		MsgId maxInboxRead,
2691 		MsgId maxOutboxRead) {
2692 	if (folder) {
2693 		setFolder(folder);
2694 	} else {
2695 		clearFolder();
2696 	}
2697 	if (!skipUnreadUpdate()
2698 		&& maxInboxRead + 1 >= _inboxReadBefore.value_or(1)) {
2699 		setUnreadCount(unreadCount);
2700 		setInboxReadTill(maxInboxRead);
2701 	}
2702 	setOutboxReadTill(maxOutboxRead);
2703 }
2704 
applyDialogTopMessage(MsgId topMessageId)2705 void History::applyDialogTopMessage(MsgId topMessageId) {
2706 	if (topMessageId) {
2707 		const auto itemId = FullMsgId(
2708 			channelId(),
2709 			topMessageId);
2710 		if (const auto item = owner().message(itemId)) {
2711 			setLastServerMessage(item);
2712 		} else {
2713 			setLastServerMessage(nullptr);
2714 		}
2715 	} else {
2716 		setLastServerMessage(nullptr);
2717 	}
2718 	if (clearUnreadOnClientSide()) {
2719 		setUnreadCount(0);
2720 		if (const auto last = lastMessage()) {
2721 			setInboxReadTill(last->id);
2722 		}
2723 	}
2724 }
2725 
setInboxReadTill(MsgId upTo)2726 void History::setInboxReadTill(MsgId upTo) {
2727 	if (_inboxReadBefore) {
2728 		accumulate_max(*_inboxReadBefore, upTo + 1);
2729 	} else {
2730 		_inboxReadBefore = upTo + 1;
2731 	}
2732 }
2733 
setOutboxReadTill(MsgId upTo)2734 void History::setOutboxReadTill(MsgId upTo) {
2735 	if (_outboxReadBefore) {
2736 		accumulate_max(*_outboxReadBefore, upTo + 1);
2737 	} else {
2738 		_outboxReadBefore = upTo + 1;
2739 	}
2740 }
2741 
minMsgId() const2742 MsgId History::minMsgId() const {
2743 	for (const auto &block : blocks) {
2744 		for (const auto &message : block->messages) {
2745 			const auto item = message->data();
2746 			if (item->isRegular()) {
2747 				return item->id;
2748 			}
2749 		}
2750 	}
2751 	return 0;
2752 }
2753 
maxMsgId() const2754 MsgId History::maxMsgId() const {
2755 	for (const auto &block : ranges::views::reverse(blocks)) {
2756 		for (const auto &message : ranges::views::reverse(block->messages)) {
2757 			const auto item = message->data();
2758 			if (item->isRegular()) {
2759 				return item->id;
2760 			}
2761 		}
2762 	}
2763 	return 0;
2764 }
2765 
msgIdForRead() const2766 MsgId History::msgIdForRead() const {
2767 	const auto last = lastMessage();
2768 	const auto result = (last && last->isRegular())
2769 		? last->id
2770 		: MsgId(0);
2771 	return loadedAtBottom()
2772 		? std::max(result, maxMsgId())
2773 		: result;
2774 }
2775 
lastEditableMessage() const2776 HistoryItem *History::lastEditableMessage() const {
2777 	if (!loadedAtBottom()) {
2778 		return nullptr;
2779 	}
2780 	const auto now = base::unixtime::now();
2781 	for (const auto &block : ranges::views::reverse(blocks)) {
2782 		for (const auto &message : ranges::views::reverse(block->messages)) {
2783 			const auto item = message->data();
2784 			if (item->allowsEdit(now)) {
2785 				return owner().groups().findItemToEdit(item);
2786 			}
2787 		}
2788 	}
2789 	return nullptr;
2790 }
2791 
resizeToWidth(int newWidth)2792 void History::resizeToWidth(int newWidth) {
2793 	const auto resizeAllItems = (_width != newWidth);
2794 
2795 	if (!resizeAllItems && !hasPendingResizedItems()) {
2796 		return;
2797 	}
2798 	_flags &= ~(Flag::f_has_pending_resized_items);
2799 
2800 	_width = newWidth;
2801 	int y = 0;
2802 	for (const auto &block : blocks) {
2803 		block->setY(y);
2804 		y += block->resizeGetHeight(newWidth, resizeAllItems);
2805 	}
2806 	_height = y;
2807 }
2808 
forceFullResize()2809 void History::forceFullResize() {
2810 	_width = 0;
2811 	_flags |= Flag::f_has_pending_resized_items;
2812 }
2813 
channelId() const2814 ChannelId History::channelId() const {
2815 	return peerToChannel(peer->id);
2816 }
2817 
isChannel() const2818 bool History::isChannel() const {
2819 	return peerIsChannel(peer->id);
2820 }
2821 
isMegagroup() const2822 bool History::isMegagroup() const {
2823 	return peer->isMegagroup();
2824 }
2825 
migrateToOrMe() const2826 not_null<History*> History::migrateToOrMe() const {
2827 	if (const auto to = peer->migrateTo()) {
2828 		return owner().history(to);
2829 	}
2830 	// We could get it by owner().history(peer), but we optimize.
2831 	return const_cast<History*>(this);
2832 }
2833 
migrateFrom() const2834 History *History::migrateFrom() const {
2835 	if (const auto from = peer->migrateFrom()) {
2836 		return owner().history(from);
2837 	}
2838 	return nullptr;
2839 }
2840 
rangeForDifferenceRequest() const2841 MsgRange History::rangeForDifferenceRequest() const {
2842 	auto fromId = MsgId(0);
2843 	auto toId = MsgId(0);
2844 	for (const auto &block : blocks) {
2845 		for (const auto &item : block->messages) {
2846 			const auto id = item->data()->id;
2847 			if (id > 0) {
2848 				fromId = id;
2849 				break;
2850 			}
2851 		}
2852 		if (fromId) break;
2853 	}
2854 	if (fromId) {
2855 		for (auto blockIndex = blocks.size(); blockIndex > 0;) {
2856 			const auto &block = blocks[--blockIndex];
2857 			for (auto itemIndex = block->messages.size(); itemIndex > 0;) {
2858 				const auto id = block->messages[--itemIndex]->data()->id;
2859 				if (id > 0) {
2860 					toId = id;
2861 					break;
2862 				}
2863 			}
2864 			if (toId) break;
2865 		}
2866 		return { fromId, toId + 1 };
2867 	}
2868 	return MsgRange();
2869 }
2870 
insertJoinedMessage()2871 HistoryService *History::insertJoinedMessage() {
2872 	const auto channel = peer->asChannel();
2873 	if (!channel
2874 		|| _joinedMessage
2875 		|| !channel->amIn()
2876 		|| (peer->isMegagroup()
2877 			&& channel->mgInfo->joinedMessageFound)) {
2878 		return _joinedMessage;
2879 	}
2880 
2881 	const auto inviter = (channel->inviter.bare > 0)
2882 		? owner().userLoaded(channel->inviter)
2883 		: nullptr;
2884 	if (!inviter) {
2885 		return nullptr;
2886 	}
2887 
2888 	if (peer->isMegagroup()
2889 		&& peer->migrateFrom()
2890 		&& !blocks.empty()
2891 		&& blocks.front()->messages.front()->data()->id == 1) {
2892 		channel->mgInfo->joinedMessageFound = true;
2893 		return nullptr;
2894 	}
2895 
2896 	_joinedMessage = GenerateJoinedMessage(
2897 		this,
2898 		channel->inviteDate,
2899 		inviter,
2900 		channel->inviteViaRequest);
2901 	insertMessageToBlocks(_joinedMessage);
2902 	return _joinedMessage;
2903 }
2904 
insertMessageToBlocks(not_null<HistoryItem * > item)2905 void History::insertMessageToBlocks(not_null<HistoryItem*> item) {
2906 	Expects(item->mainView() == nullptr);
2907 
2908 	if (isEmpty()) {
2909 		addNewToBack(item, false);
2910 		return;
2911 	}
2912 
2913 	const auto itemDate = item->date();
2914 	for (auto blockIndex = blocks.size(); blockIndex > 0;) {
2915 		const auto &block = blocks[--blockIndex];
2916 		for (auto itemIndex = block->messages.size(); itemIndex > 0;) {
2917 			if (block->messages[--itemIndex]->data()->date() <= itemDate) {
2918 				++itemIndex;
2919 				addNewInTheMiddle(item, blockIndex, itemIndex);
2920 				const auto lastDate = chatListTimeId();
2921 				if (!lastDate || itemDate >= lastDate) {
2922 					setLastMessage(item);
2923 				}
2924 				return;
2925 			}
2926 		}
2927 	}
2928 
2929 	startBuildingFrontBlock();
2930 	addItemToBlock(item);
2931 	finishBuildingFrontBlock();
2932 }
2933 
checkLocalMessages()2934 void History::checkLocalMessages() {
2935 	if (isEmpty() && (!loadedAtTop() || !loadedAtBottom())) {
2936 		return;
2937 	}
2938 	const auto firstDate = loadedAtTop()
2939 		? 0
2940 		: blocks.front()->messages.front()->data()->date();
2941 	const auto lastDate = loadedAtBottom()
2942 		? std::numeric_limits<TimeId>::max()
2943 		: blocks.back()->messages.back()->data()->date();
2944 	const auto goodDate = [&](TimeId date) {
2945 		return (date >= firstDate && date < lastDate);
2946 	};
2947 	for (const auto &item : _clientSideMessages) {
2948 		if (!item->mainView() && goodDate(item->date())) {
2949 			insertMessageToBlocks(item);
2950 		}
2951 	}
2952 	if (isChannel()
2953 		&& !_joinedMessage
2954 		&& peer->asChannel()->inviter
2955 		&& goodDate(peer->asChannel()->inviteDate)) {
2956 		insertJoinedMessage();
2957 	}
2958 }
2959 
removeJoinedMessage()2960 void History::removeJoinedMessage() {
2961 	if (_joinedMessage) {
2962 		_joinedMessage->destroy();
2963 	}
2964 }
2965 
isEmpty() const2966 bool History::isEmpty() const {
2967 	return blocks.empty();
2968 }
2969 
isDisplayedEmpty() const2970 bool History::isDisplayedEmpty() const {
2971 	if (!loadedAtTop() || !loadedAtBottom()) {
2972 		return false;
2973 	}
2974 	const auto first = findFirstNonEmpty();
2975 	if (!first) {
2976 		return true;
2977 	}
2978 	const auto chat = peer->asChat();
2979 	if (!chat || !chat->amCreator()) {
2980 		return false;
2981 	}
2982 
2983 	// For legacy chats we want to show the chat with only
2984 	// messages about you creating the group and maybe about you
2985 	// changing the group photo as an empty chat with
2986 	// a nice information about the group features.
2987 	if (nonEmptyCountMoreThan(2)) {
2988 		return false;
2989 	}
2990 	const auto isChangePhoto = [](not_null<HistoryItem*> item) {
2991 		if (const auto media = item->media()) {
2992 			return (media->photo() != nullptr) && item->isService();
2993 		}
2994 		return false;
2995 	};
2996 	const auto last = findLastNonEmpty();
2997 	if (first == last) {
2998 		return first->data()->isGroupEssential()
2999 			|| isChangePhoto(first->data());
3000 	}
3001 	return first->data()->isGroupEssential() && isChangePhoto(last->data());
3002 }
3003 
findFirstNonEmpty() const3004 auto History::findFirstNonEmpty() const -> Element* {
3005 	for (const auto &block : blocks) {
3006 		for (const auto &element : block->messages) {
3007 			if (!element->data()->isEmpty()) {
3008 				return element.get();
3009 			}
3010 		}
3011 	}
3012 	return nullptr;
3013 }
3014 
findFirstDisplayed() const3015 auto History::findFirstDisplayed() const -> Element* {
3016 	for (const auto &block : blocks) {
3017 		for (const auto &element : block->messages) {
3018 			if (!element->data()->isEmpty() && !element->isHidden()) {
3019 				return element.get();
3020 			}
3021 		}
3022 	}
3023 	return nullptr;
3024 }
3025 
findLastNonEmpty() const3026 auto History::findLastNonEmpty() const -> Element* {
3027 	for (const auto &block : ranges::views::reverse(blocks)) {
3028 		for (const auto &element : ranges::views::reverse(block->messages)) {
3029 			if (!element->data()->isEmpty()) {
3030 				return element.get();
3031 			}
3032 		}
3033 	}
3034 	return nullptr;
3035 }
3036 
findLastDisplayed() const3037 auto History::findLastDisplayed() const -> Element* {
3038 	for (const auto &block : ranges::views::reverse(blocks)) {
3039 		for (const auto &element : ranges::views::reverse(block->messages)) {
3040 			if (!element->data()->isEmpty() && !element->isHidden()) {
3041 				return element.get();
3042 			}
3043 		}
3044 	}
3045 	return nullptr;
3046 }
3047 
nonEmptyCountMoreThan(int count) const3048 bool History::nonEmptyCountMoreThan(int count) const {
3049 	Expects(count >= 0);
3050 
3051 	for (const auto &block : blocks) {
3052 		for (const auto &element : block->messages) {
3053 			if (!element->data()->isEmpty()) {
3054 				if (!count--) {
3055 					return true;
3056 				}
3057 			}
3058 		}
3059 	}
3060 	return false;
3061 }
3062 
hasOrphanMediaGroupPart() const3063 bool History::hasOrphanMediaGroupPart() const {
3064 	if (loadedAtTop() || !loadedAtBottom()) {
3065 		return false;
3066 	} else if (blocks.size() != 1) {
3067 		return false;
3068 	} else if (blocks.front()->messages.size() != 1) {
3069 		return false;
3070 	}
3071 	const auto last = blocks.front()->messages.front()->data();
3072 	return last->groupId() != MessageGroupId();
3073 }
3074 
removeOrphanMediaGroupPart()3075 bool History::removeOrphanMediaGroupPart() {
3076 	if (hasOrphanMediaGroupPart()) {
3077 		clear(ClearType::Unload);
3078 		return true;
3079 	}
3080 	return false;
3081 }
3082 
collectMessagesFromUserToDelete(not_null<UserData * > user) const3083 QVector<MsgId> History::collectMessagesFromUserToDelete(
3084 		not_null<UserData*> user) const {
3085 	auto result = QVector<MsgId>();
3086 	for (const auto &block : blocks) {
3087 		for (const auto &message : block->messages) {
3088 			const auto item = message->data();
3089 			if (item->from() == user && item->canDelete()) {
3090 				result.push_back(item->id);
3091 			}
3092 		}
3093 	}
3094 	return result;
3095 }
3096 
clear(ClearType type)3097 void History::clear(ClearType type) {
3098 	_unreadBarView = nullptr;
3099 	_firstUnreadView = nullptr;
3100 	removeJoinedMessage();
3101 
3102 	forgetScrollState();
3103 	blocks.clear();
3104 	owner().notifyHistoryUnloaded(this);
3105 	lastKeyboardInited = false;
3106 	if (type == ClearType::Unload) {
3107 		_loadedAtTop = _loadedAtBottom = false;
3108 	} else {
3109 		// Leave the 'sending' messages in local messages.
3110 		auto local = base::flat_set<not_null<HistoryItem*>>();
3111 		for (const auto &item : _clientSideMessages) {
3112 			if (!item->isSending()) {
3113 				local.emplace(item);
3114 			}
3115 		}
3116 		for (const auto &item : local) {
3117 			item->destroy();
3118 		}
3119 		_notifications.clear();
3120 		owner().notifyHistoryCleared(this);
3121 		if (unreadCountKnown()) {
3122 			setUnreadCount(0);
3123 		}
3124 		if (type == ClearType::DeleteChat) {
3125 			setLastServerMessage(nullptr);
3126 		} else if (_lastMessage && *_lastMessage) {
3127 			if ((*_lastMessage)->isRegular()) {
3128 				(*_lastMessage)->applyEditionToHistoryCleared();
3129 			} else {
3130 				_lastMessage = std::nullopt;
3131 			}
3132 		}
3133 		const auto tillId = (_lastMessage && *_lastMessage)
3134 			? (*_lastMessage)->id
3135 			: std::numeric_limits<MsgId>::max();
3136 		clearUpTill(tillId);
3137 		if (blocks.empty() && _lastMessage && *_lastMessage) {
3138 			addItemToBlock(*_lastMessage);
3139 		}
3140 		_loadedAtTop = _loadedAtBottom = _lastMessage.has_value();
3141 		clearSharedMedia();
3142 		clearLastKeyboard();
3143 	}
3144 
3145 	if (const auto chat = peer->asChat()) {
3146 		chat->lastAuthors.clear();
3147 		chat->markupSenders.clear();
3148 	} else if (const auto channel = peer->asMegagroup()) {
3149 		channel->mgInfo->markupSenders.clear();
3150 	}
3151 
3152 	owner().notifyHistoryChangeDelayed(this);
3153 	owner().sendHistoryChangeNotifications();
3154 }
3155 
clearUpTill(MsgId availableMinId)3156 void History::clearUpTill(MsgId availableMinId) {
3157 	auto remove = std::vector<not_null<HistoryItem*>>();
3158 	remove.reserve(_messages.size());
3159 	for (const auto &item : _messages) {
3160 		const auto itemId = item->id;
3161 		if (!item->isRegular()) {
3162 			continue;
3163 		} else if (itemId == availableMinId) {
3164 			item->applyEditionToHistoryCleared();
3165 		} else if (itemId < availableMinId) {
3166 			remove.push_back(item.get());
3167 		}
3168 	}
3169 	for (const auto item : remove) {
3170 		item->destroy();
3171 	}
3172 	requestChatListMessage();
3173 }
3174 
applyGroupAdminChanges(const base::flat_set<UserId> & changes)3175 void History::applyGroupAdminChanges(const base::flat_set<UserId> &changes) {
3176 	for (const auto &block : blocks) {
3177 		for (const auto &message : block->messages) {
3178 			message->applyGroupAdminChanges(changes);
3179 		}
3180 	}
3181 }
3182 
changedChatListPinHook()3183 void History::changedChatListPinHook() {
3184 	session().changes().historyUpdated(this, UpdateFlag::IsPinned);
3185 }
3186 
removeBlock(not_null<HistoryBlock * > block)3187 void History::removeBlock(not_null<HistoryBlock*> block) {
3188 	Expects(block->messages.empty());
3189 
3190 	if (_buildingFrontBlock && block == _buildingFrontBlock->block) {
3191 		_buildingFrontBlock->block = nullptr;
3192 	}
3193 
3194 	int index = block->indexInHistory();
3195 	blocks.erase(blocks.begin() + index);
3196 	if (index < blocks.size()) {
3197 		for (int i = index, l = blocks.size(); i < l; ++i) {
3198 			blocks[i]->setIndexInHistory(i);
3199 		}
3200 		blocks[index]->messages.front()->previousInBlocksChanged();
3201 	} else if (!blocks.empty() && !blocks.back()->messages.empty()) {
3202 		blocks.back()->messages.back()->nextInBlocksRemoved();
3203 	}
3204 }
3205 
hasPinnedMessages() const3206 bool History::hasPinnedMessages() const {
3207 	return _hasPinnedMessages;
3208 }
3209 
setHasPinnedMessages(bool has)3210 void History::setHasPinnedMessages(bool has) {
3211 	_hasPinnedMessages = has;
3212 	session().changes().historyUpdated(this, UpdateFlag::PinnedMessages);
3213 }
3214 
3215 History::~History() = default;
3216 
HistoryBlock(not_null<History * > history)3217 HistoryBlock::HistoryBlock(not_null<History*> history)
3218 : _history(history) {
3219 }
3220 
resizeGetHeight(int newWidth,bool resizeAllItems)3221 int HistoryBlock::resizeGetHeight(int newWidth, bool resizeAllItems) {
3222 	auto y = 0;
3223 	for (const auto &message : messages) {
3224 		message->setY(y);
3225 		if (resizeAllItems || message->pendingResize()) {
3226 			y += message->resizeGetHeight(newWidth);
3227 		} else {
3228 			y += message->height();
3229 		}
3230 	}
3231 	_height = y;
3232 	return _height;
3233 }
3234 
remove(not_null<Element * > view)3235 void HistoryBlock::remove(not_null<Element*> view) {
3236 	Expects(view->block() == this);
3237 
3238 	_history->mainViewRemoved(this, view);
3239 
3240 	const auto blockIndex = indexInHistory();
3241 	const auto itemIndex = view->indexInBlock();
3242 	const auto item = view->data();
3243 	item->clearMainView();
3244 	messages.erase(messages.begin() + itemIndex);
3245 	for (auto i = itemIndex, l = int(messages.size()); i < l; ++i) {
3246 		messages[i]->setIndexInBlock(i);
3247 	}
3248 	if (messages.empty()) {
3249 		// Deletes this.
3250 		_history->removeBlock(this);
3251 	} else if (itemIndex < messages.size()) {
3252 		messages[itemIndex]->previousInBlocksChanged();
3253 	} else if (blockIndex + 1 < _history->blocks.size()) {
3254 		_history->blocks[blockIndex + 1]->messages.front()->previousInBlocksChanged();
3255 	} else if (!_history->blocks.empty() && !_history->blocks.back()->messages.empty()) {
3256 		_history->blocks.back()->messages.back()->nextInBlocksRemoved();
3257 	}
3258 }
3259 
refreshView(not_null<Element * > view)3260 void HistoryBlock::refreshView(not_null<Element*> view) {
3261 	Expects(view->block() == this);
3262 
3263 	const auto item = view->data();
3264 	auto refreshed = item->createView(
3265 		HistoryInner::ElementDelegate(),
3266 		view);
3267 
3268 	auto blockIndex = indexInHistory();
3269 	auto itemIndex = view->indexInBlock();
3270 	_history->viewReplaced(view, refreshed.get());
3271 
3272 	messages[itemIndex] = std::move(refreshed);
3273 	messages[itemIndex]->attachToBlock(this, itemIndex);
3274 	if (itemIndex + 1 < messages.size()) {
3275 		messages[itemIndex + 1]->previousInBlocksChanged();
3276 	} else if (blockIndex + 1 < _history->blocks.size()) {
3277 		_history->blocks[blockIndex + 1]->messages.front()->previousInBlocksChanged();
3278 	} else if (!_history->blocks.empty() && !_history->blocks.back()->messages.empty()) {
3279 		_history->blocks.back()->messages.back()->nextInBlocksRemoved();
3280 	}
3281 }
3282 
3283 HistoryBlock::~HistoryBlock() = default;
3284