history.cpp 100 KB

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