data_stickers.cpp 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849
  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 "data/stickers/data_stickers.h"
  8. #include "api/api_hash.h"
  9. #include "chat_helpers/compose/compose_show.h"
  10. #include "data/data_document.h"
  11. #include "data/data_session.h"
  12. #include "data/data_user.h"
  13. #include "ui/boxes/confirm_box.h"
  14. #include "ui/text/text_utilities.h"
  15. #include "lang/lang_keys.h"
  16. #include "data/data_premium_limits.h"
  17. #include "boxes/premium_limits_box.h"
  18. #include "history/history.h"
  19. #include "history/history_item.h"
  20. #include "history/history_item_components.h"
  21. #include "apiwrap.h"
  22. #include "storage/storage_account.h"
  23. #include "settings/settings_premium.h"
  24. #include "core/application.h"
  25. #include "core/core_settings.h"
  26. #include "main/main_session.h"
  27. #include "main/main_app_config.h"
  28. #include "mtproto/mtproto_config.h"
  29. #include "ui/toast/toast.h"
  30. #include "ui/image/image_location_factory.h"
  31. #include "window/window_controller.h"
  32. #include "window/window_session_controller.h"
  33. #include "mainwindow.h"
  34. #include "base/unixtime.h"
  35. #include "boxes/abstract_box.h" // Ui::show().
  36. #include "styles/style_chat_helpers.h"
  37. namespace Data {
  38. namespace {
  39. constexpr auto kPremiumToastDuration = 5 * crl::time(1000);
  40. using SetFlag = StickersSetFlag;
  41. [[nodiscard]] TextWithEntities SavedGifsToast(
  42. const Data::PremiumLimits &limits) {
  43. const auto defaultLimit = limits.gifsDefault();
  44. const auto premiumLimit = limits.gifsPremium();
  45. return Ui::Text::Bold(
  46. tr::lng_saved_gif_limit_title(tr::now, lt_count, defaultLimit)
  47. ).append('\n').append(
  48. tr::lng_saved_gif_limit_more(
  49. tr::now,
  50. lt_count,
  51. premiumLimit,
  52. lt_link,
  53. Ui::Text::Link(tr::lng_saved_gif_limit_link(tr::now)),
  54. Ui::Text::WithEntities));
  55. }
  56. [[nodiscard]] TextWithEntities FaveStickersToast(
  57. const Data::PremiumLimits &limits) {
  58. const auto defaultLimit = limits.stickersFavedDefault();
  59. const auto premiumLimit = limits.stickersFavedPremium();
  60. return Ui::Text::Bold(
  61. tr::lng_fave_sticker_limit_title(tr::now, lt_count, defaultLimit)
  62. ).append('\n').append(
  63. tr::lng_fave_sticker_limit_more(
  64. tr::now,
  65. lt_count,
  66. premiumLimit,
  67. lt_link,
  68. Ui::Text::Link(tr::lng_fave_sticker_limit_link(tr::now)),
  69. Ui::Text::WithEntities));
  70. }
  71. void MaybeShowPremiumToast(
  72. std::shared_ptr<ChatHelpers::Show> show,
  73. TextWithEntities text,
  74. const QString &ref) {
  75. if (!show) {
  76. return;
  77. }
  78. const auto session = &show->session();
  79. if (session->user()->isPremium()) {
  80. return;
  81. }
  82. const auto filter = [=](const auto ...) {
  83. if (const auto controller = show->resolveWindow()) {
  84. Settings::ShowPremium(controller, ref);
  85. }
  86. return false;
  87. };
  88. show->showToast({
  89. .text = std::move(text),
  90. .filter = filter,
  91. .duration = kPremiumToastDuration,
  92. });
  93. }
  94. void RemoveFromSet(
  95. StickersSets &sets,
  96. not_null<DocumentData*> document,
  97. uint64 setId) {
  98. const auto it = sets.find(setId);
  99. if (it == sets.end()) {
  100. return;
  101. }
  102. const auto set = it->second.get();
  103. const auto index = set->stickers.indexOf(document);
  104. if (index < 0) {
  105. return;
  106. }
  107. set->stickers.removeAt(index);
  108. if (!set->dates.empty()) {
  109. set->dates.erase(set->dates.begin() + index);
  110. }
  111. for (auto i = set->emoji.begin(); i != set->emoji.end();) {
  112. const auto index = i->second.indexOf(document);
  113. if (index >= 0) {
  114. i->second.removeAt(index);
  115. if (i->second.empty()) {
  116. i = set->emoji.erase(i);
  117. continue;
  118. }
  119. }
  120. ++i;
  121. }
  122. if (set->stickers.empty()) {
  123. sets.erase(it);
  124. }
  125. }
  126. } // namespace
  127. Stickers::Stickers(not_null<Session*> owner) : _owner(owner) {
  128. }
  129. Session &Stickers::owner() const {
  130. return *_owner;
  131. }
  132. Main::Session &Stickers::session() const {
  133. return _owner->session();
  134. }
  135. void Stickers::notifyUpdated(StickersType type) {
  136. _updated.fire_copy(type);
  137. }
  138. rpl::producer<StickersType> Stickers::updated() const {
  139. return _updated.events();
  140. }
  141. rpl::producer<> Stickers::updated(StickersType type) const {
  142. using namespace rpl::mappers;
  143. return updated() | rpl::filter(_1 == type) | rpl::to_empty;
  144. }
  145. void Stickers::notifyRecentUpdated(StickersType type) {
  146. _recentUpdated.fire_copy(type);
  147. }
  148. rpl::producer<StickersType> Stickers::recentUpdated() const {
  149. return _recentUpdated.events();
  150. }
  151. rpl::producer<> Stickers::recentUpdated(StickersType type) const {
  152. using namespace rpl::mappers;
  153. return recentUpdated() | rpl::filter(_1 == type) | rpl::to_empty;
  154. }
  155. void Stickers::notifySavedGifsUpdated() {
  156. _savedGifsUpdated.fire({});
  157. }
  158. rpl::producer<> Stickers::savedGifsUpdated() const {
  159. return _savedGifsUpdated.events();
  160. }
  161. void Stickers::notifyStickerSetInstalled(uint64 setId) {
  162. _stickerSetInstalled.fire(std::move(setId));
  163. }
  164. rpl::producer<uint64> Stickers::stickerSetInstalled() const {
  165. return _stickerSetInstalled.events();
  166. }
  167. void Stickers::notifyEmojiSetInstalled(uint64 setId) {
  168. _emojiSetInstalled.fire(std::move(setId));
  169. }
  170. rpl::producer<uint64> Stickers::emojiSetInstalled() const {
  171. return _emojiSetInstalled.events();
  172. }
  173. void Stickers::incrementSticker(not_null<DocumentData*> document) {
  174. if (!document->sticker() || !document->sticker()->set) {
  175. return;
  176. }
  177. bool writeRecentStickers = false;
  178. auto &sets = setsRef();
  179. auto it = sets.find(Data::Stickers::CloudRecentSetId);
  180. if (it == sets.cend()) {
  181. it = sets.emplace(
  182. Data::Stickers::CloudRecentSetId,
  183. std::make_unique<Data::StickersSet>(
  184. &session().data(),
  185. Data::Stickers::CloudRecentSetId,
  186. uint64(0), // accessHash
  187. uint64(0), // hash
  188. tr::lng_recent_stickers(tr::now),
  189. QString(),
  190. 0, // count
  191. SetFlag::Special,
  192. TimeId(0))).first;
  193. } else {
  194. it->second->title = tr::lng_recent_stickers(tr::now);
  195. }
  196. const auto set = it->second.get();
  197. auto removedFromEmoji = std::vector<not_null<EmojiPtr>>();
  198. auto index = set->stickers.indexOf(document);
  199. if (index > 0) {
  200. if (set->dates.empty()) {
  201. session().api().requestSpecialStickersForce(false, true, false);
  202. } else {
  203. Assert(set->dates.size() == set->stickers.size());
  204. set->dates.erase(set->dates.begin() + index);
  205. }
  206. set->stickers.removeAt(index);
  207. for (auto i = set->emoji.begin(); i != set->emoji.end();) {
  208. if (const auto index = i->second.indexOf(document); index >= 0) {
  209. removedFromEmoji.emplace_back(i->first);
  210. i->second.removeAt(index);
  211. if (i->second.empty()) {
  212. i = set->emoji.erase(i);
  213. continue;
  214. }
  215. }
  216. ++i;
  217. }
  218. }
  219. if (index) {
  220. if (set->dates.size() == set->stickers.size()) {
  221. set->dates.insert(set->dates.begin(), base::unixtime::now());
  222. }
  223. set->stickers.push_front(document);
  224. if (const auto emojiList = getEmojiListFromSet(document)) {
  225. for (const auto &emoji : *emojiList) {
  226. set->emoji[emoji].push_front(document);
  227. }
  228. } else if (!removedFromEmoji.empty()) {
  229. for (const auto emoji : removedFromEmoji) {
  230. set->emoji[emoji].push_front(document);
  231. }
  232. } else {
  233. session().api().requestSpecialStickersForce(false, true, false);
  234. }
  235. writeRecentStickers = true;
  236. }
  237. // Remove that sticker from old recent, now it is in cloud recent stickers.
  238. bool writeOldRecent = false;
  239. auto &recent = getRecentPack();
  240. for (auto i = recent.begin(), e = recent.end(); i != e; ++i) {
  241. if (i->first == document) {
  242. writeOldRecent = true;
  243. recent.erase(i);
  244. break;
  245. }
  246. }
  247. while (!recent.isEmpty()
  248. && (set->stickers.size() + recent.size()
  249. > session().serverConfig().stickersRecentLimit)) {
  250. writeOldRecent = true;
  251. recent.pop_back();
  252. }
  253. if (writeOldRecent) {
  254. session().saveSettings();
  255. }
  256. // Remove that sticker from custom stickers, now it is in cloud recent stickers.
  257. bool writeInstalledStickers = false;
  258. auto customIt = sets.find(Data::Stickers::CustomSetId);
  259. if (customIt != sets.cend()) {
  260. const auto custom = customIt->second.get();
  261. int removeIndex = custom->stickers.indexOf(document);
  262. if (removeIndex >= 0) {
  263. custom->stickers.removeAt(removeIndex);
  264. if (custom->stickers.isEmpty()) {
  265. sets.erase(customIt);
  266. }
  267. writeInstalledStickers = true;
  268. }
  269. }
  270. if (writeInstalledStickers) {
  271. session().local().writeInstalledStickers();
  272. }
  273. if (writeRecentStickers) {
  274. session().local().writeRecentStickers();
  275. }
  276. notifyRecentUpdated(StickersType::Stickers);
  277. }
  278. void Stickers::addSavedGif(
  279. std::shared_ptr<ChatHelpers::Show> show,
  280. not_null<DocumentData*> document) {
  281. const auto index = _savedGifs.indexOf(document);
  282. if (!index) {
  283. return;
  284. }
  285. if (index > 0) {
  286. _savedGifs.remove(index);
  287. }
  288. _savedGifs.push_front(document);
  289. const auto session = &document->session();
  290. const auto limits = Data::PremiumLimits(session);
  291. if (_savedGifs.size() > limits.gifsCurrent()) {
  292. _savedGifs.pop_back();
  293. MaybeShowPremiumToast(
  294. show,
  295. SavedGifsToast(limits),
  296. LimitsPremiumRef("saved_gifs"));
  297. }
  298. session->local().writeSavedGifs();
  299. notifySavedGifsUpdated();
  300. setLastSavedGifsUpdate(0);
  301. session->api().updateSavedGifs();
  302. }
  303. void Stickers::checkSavedGif(not_null<HistoryItem*> item) {
  304. if (item->Has<HistoryMessageForwarded>()
  305. || (!item->out()
  306. && item->history()->peer != session().user())) {
  307. return;
  308. }
  309. if (const auto media = item->media()) {
  310. if (const auto document = media->document()) {
  311. if (document->isGifv()) {
  312. addSavedGif(nullptr, document);
  313. }
  314. }
  315. }
  316. }
  317. void Stickers::applyArchivedResult(
  318. const MTPDmessages_stickerSetInstallResultArchive &d) {
  319. auto &v = d.vsets().v;
  320. StickersSetsOrder archived;
  321. archived.reserve(v.size());
  322. QMap<uint64, uint64> setsToRequest;
  323. auto masksCount = 0;
  324. auto stickersCount = 0;
  325. for (const auto &data : v) {
  326. const auto set = feedSet(data);
  327. if (set->flags & SetFlag::NotLoaded) {
  328. setsToRequest.insert(set->id, set->accessHash);
  329. }
  330. if (set->type() == StickersType::Emoji) {
  331. continue;
  332. }
  333. const auto isMasks = (set->type() == StickersType::Masks);
  334. (isMasks ? masksCount : stickersCount)++;
  335. auto &order = isMasks ? maskSetsOrderRef() : setsOrderRef();
  336. const auto index = order.indexOf(set->id);
  337. if (index >= 0) {
  338. order.removeAt(index);
  339. }
  340. archived.push_back(set->id);
  341. }
  342. if (!setsToRequest.isEmpty()) {
  343. for (auto i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) {
  344. session().api().scheduleStickerSetRequest(i.key(), i.value());
  345. }
  346. session().api().requestStickerSets();
  347. }
  348. if (stickersCount) {
  349. session().local().writeInstalledStickers();
  350. session().local().writeArchivedStickers();
  351. }
  352. if (masksCount) {
  353. session().local().writeInstalledMasks();
  354. session().local().writeArchivedMasks();
  355. }
  356. // TODO async toast.
  357. Ui::Toast::Show(Ui::Toast::Config{
  358. .text = { tr::lng_stickers_packs_archived(tr::now) },
  359. .st = &st::stickersToast,
  360. });
  361. //Ui::show(
  362. // Box<StickersBox>(archived, &session()),
  363. // Ui::LayerOption::KeepOther);
  364. if (stickersCount) {
  365. notifyUpdated(StickersType::Stickers);
  366. }
  367. if (masksCount) {
  368. notifyUpdated(StickersType::Masks);
  369. }
  370. }
  371. void Stickers::installLocally(uint64 setId) {
  372. auto &sets = setsRef();
  373. auto it = sets.find(setId);
  374. if (it == sets.end()) {
  375. return;
  376. }
  377. const auto set = it->second.get();
  378. const auto flags = set->flags;
  379. set->flags &= ~(SetFlag::Archived | SetFlag::Unread);
  380. set->flags |= SetFlag::Installed;
  381. set->installDate = base::unixtime::now();
  382. auto changedFlags = flags ^ set->flags;
  383. const auto isMasks = (set->type() == StickersType::Masks);
  384. const auto isEmoji = (set->type() == StickersType::Emoji);
  385. auto &order = isEmoji
  386. ? emojiSetsOrderRef()
  387. : isMasks
  388. ? maskSetsOrderRef()
  389. : setsOrderRef();
  390. int insertAtIndex = 0, currentIndex = order.indexOf(setId);
  391. if (currentIndex != insertAtIndex) {
  392. if (currentIndex > 0) {
  393. order.removeAt(currentIndex);
  394. }
  395. order.insert(insertAtIndex, setId);
  396. }
  397. auto customIt = sets.find(CustomSetId);
  398. if (customIt != sets.cend()) {
  399. const auto custom = customIt->second.get();
  400. for (const auto sticker : std::as_const(set->stickers)) {
  401. int removeIndex = custom->stickers.indexOf(sticker);
  402. if (removeIndex >= 0) custom->stickers.removeAt(removeIndex);
  403. }
  404. if (custom->stickers.isEmpty()) {
  405. sets.erase(customIt);
  406. }
  407. }
  408. session().local().writeInstalledStickers();
  409. if (!isMasks && (changedFlags & SetFlag::Unread)) {
  410. if (isEmoji) {
  411. session().local().writeFeaturedCustomEmoji();
  412. } else {
  413. session().local().writeFeaturedStickers();
  414. }
  415. }
  416. if (!isEmoji && (changedFlags & SetFlag::Archived)) {
  417. auto &archivedOrder = isMasks
  418. ? archivedMaskSetsOrderRef()
  419. : archivedSetsOrderRef();
  420. const auto index = archivedOrder.indexOf(setId);
  421. if (index >= 0) {
  422. archivedOrder.removeAt(index);
  423. if (isMasks) {
  424. session().local().writeArchivedMasks();
  425. } else {
  426. session().local().writeArchivedStickers();
  427. }
  428. }
  429. }
  430. notifyUpdated(set->type());
  431. }
  432. void Stickers::undoInstallLocally(uint64 setId) {
  433. const auto &sets = this->sets();
  434. const auto it = sets.find(setId);
  435. if (it == sets.end()) {
  436. return;
  437. }
  438. const auto set = it->second.get();
  439. set->flags &= ~SetFlag::Installed;
  440. set->installDate = TimeId(0);
  441. auto &order = setsOrderRef();
  442. int currentIndex = order.indexOf(setId);
  443. if (currentIndex >= 0) {
  444. order.removeAt(currentIndex);
  445. }
  446. session().local().writeInstalledStickers();
  447. notifyUpdated(set->type());
  448. Ui::show(
  449. Ui::MakeInformBox(tr::lng_stickers_not_found()),
  450. Ui::LayerOption::KeepOther);
  451. }
  452. bool Stickers::isFaved(not_null<const DocumentData*> document) const {
  453. const auto &sets = this->sets();
  454. const auto it = sets.find(FavedSetId);
  455. if (it == sets.cend()) {
  456. return false;
  457. }
  458. for (const auto sticker : std::as_const(it->second->stickers)) {
  459. if (sticker == document) {
  460. return true;
  461. }
  462. }
  463. return false;
  464. }
  465. void Stickers::checkFavedLimit(
  466. StickersSet &set,
  467. std::shared_ptr<ChatHelpers::Show> show) {
  468. const auto session = &_owner->session();
  469. const auto limits = Data::PremiumLimits(session);
  470. if (set.stickers.size() <= limits.stickersFavedCurrent()) {
  471. return;
  472. }
  473. auto removing = set.stickers.back();
  474. set.stickers.pop_back();
  475. for (auto i = set.emoji.begin(); i != set.emoji.end();) {
  476. auto index = i->second.indexOf(removing);
  477. if (index >= 0) {
  478. i->second.removeAt(index);
  479. if (i->second.empty()) {
  480. i = set.emoji.erase(i);
  481. continue;
  482. }
  483. }
  484. ++i;
  485. }
  486. MaybeShowPremiumToast(
  487. std::move(show),
  488. FaveStickersToast(limits),
  489. LimitsPremiumRef("stickers_faved"));
  490. }
  491. void Stickers::pushFavedToFront(
  492. StickersSet &set,
  493. std::shared_ptr<ChatHelpers::Show> show,
  494. not_null<DocumentData*> document,
  495. const std::vector<not_null<EmojiPtr>> &emojiList) {
  496. set.stickers.push_front(document);
  497. for (auto emoji : emojiList) {
  498. set.emoji[emoji].push_front(document);
  499. }
  500. checkFavedLimit(set, std::move(show));
  501. }
  502. void Stickers::moveFavedToFront(StickersSet &set, int index) {
  503. Expects(index > 0 && index < set.stickers.size());
  504. auto document = set.stickers[index];
  505. while (index-- != 0) {
  506. set.stickers[index + 1] = set.stickers[index];
  507. }
  508. set.stickers[0] = document;
  509. for (auto &[emoji, list] : set.emoji) {
  510. auto index = list.indexOf(document);
  511. if (index > 0) {
  512. while (index-- != 0) {
  513. list[index + 1] = list[index];
  514. }
  515. list[0] = document;
  516. }
  517. }
  518. }
  519. void Stickers::setIsFaved(
  520. std::shared_ptr<ChatHelpers::Show> show,
  521. not_null<DocumentData*> document,
  522. std::optional<std::vector<not_null<EmojiPtr>>> emojiList) {
  523. auto &sets = setsRef();
  524. auto it = sets.find(FavedSetId);
  525. if (it == sets.end()) {
  526. it = sets.emplace(FavedSetId, std::make_unique<StickersSet>(
  527. &document->owner(),
  528. FavedSetId,
  529. uint64(0), // accessHash
  530. uint64(0), // hash
  531. Lang::Hard::FavedSetTitle(),
  532. QString(),
  533. 0, // count
  534. SetFlag::Special,
  535. TimeId(0))).first;
  536. }
  537. const auto set = it->second.get();
  538. auto index = set->stickers.indexOf(document);
  539. if (index == 0) {
  540. return;
  541. }
  542. if (index > 0) {
  543. moveFavedToFront(*set, index);
  544. } else if (emojiList) {
  545. pushFavedToFront(*set, show, document, *emojiList);
  546. } else if (auto list = getEmojiListFromSet(document)) {
  547. pushFavedToFront(*set, show, document, *list);
  548. } else {
  549. requestSetToPushFaved(show, document);
  550. return;
  551. }
  552. session().local().writeFavedStickers();
  553. notifyUpdated(StickersType::Stickers);
  554. notifyStickerSetInstalled(FavedSetId);
  555. }
  556. void Stickers::requestSetToPushFaved(
  557. std::shared_ptr<ChatHelpers::Show> show,
  558. not_null<DocumentData*> document) {
  559. auto addAnyway = [=](std::vector<not_null<EmojiPtr>> list) {
  560. if (list.empty()) {
  561. if (auto sticker = document->sticker()) {
  562. if (auto emoji = Ui::Emoji::Find(sticker->alt)) {
  563. list.push_back(emoji);
  564. }
  565. }
  566. }
  567. setIsFaved(nullptr, document, std::move(list));
  568. };
  569. session().api().request(MTPmessages_GetStickerSet(
  570. Data::InputStickerSet(document->sticker()->set),
  571. MTP_int(0) // hash
  572. )).done([=](const MTPmessages_StickerSet &result) {
  573. result.match([&](const MTPDmessages_stickerSet &data) {
  574. auto list = std::vector<not_null<EmojiPtr>>();
  575. list.reserve(data.vpacks().v.size());
  576. for (const auto &mtpPack : data.vpacks().v) {
  577. auto &pack = mtpPack.c_stickerPack();
  578. for (const auto &documentId : pack.vdocuments().v) {
  579. if (documentId.v == document->id) {
  580. if (const auto emoji = Ui::Emoji::Find(qs(mtpPack.c_stickerPack().vemoticon()))) {
  581. list.emplace_back(emoji);
  582. }
  583. break;
  584. }
  585. }
  586. }
  587. addAnyway(std::move(list));
  588. }, [](const MTPDmessages_stickerSetNotModified &) {
  589. LOG(("API Error: Unexpected messages.stickerSetNotModified."));
  590. });
  591. }).fail([=] {
  592. // Perhaps this is a deleted sticker pack. Add anyway.
  593. addAnyway({});
  594. }).send();
  595. }
  596. void Stickers::removeFromRecentSet(not_null<DocumentData*> document) {
  597. RemoveFromSet(setsRef(), document, CloudRecentSetId);
  598. session().local().writeRecentStickers();
  599. notifyRecentUpdated(StickersType::Stickers);
  600. }
  601. void Stickers::setIsNotFaved(not_null<DocumentData*> document) {
  602. RemoveFromSet(setsRef(), document, FavedSetId);
  603. session().local().writeFavedStickers();
  604. notifyUpdated(StickersType::Stickers);
  605. }
  606. void Stickers::setFaved(
  607. std::shared_ptr<ChatHelpers::Show> show,
  608. not_null<DocumentData*> document,
  609. bool faved) {
  610. if (faved) {
  611. setIsFaved(std::move(show), document);
  612. } else {
  613. setIsNotFaved(document);
  614. }
  615. }
  616. void Stickers::setsReceived(
  617. const QVector<MTPStickerSet> &data,
  618. uint64 hash) {
  619. somethingReceived(data, hash, StickersType::Stickers);
  620. }
  621. void Stickers::masksReceived(
  622. const QVector<MTPStickerSet> &data,
  623. uint64 hash) {
  624. somethingReceived(data, hash, StickersType::Masks);
  625. }
  626. void Stickers::emojiReceived(
  627. const QVector<MTPStickerSet> &data,
  628. uint64 hash) {
  629. somethingReceived(data, hash, StickersType::Emoji);
  630. }
  631. void Stickers::somethingReceived(
  632. const QVector<MTPStickerSet> &list,
  633. uint64 hash,
  634. StickersType type) {
  635. auto &setsOrder = (type == StickersType::Emoji)
  636. ? emojiSetsOrderRef()
  637. : (type == StickersType::Masks)
  638. ? maskSetsOrderRef()
  639. : setsOrderRef();
  640. setsOrder.clear();
  641. auto &sets = setsRef();
  642. QMap<uint64, uint64> setsToRequest;
  643. for (auto &[id, set] : sets) {
  644. const auto archived = !!(set->flags & SetFlag::Archived);
  645. if (!archived && (type == set->type())) {
  646. // Mark for removing.
  647. set->flags &= ~SetFlag::Installed;
  648. set->installDate = 0;
  649. }
  650. }
  651. for (const auto &info : list) {
  652. const auto set = feedSet(info);
  653. if (!(set->flags & SetFlag::Archived)
  654. || (set->flags & SetFlag::Official)) {
  655. setsOrder.push_back(set->id);
  656. if (set->stickers.isEmpty()
  657. || (set->flags & SetFlag::NotLoaded)) {
  658. setsToRequest.insert(set->id, set->accessHash);
  659. }
  660. }
  661. }
  662. auto writeRecent = false;
  663. auto &recent = getRecentPack();
  664. for (auto it = sets.begin(); it != sets.end();) {
  665. const auto set = it->second.get();
  666. const auto installed = !!(set->flags & SetFlag::Installed);
  667. const auto featured = !!(set->flags & SetFlag::Featured);
  668. const auto special = !!(set->flags & SetFlag::Special);
  669. const auto archived = !!(set->flags & SetFlag::Archived);
  670. const auto emoji = !!(set->flags & SetFlag::Emoji);
  671. const auto locked = (set->locked > 0);
  672. if (!installed) { // remove not mine sets from recent stickers
  673. for (auto i = recent.begin(); i != recent.cend();) {
  674. if (set->stickers.indexOf(i->first) >= 0) {
  675. i = recent.erase(i);
  676. writeRecent = true;
  677. } else {
  678. ++i;
  679. }
  680. }
  681. }
  682. if (installed || featured || special || archived || emoji || locked) {
  683. ++it;
  684. } else {
  685. it = sets.erase(it);
  686. }
  687. }
  688. if (!setsToRequest.isEmpty()) {
  689. auto &api = session().api();
  690. for (auto i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) {
  691. api.scheduleStickerSetRequest(i.key(), i.value());
  692. }
  693. api.requestStickerSets();
  694. }
  695. if (type == StickersType::Emoji) {
  696. session().local().writeInstalledCustomEmoji();
  697. } else if (type == StickersType::Masks) {
  698. session().local().writeInstalledMasks();
  699. } else {
  700. session().local().writeInstalledStickers();
  701. }
  702. if (writeRecent) {
  703. session().saveSettings();
  704. }
  705. const auto counted = (type == StickersType::Emoji)
  706. ? Api::CountCustomEmojiHash(&session())
  707. : (type == StickersType::Masks)
  708. ? Api::CountMasksHash(&session())
  709. : Api::CountStickersHash(&session());
  710. if (counted != hash) {
  711. LOG(("API Error: received %1 hash %2 while counted hash is %3"
  712. ).arg((type == StickersType::Emoji)
  713. ? "custom-emoji"
  714. : (type == StickersType::Masks)
  715. ? "masks"
  716. : "stickers"
  717. ).arg(hash
  718. ).arg(counted));
  719. }
  720. notifyUpdated(type);
  721. }
  722. void Stickers::setPackAndEmoji(
  723. StickersSet &set,
  724. StickersPack &&pack,
  725. const std::vector<TimeId> &&dates,
  726. const QVector<MTPStickerPack> &packs) {
  727. set.stickers = std::move(pack);
  728. set.dates = std::move(dates);
  729. set.emoji.clear();
  730. for (const auto &mtpPack : packs) {
  731. Assert(mtpPack.type() == mtpc_stickerPack);
  732. auto &pack = mtpPack.c_stickerPack();
  733. if (auto emoji = Ui::Emoji::Find(qs(pack.vemoticon()))) {
  734. emoji = emoji->original();
  735. auto &stickers = pack.vdocuments().v;
  736. auto p = StickersPack();
  737. p.reserve(stickers.size());
  738. for (auto j = 0, c = int(stickers.size()); j != c; ++j) {
  739. auto document = owner().document(stickers[j].v);
  740. if (!document || !document->sticker()) continue;
  741. p.push_back(document);
  742. }
  743. set.emoji[emoji] = std::move(p);
  744. }
  745. }
  746. }
  747. not_null<StickersSet*> Stickers::collectibleSet() {
  748. const auto setId = CollectibleSetId;
  749. auto &sets = setsRef();
  750. auto it = sets.find(setId);
  751. if (it == sets.cend()) {
  752. it = sets.emplace(setId, std::make_unique<StickersSet>(
  753. &owner(),
  754. setId,
  755. uint64(0), // accessHash
  756. uint64(0), // hash
  757. tr::lng_collectible_emoji(tr::now),
  758. QString(),
  759. 0, // count
  760. SetFlag::Special,
  761. TimeId(0))).first;
  762. }
  763. return it->second.get();
  764. }
  765. void Stickers::specialSetReceived(
  766. uint64 setId,
  767. const QString &setTitle,
  768. const QVector<MTPDocument> &items,
  769. uint64 hash,
  770. const QVector<MTPStickerPack> &packs,
  771. const QVector<MTPint> &usageDates) {
  772. auto &sets = setsRef();
  773. auto it = sets.find(setId);
  774. if (items.isEmpty()) {
  775. if (it != sets.cend()) {
  776. sets.erase(it);
  777. }
  778. } else {
  779. if (it == sets.cend()) {
  780. it = sets.emplace(setId, std::make_unique<StickersSet>(
  781. &owner(),
  782. setId,
  783. uint64(0), // accessHash
  784. uint64(0), // hash
  785. setTitle,
  786. QString(),
  787. 0, // count
  788. SetFlag::Special,
  789. TimeId(0))).first;
  790. } else {
  791. it->second->title = setTitle;
  792. }
  793. const auto set = it->second.get();
  794. set->hash = hash;
  795. auto dates = std::vector<TimeId>();
  796. auto dateIndex = 0;
  797. auto datesAvailable = (items.size() == usageDates.size())
  798. && ((setId == CloudRecentSetId)
  799. || (setId == CloudRecentAttachedSetId));
  800. auto customIt = sets.find(CustomSetId);
  801. auto pack = StickersPack();
  802. pack.reserve(items.size());
  803. for (const auto &item : items) {
  804. ++dateIndex;
  805. const auto document = owner().processDocument(item);
  806. if (!document->sticker()) {
  807. continue;
  808. }
  809. pack.push_back(document);
  810. if (datesAvailable) {
  811. dates.push_back(TimeId(usageDates[dateIndex - 1].v));
  812. }
  813. if (customIt != sets.cend()) {
  814. const auto custom = customIt->second.get();
  815. auto index = custom->stickers.indexOf(document);
  816. if (index >= 0) {
  817. custom->stickers.removeAt(index);
  818. }
  819. }
  820. }
  821. if (customIt != sets.cend()
  822. && customIt->second->stickers.isEmpty()) {
  823. sets.erase(customIt);
  824. customIt = sets.end();
  825. }
  826. auto writeRecent = false;
  827. auto &recent = getRecentPack();
  828. for (auto i = recent.begin(); i != recent.cend();) {
  829. if (set->stickers.indexOf(i->first) >= 0 && pack.indexOf(i->first) < 0) {
  830. i = recent.erase(i);
  831. writeRecent = true;
  832. } else {
  833. ++i;
  834. }
  835. }
  836. if (pack.isEmpty()) {
  837. sets.erase(it);
  838. } else {
  839. setPackAndEmoji(*set, std::move(pack), std::move(dates), packs);
  840. }
  841. if (writeRecent) {
  842. session().saveSettings();
  843. }
  844. }
  845. switch (setId) {
  846. case CloudRecentSetId: {
  847. const auto counted = Api::CountRecentStickersHash(&session());
  848. if (counted != hash) {
  849. LOG(("API Error: "
  850. "received recent stickers hash %1 while counted hash is %2"
  851. ).arg(hash
  852. ).arg(counted));
  853. }
  854. session().local().writeRecentStickers();
  855. } break;
  856. case CloudRecentAttachedSetId: {
  857. const auto counted = Api::CountRecentStickersHash(&session(), true);
  858. if (counted != hash) {
  859. LOG(("API Error: "
  860. "received recent attached stickers hash %1 "
  861. "while counted hash is %2"
  862. ).arg(hash
  863. ).arg(counted));
  864. }
  865. session().local().writeRecentMasks();
  866. } break;
  867. case FavedSetId: {
  868. const auto counted = Api::CountFavedStickersHash(&session());
  869. if (counted != hash) {
  870. LOG(("API Error: "
  871. "received faved stickers hash %1 while counted hash is %2"
  872. ).arg(hash
  873. ).arg(counted));
  874. }
  875. session().local().writeFavedStickers();
  876. } break;
  877. default: Unexpected("setId in SpecialSetReceived()");
  878. }
  879. notifyUpdated((setId == CloudRecentAttachedSetId)
  880. ? StickersType::Masks
  881. : StickersType::Stickers);
  882. }
  883. void Stickers::featuredSetsReceived(
  884. const MTPmessages_FeaturedStickers &result) {
  885. setLastFeaturedUpdate(crl::now());
  886. result.match([](const MTPDmessages_featuredStickersNotModified &) {
  887. }, [&](const MTPDmessages_featuredStickers &data) {
  888. featuredReceived(data, StickersType::Stickers);
  889. });
  890. }
  891. void Stickers::featuredEmojiSetsReceived(
  892. const MTPmessages_FeaturedStickers &result) {
  893. setLastFeaturedEmojiUpdate(crl::now());
  894. result.match([](const MTPDmessages_featuredStickersNotModified &) {
  895. }, [&](const MTPDmessages_featuredStickers &data) {
  896. featuredReceived(data, StickersType::Emoji);
  897. });
  898. }
  899. void Stickers::featuredReceived(
  900. const MTPDmessages_featuredStickers &data,
  901. StickersType type) {
  902. const auto &list = data.vsets().v;
  903. const auto &unread = data.vunread().v;
  904. const auto hash = data.vhash().v;
  905. auto &&unreadIds = ranges::views::all(
  906. unread
  907. ) | ranges::views::transform(&MTPlong::v);
  908. const auto unreadMap = base::flat_set<uint64>{
  909. unreadIds.begin(),
  910. unreadIds.end()
  911. };
  912. const auto isEmoji = (type == StickersType::Emoji);
  913. auto &featuredOrder = isEmoji
  914. ? featuredEmojiSetsOrderRef()
  915. : featuredSetsOrderRef();
  916. featuredOrder.clear();
  917. auto &sets = setsRef();
  918. auto setsToRequest = base::flat_map<uint64, uint64>();
  919. for (auto &[id, set] : sets) {
  920. // Mark for removing.
  921. if (set->type() == type) {
  922. set->flags &= ~SetFlag::Featured;
  923. }
  924. }
  925. for (const auto &entry : list) {
  926. const auto data = entry.match([&](const auto &data) {
  927. return data.vset().match([&](const MTPDstickerSet &data) {
  928. return &data;
  929. });
  930. });
  931. auto it = sets.find(data->vid().v);
  932. const auto title = getSetTitle(*data);
  933. const auto installDate = data->vinstalled_date().value_or_empty();
  934. auto thumbnailType = StickerType::Webp;
  935. const auto thumbnail = [&] {
  936. if (const auto thumbs = data->vthumbs()) {
  937. for (const auto &thumb : thumbs->v) {
  938. const auto result = Images::FromPhotoSize(
  939. &session(),
  940. *data,
  941. thumb);
  942. if (result.location.valid()) {
  943. thumbnailType = ThumbnailTypeFromPhotoSize(thumb);
  944. return result;
  945. }
  946. }
  947. }
  948. return ImageWithLocation();
  949. }();
  950. const auto setId = data->vid().v;
  951. const auto flags = SetFlag::Featured
  952. | (unreadMap.contains(setId) ? SetFlag::Unread : SetFlag())
  953. | ParseStickersSetFlags(*data);
  954. if (it == sets.cend()) {
  955. it = sets.emplace(data->vid().v, std::make_unique<StickersSet>(
  956. &owner(),
  957. setId,
  958. data->vaccess_hash().v,
  959. data->vhash().v,
  960. title,
  961. qs(data->vshort_name()),
  962. data->vcount().v,
  963. flags | SetFlag::NotLoaded,
  964. installDate)).first;
  965. } else {
  966. const auto set = it->second.get();
  967. set->accessHash = data->vaccess_hash().v;
  968. set->title = title;
  969. set->shortName = qs(data->vshort_name());
  970. set->flags = flags
  971. | (set->flags & (SetFlag::NotLoaded | SetFlag::Special));
  972. set->installDate = installDate;
  973. if (set->count != data->vcount().v || set->hash != data->vhash().v || set->emoji.empty()) {
  974. set->count = data->vcount().v;
  975. set->hash = data->vhash().v;
  976. set->flags |= SetFlag::NotLoaded; // need to request this set
  977. }
  978. }
  979. it->second->setThumbnail(thumbnail, thumbnailType);
  980. it->second->thumbnailDocumentId = data->vthumb_document_id().value_or_empty();
  981. featuredOrder.push_back(data->vid().v);
  982. if (it->second->stickers.isEmpty()
  983. || (it->second->flags & SetFlag::NotLoaded)) {
  984. setsToRequest.emplace(data->vid().v, data->vaccess_hash().v);
  985. }
  986. }
  987. auto unreadCount = 0;
  988. for (auto it = sets.begin(); it != sets.end();) {
  989. const auto set = it->second.get();
  990. const auto installed = (set->flags & SetFlag::Installed);
  991. const auto featured = (set->flags & SetFlag::Featured);
  992. const auto special = (set->flags & SetFlag::Special);
  993. const auto archived = (set->flags & SetFlag::Archived);
  994. const auto emoji = !!(set->flags & SetFlag::Emoji);
  995. const auto locked = (set->locked > 0);
  996. if (installed || featured || special || archived || emoji || locked) {
  997. if (featured && (set->flags & SetFlag::Unread)) {
  998. if (!(set->flags & SetFlag::Emoji)) {
  999. ++unreadCount;
  1000. }
  1001. }
  1002. ++it;
  1003. } else {
  1004. it = sets.erase(it);
  1005. }
  1006. }
  1007. setFeaturedSetsUnreadCount(unreadCount);
  1008. const auto counted = isEmoji
  1009. ? Api::CountFeaturedEmojiHash(&session())
  1010. : Api::CountFeaturedStickersHash(&session());
  1011. if (counted != hash) {
  1012. LOG(("API Error: "
  1013. "received featured stickers hash %1 while counted hash is %2"
  1014. ).arg(hash
  1015. ).arg(counted));
  1016. }
  1017. if (!setsToRequest.empty()) {
  1018. auto &api = session().api();
  1019. for (const auto &[setId, accessHash] : setsToRequest) {
  1020. api.scheduleStickerSetRequest(setId, accessHash);
  1021. }
  1022. api.requestStickerSets();
  1023. }
  1024. if (isEmoji) {
  1025. session().local().writeFeaturedCustomEmoji();
  1026. } else {
  1027. session().local().writeFeaturedStickers();
  1028. }
  1029. notifyUpdated(type);
  1030. }
  1031. void Stickers::gifsReceived(const QVector<MTPDocument> &items, uint64 hash) {
  1032. auto &saved = savedGifsRef();
  1033. saved.clear();
  1034. saved.reserve(items.size());
  1035. for (const auto &item : items) {
  1036. const auto document = owner().processDocument(item);
  1037. if (!document->isGifv()) {
  1038. LOG(("API Error: "
  1039. "bad document returned in Stickers::gifsReceived!"));
  1040. continue;
  1041. }
  1042. saved.push_back(document);
  1043. }
  1044. const auto counted = Api::CountSavedGifsHash(&session());
  1045. if (counted != hash) {
  1046. LOG(("API Error: "
  1047. "received saved gifs hash %1 while counted hash is %2"
  1048. ).arg(hash
  1049. ).arg(counted));
  1050. }
  1051. session().local().writeSavedGifs();
  1052. notifySavedGifsUpdated();
  1053. }
  1054. std::vector<not_null<DocumentData*>> Stickers::getPremiumList(uint64 seed) {
  1055. struct StickerWithDate {
  1056. not_null<DocumentData*> document;
  1057. TimeId date = 0;
  1058. };
  1059. auto result = std::vector<StickerWithDate>();
  1060. auto &sets = setsRef();
  1061. auto setsToRequest = base::flat_map<uint64, uint64>();
  1062. const auto add = [&](not_null<DocumentData*> document, TimeId date) {
  1063. if (ranges::find(result, document, [](const StickerWithDate &data) {
  1064. return data.document;
  1065. }) == result.end()) {
  1066. result.push_back({ document, date });
  1067. }
  1068. };
  1069. constexpr auto kSlice = 65536;
  1070. const auto CreateSortKey = [&](
  1071. not_null<DocumentData*> document,
  1072. int base) {
  1073. if (document->sticker() && document->sticker()->isAnimated()) {
  1074. base += kSlice;
  1075. }
  1076. return TimeId(base + int((document->id ^ seed) % kSlice));
  1077. };
  1078. const auto CreateRecentSortKey = [&](not_null<DocumentData*> document) {
  1079. return CreateSortKey(document, kSlice * 6);
  1080. };
  1081. auto myCounter = 0;
  1082. const auto CreateMySortKey = [&](not_null<DocumentData*> document) {
  1083. auto base = kSlice * 6;
  1084. if (!document->sticker() || !document->sticker()->isAnimated()) {
  1085. base -= kSlice;
  1086. }
  1087. return (base - (++myCounter));
  1088. };
  1089. const auto CreateFeaturedSortKey = [&](not_null<DocumentData*> document) {
  1090. return CreateSortKey(document, kSlice * 2);
  1091. };
  1092. const auto InstallDateAdjusted = [&](
  1093. TimeId date,
  1094. not_null<DocumentData*> document) {
  1095. return (document->sticker() && document->sticker()->isAnimated())
  1096. ? date
  1097. : date / 2;
  1098. };
  1099. const auto RecentInstallDate = [&](not_null<DocumentData*> document) {
  1100. Expects(document->sticker() != nullptr);
  1101. const auto sticker = document->sticker();
  1102. if (sticker->set.id) {
  1103. const auto setIt = sets.find(sticker->set.id);
  1104. if (setIt != sets.end()) {
  1105. return InstallDateAdjusted(setIt->second->installDate, document);
  1106. }
  1107. }
  1108. return TimeId(0);
  1109. };
  1110. auto recentIt = sets.find(Stickers::CloudRecentSetId);
  1111. if (recentIt != sets.cend()) {
  1112. const auto recent = recentIt->second.get();
  1113. const auto count = int(recent->stickers.size());
  1114. result.reserve(count);
  1115. for (auto i = 0; i != count; ++i) {
  1116. const auto document = recent->stickers[i];
  1117. auto index = i;
  1118. if (!document->isPremiumSticker()) {
  1119. continue;
  1120. } else {
  1121. index = recent->stickers.indexOf(document);
  1122. }
  1123. const auto usageDate = (recent->dates.empty() || index < 0)
  1124. ? 0
  1125. : recent->dates[index];
  1126. const auto date = usageDate
  1127. ? usageDate
  1128. : RecentInstallDate(document);
  1129. result.push_back({
  1130. document,
  1131. date ? date : CreateRecentSortKey(document) });
  1132. }
  1133. }
  1134. const auto addList = [&](
  1135. const StickersSetsOrder &order,
  1136. SetFlag skip) {
  1137. for (const auto setId : order) {
  1138. auto it = sets.find(setId);
  1139. if (it == sets.cend() || (it->second->flags & skip)) {
  1140. continue;
  1141. }
  1142. const auto set = it->second.get();
  1143. if (set->emoji.empty()) {
  1144. setsToRequest.emplace(set->id, set->accessHash);
  1145. set->flags |= SetFlag::NotLoaded;
  1146. continue;
  1147. }
  1148. const auto my = (set->flags & SetFlag::Installed);
  1149. result.reserve(result.size() + set->stickers.size());
  1150. for (const auto document : set->stickers) {
  1151. if (!document->isPremiumSticker()) {
  1152. continue;
  1153. }
  1154. const auto installDate = my ? set->installDate : TimeId(0);
  1155. const auto date = (installDate > 1)
  1156. ? InstallDateAdjusted(installDate, document)
  1157. : my
  1158. ? CreateMySortKey(document)
  1159. : CreateFeaturedSortKey(document);
  1160. add(document, date);
  1161. }
  1162. }
  1163. };
  1164. addList(setsOrder(), SetFlag::Archived);
  1165. addList(featuredSetsOrder(), SetFlag::Installed);
  1166. if (!setsToRequest.empty()) {
  1167. for (const auto &[setId, accessHash] : setsToRequest) {
  1168. session().api().scheduleStickerSetRequest(setId, accessHash);
  1169. }
  1170. session().api().requestStickerSets();
  1171. }
  1172. ranges::sort(result, std::greater<>(), &StickerWithDate::date);
  1173. return result
  1174. | ranges::views::transform(&StickerWithDate::document)
  1175. | ranges::to_vector;
  1176. }
  1177. std::vector<not_null<DocumentData*>> Stickers::getListByEmoji(
  1178. std::vector<EmojiPtr> emoji,
  1179. uint64 seed,
  1180. bool forceAllResults) {
  1181. auto all = base::flat_set<EmojiPtr>();
  1182. for (const auto &one : emoji) {
  1183. all.emplace(one->original());
  1184. }
  1185. const auto single = (all.size() == 1) ? all.front() : nullptr;
  1186. struct StickerWithDate {
  1187. not_null<DocumentData*> document;
  1188. TimeId date = 0;
  1189. };
  1190. auto result = std::vector<StickerWithDate>();
  1191. auto &sets = setsRef();
  1192. auto setsToRequest = base::flat_map<uint64, uint64>();
  1193. const auto add = [&](not_null<DocumentData*> document, TimeId date) {
  1194. if (ranges::find(result, document, [](const StickerWithDate &data) {
  1195. return data.document;
  1196. }) == result.end()) {
  1197. result.push_back({ document, date });
  1198. }
  1199. };
  1200. constexpr auto kSlice = 65536;
  1201. const auto CreateSortKey = [&](
  1202. not_null<DocumentData*> document,
  1203. int base) {
  1204. if (document->sticker() && document->sticker()->isAnimated()) {
  1205. base += kSlice;
  1206. }
  1207. return TimeId(base + int((document->id ^ seed) % kSlice));
  1208. };
  1209. const auto CreateRecentSortKey = [&](not_null<DocumentData*> document) {
  1210. return CreateSortKey(document, kSlice * 6);
  1211. };
  1212. auto myCounter = 0;
  1213. const auto CreateMySortKey = [&](not_null<DocumentData*> document) {
  1214. auto base = kSlice * 6;
  1215. if (!document->sticker() || !document->sticker()->isAnimated()) {
  1216. base -= kSlice;
  1217. }
  1218. return (base - (++myCounter));
  1219. };
  1220. const auto CreateFeaturedSortKey = [&](not_null<DocumentData*> document) {
  1221. return CreateSortKey(document, kSlice * 2);
  1222. };
  1223. const auto CreateOtherSortKey = [&](not_null<DocumentData*> document) {
  1224. return CreateSortKey(document, 0);
  1225. };
  1226. const auto InstallDateAdjusted = [&](
  1227. TimeId date,
  1228. not_null<DocumentData*> document) {
  1229. return (document->sticker() && document->sticker()->isAnimated())
  1230. ? date
  1231. : date / 2;
  1232. };
  1233. const auto RecentInstallDate = [&](not_null<DocumentData*> document) {
  1234. Expects(document->sticker() != nullptr);
  1235. const auto sticker = document->sticker();
  1236. if (sticker->set.id) {
  1237. const auto setIt = sets.find(sticker->set.id);
  1238. if (setIt != sets.end()) {
  1239. return InstallDateAdjusted(setIt->second->installDate, document);
  1240. }
  1241. }
  1242. return TimeId(0);
  1243. };
  1244. auto recentIt = sets.find(Stickers::CloudRecentSetId);
  1245. if (recentIt != sets.cend()) {
  1246. const auto recent = recentIt->second.get();
  1247. const auto i = single
  1248. ? recent->emoji.find(single)
  1249. : recent->emoji.end();
  1250. const auto list = (i != recent->emoji.end())
  1251. ? &i->second
  1252. : !single
  1253. ? &recent->stickers
  1254. : nullptr;
  1255. if (list) {
  1256. const auto count = int(list->size());
  1257. result.reserve(count);
  1258. for (auto i = 0; i != count; ++i) {
  1259. const auto document = (*list)[i];
  1260. const auto sticker = document->sticker();
  1261. auto index = i;
  1262. if (!sticker) {
  1263. continue;
  1264. } else if (!single) {
  1265. const auto main = Ui::Emoji::Find(sticker->alt);
  1266. if (!main || !all.contains(main)) {
  1267. continue;
  1268. }
  1269. } else {
  1270. index = recent->stickers.indexOf(document);
  1271. }
  1272. const auto usageDate = (recent->dates.empty() || index < 0)
  1273. ? 0
  1274. : recent->dates[index];
  1275. const auto date = usageDate
  1276. ? usageDate
  1277. : RecentInstallDate(document);
  1278. result.push_back({
  1279. document,
  1280. date ? date : CreateRecentSortKey(document) });
  1281. }
  1282. }
  1283. }
  1284. const auto addList = [&](
  1285. const StickersSetsOrder &order,
  1286. SetFlag skip) {
  1287. for (const auto setId : order) {
  1288. auto it = sets.find(setId);
  1289. if (it == sets.cend() || (it->second->flags & skip)) {
  1290. continue;
  1291. }
  1292. const auto set = it->second.get();
  1293. if (set->emoji.empty()) {
  1294. setsToRequest.emplace(set->id, set->accessHash);
  1295. set->flags |= SetFlag::NotLoaded;
  1296. continue;
  1297. }
  1298. const auto my = (set->flags & SetFlag::Installed);
  1299. const auto i = single
  1300. ? set->emoji.find(single)
  1301. : set->emoji.end();
  1302. const auto list = (i != set->emoji.end())
  1303. ? &i->second
  1304. : !single
  1305. ? &set->stickers
  1306. : nullptr;
  1307. if (list) {
  1308. result.reserve(result.size() + list->size());
  1309. for (const auto document : *list) {
  1310. const auto sticker = document->sticker();
  1311. if (!sticker) {
  1312. continue;
  1313. } else if (!single) {
  1314. const auto main = Ui::Emoji::Find(sticker->alt);
  1315. if (!main || !all.contains(main)) {
  1316. continue;
  1317. }
  1318. }
  1319. const auto installDate = my ? set->installDate : TimeId(0);
  1320. const auto date = (installDate > 1)
  1321. ? InstallDateAdjusted(installDate, document)
  1322. : my
  1323. ? CreateMySortKey(document)
  1324. : CreateFeaturedSortKey(document);
  1325. add(document, date);
  1326. }
  1327. }
  1328. }
  1329. };
  1330. addList(setsOrder(), SetFlag::Archived);
  1331. //addList(featuredSetsOrder(), SetFlag::Installed);
  1332. if (!setsToRequest.empty()) {
  1333. for (const auto &[setId, accessHash] : setsToRequest) {
  1334. session().api().scheduleStickerSetRequest(setId, accessHash);
  1335. }
  1336. session().api().requestStickerSets();
  1337. }
  1338. if (forceAllResults || Core::App().settings().suggestStickersByEmoji()) {
  1339. const auto key = ranges::accumulate(
  1340. all,
  1341. QString(),
  1342. ranges::plus(),
  1343. &Ui::Emoji::One::text);
  1344. const auto others = session().api().stickersByEmoji(key);
  1345. if (others) {
  1346. result.reserve(result.size() + others->size());
  1347. for (const auto document : *others) {
  1348. add(document, CreateOtherSortKey(document));
  1349. }
  1350. } else if (!forceAllResults) {
  1351. return {};
  1352. }
  1353. }
  1354. ranges::sort(result, std::greater<>(), &StickerWithDate::date);
  1355. const auto appConfig = &session().appConfig();
  1356. auto mixed = std::vector<not_null<DocumentData*>>();
  1357. mixed.reserve(result.size());
  1358. auto premiumIndex = 0, nonPremiumIndex = 0;
  1359. const auto skipToNext = [&](bool premium) {
  1360. auto &index = premium ? premiumIndex : nonPremiumIndex;
  1361. while (index < result.size()
  1362. && result[index].document->isPremiumSticker() != premium) {
  1363. ++index;
  1364. }
  1365. };
  1366. const auto done = [&](bool premium) {
  1367. skipToNext(premium);
  1368. const auto &index = premium ? premiumIndex : nonPremiumIndex;
  1369. return (index == result.size());
  1370. };
  1371. const auto take = [&](bool premium) {
  1372. if (done(premium)) {
  1373. return false;
  1374. }
  1375. auto &index = premium ? premiumIndex : nonPremiumIndex;
  1376. mixed.push_back(result[index++].document);
  1377. return true;
  1378. };
  1379. if (session().premium()) {
  1380. const auto normalsPerPremium = appConfig->get<int>(
  1381. u"stickers_normal_by_emoji_per_premium_num"_q,
  1382. 2);
  1383. do {
  1384. // Add "stickers_normal_by_emoji_per_premium_num" non-premium.
  1385. for (auto i = 0; i < normalsPerPremium; ++i) {
  1386. if (!take(false)) {
  1387. break;
  1388. }
  1389. }
  1390. // Then one premium.
  1391. } while (!done(false) && take(true));
  1392. // Add what's left.
  1393. while (take(false)) {
  1394. }
  1395. while (take(true)) {
  1396. }
  1397. } else {
  1398. // All non-premium.
  1399. while (take(false)) {
  1400. }
  1401. // In the end add "stickers_premium_by_emoji_num" premium.
  1402. const auto premiumsToEnd = appConfig->get<int>(
  1403. u"stickers_premium_by_emoji_num"_q,
  1404. 0);
  1405. for (auto i = 0; i < premiumsToEnd; ++i) {
  1406. if (!take(true)) {
  1407. break;
  1408. }
  1409. }
  1410. }
  1411. return mixed;
  1412. }
  1413. std::optional<std::vector<not_null<EmojiPtr>>> Stickers::getEmojiListFromSet(
  1414. not_null<DocumentData*> document) {
  1415. if (auto sticker = document->sticker()) {
  1416. auto &inputSet = sticker->set;
  1417. if (!inputSet.id) {
  1418. return std::nullopt;
  1419. }
  1420. const auto &sets = this->sets();
  1421. auto it = sets.find(inputSet.id);
  1422. if (it == sets.cend()) {
  1423. return std::nullopt;
  1424. }
  1425. const auto set = it->second.get();
  1426. auto result = std::vector<not_null<EmojiPtr>>();
  1427. for (auto i = set->emoji.cbegin(), e = set->emoji.cend(); i != e; ++i) {
  1428. if (i->second.contains(document)) {
  1429. result.emplace_back(i->first);
  1430. }
  1431. }
  1432. if (result.empty()) {
  1433. return std::nullopt;
  1434. }
  1435. return result;
  1436. }
  1437. return std::nullopt;
  1438. }
  1439. not_null<StickersSet*> Stickers::feedSet(const MTPStickerSet &info) {
  1440. auto &sets = setsRef();
  1441. const auto &data = info.data();
  1442. auto it = sets.find(data.vid().v);
  1443. auto title = getSetTitle(data);
  1444. auto oldFlags = StickersSetFlags(0);
  1445. auto thumbnailType = StickerType::Webp;
  1446. const auto thumbnail = [&] {
  1447. if (const auto thumbs = data.vthumbs()) {
  1448. for (const auto &thumb : thumbs->v) {
  1449. const auto result = Images::FromPhotoSize(
  1450. &session(),
  1451. data,
  1452. thumb);
  1453. if (result.location.valid()) {
  1454. thumbnailType = Data::ThumbnailTypeFromPhotoSize(thumb);
  1455. return result;
  1456. }
  1457. }
  1458. }
  1459. return ImageWithLocation();
  1460. }();
  1461. const auto flags = ParseStickersSetFlags(data);
  1462. if (it == sets.cend()) {
  1463. it = sets.emplace(data.vid().v, std::make_unique<StickersSet>(
  1464. &owner(),
  1465. data.vid().v,
  1466. data.vaccess_hash().v,
  1467. data.vhash().v,
  1468. title,
  1469. qs(data.vshort_name()),
  1470. data.vcount().v,
  1471. flags | SetFlag::NotLoaded,
  1472. data.vinstalled_date().value_or_empty())).first;
  1473. } else {
  1474. const auto set = it->second.get();
  1475. set->accessHash = data.vaccess_hash().v;
  1476. set->title = title;
  1477. set->shortName = qs(data.vshort_name());
  1478. oldFlags = set->flags;
  1479. const auto clientFlags = set->flags
  1480. & (SetFlag::Featured
  1481. | SetFlag::Unread
  1482. | SetFlag::NotLoaded
  1483. | SetFlag::Special);
  1484. set->flags = flags | clientFlags;
  1485. const auto installDate = data.vinstalled_date();
  1486. set->installDate = installDate
  1487. ? (installDate->v ? installDate->v : base::unixtime::now())
  1488. : TimeId(0);
  1489. if (set->count != data.vcount().v
  1490. || set->hash != data.vhash().v
  1491. || set->emoji.empty()) {
  1492. // Need to request this data.
  1493. set->count = data.vcount().v;
  1494. set->hash = data.vhash().v;
  1495. set->flags |= SetFlag::NotLoaded;
  1496. }
  1497. }
  1498. const auto set = it->second.get();
  1499. set->setThumbnail(thumbnail, thumbnailType);
  1500. set->thumbnailDocumentId = data.vthumb_document_id().value_or_empty();
  1501. auto changedFlags = (oldFlags ^ set->flags);
  1502. if (changedFlags & SetFlag::Archived) {
  1503. const auto isMasks = (set->type() == StickersType::Masks);
  1504. auto &archivedOrder = isMasks
  1505. ? archivedMaskSetsOrderRef()
  1506. : archivedSetsOrderRef();
  1507. const auto index = archivedOrder.indexOf(set->id);
  1508. if (set->flags & SetFlag::Archived) {
  1509. if (index < 0) {
  1510. archivedOrder.push_front(set->id);
  1511. }
  1512. } else if (index >= 0) {
  1513. archivedOrder.removeAt(index);
  1514. }
  1515. }
  1516. return it->second.get();
  1517. }
  1518. not_null<StickersSet*> Stickers::feedSetFull(
  1519. const MTPDmessages_stickerSet &data) {
  1520. const auto set = feedSet(data.vset());
  1521. feedSetStickers(set, data.vdocuments().v, data.vpacks().v);
  1522. return set;
  1523. }
  1524. not_null<StickersSet*> Stickers::feedSet(
  1525. const MTPStickerSetCovered &data) {
  1526. const auto set = data.match([&](const auto &data) {
  1527. return feedSet(data.vset());
  1528. });
  1529. data.match([](const MTPDstickerSetCovered &data) {
  1530. }, [&](const MTPDstickerSetNoCovered &data) {
  1531. }, [&](const MTPDstickerSetMultiCovered &data) {
  1532. feedSetCovers(set, data.vcovers().v);
  1533. }, [&](const MTPDstickerSetFullCovered &data) {
  1534. feedSetStickers(set, data.vdocuments().v, data.vpacks().v);
  1535. });
  1536. return set;
  1537. }
  1538. void Stickers::feedSetStickers(
  1539. not_null<StickersSet*> set,
  1540. const QVector<MTPDocument> &documents,
  1541. const QVector<MTPStickerPack> &packs) {
  1542. set->flags &= ~SetFlag::NotLoaded;
  1543. auto &sets = setsRef();
  1544. const auto wasArchived = [&] {
  1545. const auto it = sets.find(set->id);
  1546. return (it != sets.end())
  1547. && (it->second->flags & SetFlag::Archived);
  1548. }();
  1549. auto customIt = sets.find(Stickers::CustomSetId);
  1550. const auto inputSet = set->identifier();
  1551. auto pack = StickersPack();
  1552. pack.reserve(documents.size());
  1553. for (const auto &item : documents) {
  1554. const auto document = owner().processDocument(item);
  1555. if (!document->sticker()) {
  1556. continue;
  1557. }
  1558. pack.push_back(document);
  1559. if (!document->sticker()->set.id) {
  1560. document->sticker()->set = inputSet;
  1561. }
  1562. if (customIt != sets.cend()) {
  1563. const auto custom = customIt->second.get();
  1564. const auto index = custom->stickers.indexOf(document);
  1565. if (index >= 0) {
  1566. custom->stickers.removeAt(index);
  1567. }
  1568. }
  1569. }
  1570. if (customIt != sets.cend() && customIt->second->stickers.isEmpty()) {
  1571. sets.erase(customIt);
  1572. customIt = sets.end();
  1573. }
  1574. auto writeRecent = false;
  1575. auto &recent = getRecentPack();
  1576. for (auto i = recent.begin(); i != recent.cend();) {
  1577. if (set->stickers.indexOf(i->first) >= 0
  1578. && pack.indexOf(i->first) < 0) {
  1579. i = recent.erase(i);
  1580. writeRecent = true;
  1581. } else {
  1582. ++i;
  1583. }
  1584. }
  1585. const auto isEmoji = (set->type() == StickersType::Emoji);
  1586. const auto isMasks = (set->type() == StickersType::Masks);
  1587. set->stickers = pack;
  1588. set->emoji.clear();
  1589. for (auto i = 0, l = int(packs.size()); i != l; ++i) {
  1590. const auto &pack = packs[i].data();
  1591. if (auto emoji = Ui::Emoji::Find(qs(pack.vemoticon()))) {
  1592. emoji = emoji->original();
  1593. auto &stickers = pack.vdocuments().v;
  1594. auto p = StickersPack();
  1595. p.reserve(stickers.size());
  1596. for (auto j = 0, c = int(stickers.size()); j != c; ++j) {
  1597. const auto document = owner().document(stickers[j].v);
  1598. if (!document->sticker()) {
  1599. continue;
  1600. }
  1601. p.push_back(document);
  1602. }
  1603. set->emoji[emoji] = std::move(p);
  1604. }
  1605. }
  1606. if (writeRecent) {
  1607. session().saveSettings();
  1608. }
  1609. const auto isArchived = !!(set->flags & SetFlag::Archived);
  1610. if ((set->flags & SetFlag::Installed) && !isArchived) {
  1611. if (isEmoji) {
  1612. session().local().writeInstalledCustomEmoji();
  1613. } else if (isMasks) {
  1614. session().local().writeInstalledMasks();
  1615. } else {
  1616. session().local().writeInstalledStickers();
  1617. }
  1618. }
  1619. if (set->flags & SetFlag::Featured) {
  1620. if (isEmoji) {
  1621. session().local().writeFeaturedCustomEmoji();
  1622. } else if (isMasks) {
  1623. } else {
  1624. session().local().writeFeaturedStickers();
  1625. }
  1626. }
  1627. if (wasArchived != isArchived) {
  1628. if (isEmoji) {
  1629. } else if (isMasks) {
  1630. session().local().writeArchivedMasks();
  1631. } else {
  1632. session().local().writeArchivedStickers();
  1633. }
  1634. }
  1635. notifyUpdated(set->type());
  1636. }
  1637. void Stickers::feedSetCovers(
  1638. not_null<StickersSet*> set,
  1639. const QVector<MTPDocument> &documents) {
  1640. set->covers = StickersPack();
  1641. for (const auto &cover : documents) {
  1642. const auto document = session().data().processDocument(cover);
  1643. if (document->sticker()) {
  1644. set->covers.push_back(document);
  1645. }
  1646. }
  1647. }
  1648. void Stickers::newSetReceived(const MTPDmessages_stickerSet &set) {
  1649. const auto &s = set.vset().c_stickerSet();
  1650. if (!s.vinstalled_date()) {
  1651. LOG(("API Error: "
  1652. "updateNewStickerSet without install_date flag."));
  1653. return;
  1654. } else if (s.is_archived()) {
  1655. LOG(("API Error: "
  1656. "updateNewStickerSet with archived flag."));
  1657. return;
  1658. }
  1659. auto &order = s.is_emojis()
  1660. ? emojiSetsOrderRef()
  1661. : s.is_masks()
  1662. ? maskSetsOrderRef()
  1663. : setsOrderRef();
  1664. int32 insertAtIndex = 0, currentIndex = order.indexOf(s.vid().v);
  1665. if (currentIndex != insertAtIndex) {
  1666. if (currentIndex > 0) {
  1667. order.removeAt(currentIndex);
  1668. }
  1669. order.insert(insertAtIndex, s.vid().v);
  1670. }
  1671. feedSetFull(set);
  1672. }
  1673. QString Stickers::getSetTitle(const MTPDstickerSet &s) {
  1674. auto title = qs(s.vtitle());
  1675. if ((s.vflags().v & MTPDstickerSet::Flag::f_official)
  1676. && !title.compare(u"Great Minds"_q, Qt::CaseInsensitive)) {
  1677. return tr::lng_stickers_default_set(tr::now);
  1678. }
  1679. return title;
  1680. }
  1681. RecentStickerPack &Stickers::getRecentPack() const {
  1682. if (cRecentStickers().isEmpty() && !cRecentStickersPreload().isEmpty()) {
  1683. const auto p = cRecentStickersPreload();
  1684. cSetRecentStickersPreload(RecentStickerPreload());
  1685. auto &recent = cRefRecentStickers();
  1686. recent.reserve(p.size());
  1687. for (const auto &preloaded : p) {
  1688. const auto document = owner().document(preloaded.first);
  1689. if (!document || !document->sticker()) continue;
  1690. recent.push_back(qMakePair(document, preloaded.second));
  1691. }
  1692. }
  1693. return cRefRecentStickers();
  1694. }
  1695. StickerType ThumbnailTypeFromPhotoSize(const MTPPhotoSize &size) {
  1696. const auto &type = size.match([&](const auto &data) {
  1697. return data.vtype().v;
  1698. });
  1699. const auto ch = type.isEmpty() ? char() : type[0];
  1700. switch (ch) {
  1701. case 's': return StickerType::Webp;
  1702. case 'a': return StickerType::Tgs;
  1703. case 'v': return StickerType::Webm;
  1704. }
  1705. return StickerType::Webp;
  1706. }
  1707. } // namespace Stickers