From 0f15adb208b7b345f07fc0c07f6aca251ba6c5d7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 22 Dec 2021 15:14:16 +0000 Subject: [PATCH] Sort contacts by last seen by default. Fixes #5515. --- Telegram/Resources/icons/contacts_add.png | Bin 107 -> 0 bytes Telegram/Resources/icons/contacts_add@2x.png | Bin 126 -> 0 bytes Telegram/Resources/icons/contacts_add@3x.png | Bin 174 -> 0 bytes .../Resources/icons/contacts_alphabet.png | Bin 0 -> 448 bytes .../Resources/icons/contacts_alphabet@2x.png | Bin 0 -> 784 bytes .../Resources/icons/contacts_alphabet@3x.png | Bin 0 -> 1148 bytes Telegram/Resources/icons/contacts_online.png | Bin 0 -> 385 bytes .../Resources/icons/contacts_online@2x.png | Bin 0 -> 649 bytes .../Resources/icons/contacts_online@3x.png | Bin 0 -> 1014 bytes Telegram/SourceFiles/boxes/boxes.style | 15 +++ .../boxes/peer_list_controllers.cpp | 96 ++++++++++++++++-- .../SourceFiles/boxes/peer_list_controllers.h | 13 +++ 12 files changed, 116 insertions(+), 8 deletions(-) delete mode 100644 Telegram/Resources/icons/contacts_add.png delete mode 100644 Telegram/Resources/icons/contacts_add@2x.png delete mode 100644 Telegram/Resources/icons/contacts_add@3x.png create mode 100644 Telegram/Resources/icons/contacts_alphabet.png create mode 100644 Telegram/Resources/icons/contacts_alphabet@2x.png create mode 100644 Telegram/Resources/icons/contacts_alphabet@3x.png create mode 100644 Telegram/Resources/icons/contacts_online.png create mode 100644 Telegram/Resources/icons/contacts_online@2x.png create mode 100644 Telegram/Resources/icons/contacts_online@3x.png diff --git a/Telegram/Resources/icons/contacts_add.png b/Telegram/Resources/icons/contacts_add.png deleted file mode 100644 index 3f3efebd0b11cedb540598fa10d43954c6d84ffa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 107 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`MxHK?Ar`&KDG3Qb&Nnds`OiNw zDJA8|(w0Wf6nLBhKm02{ z7U`Z;@?rDkJtdjCnp%_AnBG1s(YvbD0H_)W8aB`0db)BSBZHW{AXwmstN~-~)xxSH$L4kwyK-~ZT=eJ#ta6Y>Cq9dQ_85{--9mSl{5wmZr1I=deboFyt I=akR{0J`%wxc~qF diff --git a/Telegram/Resources/icons/contacts_alphabet.png b/Telegram/Resources/icons/contacts_alphabet.png new file mode 100644 index 0000000000000000000000000000000000000000..8a8c99aa1d89313d219fc8648931bd046ca1c8d3 GIT binary patch literal 448 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgfOv}^7F~mYJ zIYEN8#i>DXg@dCKxr zU0q#MqK+LoQu6MOWx}SjXU;4+p(8FXzCe>--fmCvb3Ru#-KZ@uo}Ql0$;s*Pc+J|i zfoGVG-TM0adgH}L&UtpVQK6x%;b+gD{R>nQxjBuKgX7Pyui4Me%q-Bnl%#86A(0_= zA>PW$3g~=pe*WhV9yH8$WlBCj&vwPq!~ZvIFcA9UJjKh~`~TnH<)5FORZ3W}cJ1Hy z_w5y?r!@EU_Wu9#^YQD~+}C&3{{9xc-0$E{6@v>-Ei-4%oH}(X%ioevE(RyR<)sQY RQ!au+&(qbdZiqvcFo2t&;>j)7X4c4!E4U5~8&952KgoA7(Z+x1du8JUr6c)Y!OE(-f4K zOkwoek-zub=JPK#! zQESt--?o3x-}3I+@`!J5%cOhVUY7i-ut|FH+-+~%@ef;iebheBX>n0H+ITmw{jI#o z6N%jc8Zmm}?T0@W)+>~l*Z-f?t}lF{q{8{+si#H~ZxXpc7W`kz6r*oE(`RWAr{1yk zpIgp-UeDXj5VkVpNN!?Fo9lvuUyYnDmACDPn|A)WsZ?)=YW4cW!s?D~JXu?<1UO{) z+7Bia*v-#8yf@;0g29?F?Je%=lRKRjRv0#x?TA|Y$=}Cp_R?ODsQA@aot8FS_*&(< zMXa0k*MFB;Zu-+aR0R6n7H^EvoA~zo@A*n~$2ayLP2$*nHEZkb+?3;wKQ3=sVXZ61 zU6a|Mwfr*YI??&(+eL1@|E@K)%j%D^gWhy!o?UnKT$TpCtlH}~&qL+YjPtQQ@4p&F z%(}C!U~j-ihqZpoCwnbz;A%+-w=Meh_@|ft@95i~Dr`J>e#|qJ>gCXs;7MA3Ge=C| zk%$nBi)2#ew%fTzGhMPzJuOmTIUc7r*>&;7q>U$zKZO9!*rXP0{) zj-2hce8PqI-{-4!yl-jF-5SODdY_NNnN-DP48ad_S?d^0E>E3bHD}E%P-^sa^>bP0 Hl+XkKx?fC6 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/contacts_alphabet@3x.png b/Telegram/Resources/icons/contacts_alphabet@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..72a22f9f57d959fe13371d8db4b0fc8bd208b826 GIT binary patch literal 1148 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbAj}{>;)%SN;?3=-u z)*zX{2B(VI9;p^bMcq1a!Xq~~x2Wh-|8{P>{73KOPc8~8?7!uhW%{+guFlTVa^{Q~ z9IZ|t{(srJV~57k1yj3jOqw*wPFD0?WmVNBtw|XMU%!40WBT0T_pso{yLWzD{#X~q z$H$v^{)$+`?^^lkfc7dNSh>>E*SA$hKW0*w7|??KFaH1f^=s)#<_ABL5KaO+u+C6H z{h(a`^39utMN7QAymB zcR)!iqgwv$yUR*Uhy5$v?{{M$*`41XaeEa_WsAg4aD(mNO<_+uGEG#TE+)_`(#>T$dU6OzH%$b^s ziVnUvfBwXjzsRbwwY9w%C^_xZr=qWWqqfV*$OI%z*mz))m9g<+(-PJlO-)Q(%uJI$ ztzE0TNg?y!Y-VxSdc$lfY3aoVadB~*Hf>rm@#d8)BE|;WN?UfleD$h}>GsW=2NxF? z6>TW@KX&zM=VZo>{^3=g;Hg;(~bE+uQBt z_h0E}ZP&`^W-kNAxyd~R9)AArj{d&BBi9Zca@rWGY8~YmZE1b)&K;43;-aFR1?_EZ zN3MPRSO}EbEgg}Ur*}-(asQ4T6E=9gzWw#9sZi?4_A4DJH&-mEuC88rQ}5xIFD2F0 z(SJ?eJWp7sF8}9ZN^Wj%h_k~J5udWMve?+W$BwlSI5DR;F*? ze(&{-U9!P4yiTu~{_UAKZ(iQ5)APggZ^;-h-cl3!M*Q&BojY${iInSF=I19ZEG%p& z+;Q^Aix)4d<GuEYGrGbEO@Y~8pqu*8jT=Je_6KVK|5 zclK;&#Ku{g6BefJ+__Wm@9*Eg7ysS*+PF2Jlb0iU4E+e yL|9Z*R8ms%TH%6UN8DDG)_+SuB<$;wKRAQA4*Tv{AU6+`^F3YtT-G@yGywnrSPzW= literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/contacts_online.png b/Telegram/Resources/icons/contacts_online.png new file mode 100644 index 0000000000000000000000000000000000000000..8fe7e9b163c78ea3319d6605cb52bf4da2c94e7e GIT binary patch literal 385 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfUz4ml*46zV= z8*I(hVjyreb)(=T);BEN{RycvCokP_?GX2k+f~zZFRxhJ#PRO(!3XcUyf5i+?^D`m zlcx5$Q0Mib?R+;3Iu@;1mwim)$Dc0q)cUiVd^i|e3y!BtuxX~=|Nf1wLpnC@*OMnT zbE{+{swCSILqq31pSyco?rF>85qxL!+F!+Y?t4EywUdcaAjbDnd-V0vA3tX=o4JgW zC46#(M`PHD!a0jHI-FgCPIt}m;ayfaZ}nWSNiAHG$3JdV;$RZuUH)uW-mGPXGQlx# zgQvQ=zV*J`y>!-ECP_2t^Gaf$qL%AB`nWBd{^$ML`ML`?Ozhe*r2*u9Pgg&ebxsLQ E075^CsQ>@~ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/contacts_online@2x.png b/Telegram/Resources/icons/contacts_online@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3549bc9479f64f0285989c814f16feef5e8e3fff GIT binary patch literal 649 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H1$0Ck2}&NuokdH8~}@dC*UJbR`$%CzcFKmGXQj-Zt>hZ~m)#9cl# z{dB6??5(%oKK|&j@Tmd^6W4mF5-ZtCp|*+3TmRPi|LA?to%FqG@8y>vYr~2k9th&7 zuYbI}lkZJgOyH)22@DI`(;R??|L|bjF>R{X(z^Zq%^$zoEVSw83iH|UuwcfzmZKbD zK2kBKljdxSc@%Z+ZCUrxq%PIVO;b}9gZoo&n?+UZjH&z2%ixpfV;k_Pt<{N9|MJh8 zeYuGe8(G?)#9sR3u;FA%(d@2_M&~liMh3%gdE00EEpI>UXu_0!rN8g7i&ko+*pBZu z`jZ6iyr>PTFff~awr1Y@e*Ohtt9n~*gw|+3xR$kbMyq_!`Ao@qU+3kRO7R+MC4RGX z2xe0`w?1QL$hHd)k}?knboZ*u_qy$SuP?@3)LeOV`njxgN@xNANxuba literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/contacts_online@3x.png b/Telegram/Resources/icons/contacts_online@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..5cf60407b67aa9ce19b758b9e81e12da9c9b6998 GIT binary patch literal 1014 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbAj}p5W=?7?Q#I zHfpb*mZM0RLX(Dm0!J^W>_R18^w@GxMTk@RK+K|xC04S%Zi~{YpX8XWRy}Dc^*A^% z&c?vyVup!Wx2%ke!#z>Cl^s%^n|JM4_V@Vl<5Au0O^e$R&H+2_N0Ngb%kG$UC04yW zx88rxZ(9_%zWvR%xb@Mu@4kJzR@d{?(W9-c2W;fdSJ-T0jaYu!Q2a&^Tkg8->b<&* zho>({+L+<((CT#XPTl|6M-Ru`Iw5VCFoW@@tnr-!;kJVbAO8HYnd&7gFE1}EyYTe% z>C=xNcYpJ2Yt-AHKP!bDXWV8_nBt|n=2F#ctyKvJF3h^Uwe{Y)bLZHa8|732`Hdx_ zCAvD-tzW-hyt`H6Zb3OKmoH1q zt1@2P`Oe$jX!OoQ<USz$ z3Y^(GTYN+O&b#kQf4LT0Je0GENosm2X>u<|`MUj$)}&vd3;JAPP{@*orak+~o_^3s%oi@+;OLu+5 zg=>%Y`OjIjeEIT)0UVFp6!i7={r&uos2zSNz}>Ur=QQ2FCL4YQ$sCyC%$U&Sus_5> z!?aO2Z_k#Bgv5;+&F{)~zb%{neU)gzuB%!L4?cJh820eZr7PN!zR`1b8v(N3A|iA-}mq<(jYhlgL<^Q&EH&W(PT zE{FVPGd}-Y3snD?iGA%0VcTf=#e>!DE8ju42FqUpJUO>#ZrgA@!CKh2jrZ{~pEFyE zh0^-`3?=10iocf@V@_2%{9qv$oAxB{ugmnlB`)r bot, not_null chat) { const auto history = chat->owner().history(chat); auto &histories = history->owner().histories(); @@ -110,17 +114,31 @@ void AddBotToGroup(not_null bot, not_null chat) { object_ptr PrepareContactsBox( not_null sessionController) { - const auto controller = sessionController; - auto delegate = [=](not_null box) { + using Mode = ContactsBoxController::SortMode; + auto controller = std::make_unique( + &sessionController->session()); + const auto raw = controller.get(); + auto init = [=](not_null box) { + struct State { + QPointer toggleSort; + Mode mode = ContactsBoxController::SortMode::Online; + }; + const auto state = box->lifetime().make_state(); box->addButton(tr::lng_close(), [=] { box->closeBox(); }); box->addLeftButton( tr::lng_profile_add_contact(), - [=] { controller->showAddContact(); }); + [=] { sessionController->showAddContact(); }); + state->toggleSort = box->addTopButton(st::contactsSortButton, [=] { + const auto online = (state->mode == Mode::Online); + state->mode = online ? Mode::Alphabet : Mode::Online; + raw->setSortMode(state->mode); + state->toggleSort->setIconOverride( + online ? &st::contactsSortOnlineIcon : nullptr, + online ? &st::contactsSortOnlineIconOver : nullptr); + }); + raw->setSortMode(Mode::Online); }; - return Box( - std::make_unique( - &sessionController->session()), - std::move(delegate)); + return Box(std::move(controller), std::move(init)); } void PeerListRowWithLink::setActionLink(const QString &action) { @@ -368,7 +386,8 @@ ContactsBoxController::ContactsBoxController( not_null session, std::unique_ptr searchController) : PeerListController(std::move(searchController)) -, _session(session) { +, _session(session) +, _sortByOnlineTimer([=] { sort(); }) { } Main::Session &ContactsBoxController::session() const { @@ -404,6 +423,7 @@ void ContactsBoxController::rebuildRows() { }; appendList(session().data().contactsList()); checkForEmptyRows(); + sort(); delegate()->peerListRefreshRows(); } @@ -427,6 +447,66 @@ void ContactsBoxController::rowClicked(not_null row) { Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId); } +void ContactsBoxController::setSortMode(SortMode mode) { + if (_sortMode == mode) { + return; + } + _sortMode = mode; + sort(); + if (_sortMode == SortMode::Online) { + session().changes().peerUpdates( + Data::PeerUpdate::Flag::OnlineStatus + ) | rpl::filter([=](const Data::PeerUpdate &update) { + return !_sortByOnlineTimer.isActive() + && delegate()->peerListFindRow(update.peer->id.value); + }) | rpl::start_with_next([=] { + _sortByOnlineTimer.callOnce(kSortByOnlineThrottle); + }, _sortByOnlineLifetime); + } else { + _sortByOnlineTimer.cancel(); + _sortByOnlineLifetime.destroy(); + } +} + +void ContactsBoxController::sort() { + switch (_sortMode) { + case SortMode::Alphabet: sortByName(); break; + case SortMode::Online: sortByOnline(); break; + default: Unexpected("SortMode in ContactsBoxController."); + } +} + +void ContactsBoxController::sortByName() { + auto keys = base::flat_map(); + keys.reserve(delegate()->peerListFullRowsCount()); + const auto key = [&](const PeerListRow &row) { + const auto id = row.id(); + const auto i = keys.find(id); + if (i != end(keys)) { + return i->second; + } + const auto peer = row.peer(); + const auto history = peer->owner().history(peer); + return keys.emplace(id, history->chatListNameSortKey()).first->second; + }; + const auto predicate = [&](const PeerListRow &a, const PeerListRow &b) { + return (key(a).compare(key(b)) < 0); + }; + delegate()->peerListSortRows(predicate); +} + +void ContactsBoxController::sortByOnline() { + const auto now = base::unixtime::now(); + const auto key = [&](const PeerListRow &row) { + const auto user = row.peer()->asUser(); + return user ? (std::min(user->onlineTill, now) + 1) : TimeId(); + }; + const auto predicate = [&](const PeerListRow &a, const PeerListRow &b) { + return key(a) > key(b); + }; + delegate()->peerListSortRows(predicate); +} + bool ContactsBoxController::appendRow(not_null user) { if (auto row = delegate()->peerListFindRow(user->id.value)) { updateRowHook(row); diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.h b/Telegram/SourceFiles/boxes/peer_list_controllers.h index 6250e4236..a9599d8f9 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.h +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_box.h" #include "base/flat_set.h" #include "base/weak_ptr.h" +#include "base/timer.h" // Not used for now. // @@ -136,6 +137,12 @@ public: not_null peer) override final; void rowClicked(not_null row) override; + enum class SortMode { + Alphabet, + Online, + }; + void setSortMode(SortMode mode); + protected: virtual std::unique_ptr createRow(not_null user); virtual void prepareViewHook() { @@ -144,11 +151,17 @@ protected: } private: + void sort(); + void sortByName(); + void sortByOnline(); void rebuildRows(); void checkForEmptyRows(); bool appendRow(not_null user); const not_null _session; + SortMode _sortMode = SortMode::Alphabet; + base::Timer _sortByOnlineTimer; + rpl::lifetime _sortByOnlineLifetime; };