From 2f0aa6ef05f6c3657e22e6ee675387ba9a80b0db Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 30 Apr 2024 16:49:17 +0300 Subject: [PATCH 01/40] Removed state sending of low level of battery to call participant. --- Telegram/SourceFiles/calls/calls_call.cpp | 34 +++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 3a3e68b475fe7c..527839363b0be9 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -7,31 +7,29 @@ For license and copyright information please follow this link: */ #include "calls/calls_call.h" -#include "main/main_session.h" -#include "main/main_account.h" -#include "main/main_app_config.h" #include "apiwrap.h" -#include "lang/lang_keys.h" -#include "boxes/abstract_box.h" -#include "ui/boxes/confirm_box.h" -#include "ui/boxes/rate_call_box.h" -#include "calls/calls_instance.h" -#include "base/battery_saving.h" #include "base/openssl_help.h" +#include "base/platform/base_platform_info.h" #include "base/random.h" -#include "mtproto/mtproto_dh_utils.h" -#include "mtproto/mtproto_config.h" +#include "boxes/abstract_box.h" +#include "calls/calls_instance.h" +#include "calls/calls_panel.h" #include "core/application.h" #include "core/core_settings.h" -#include "window/window_controller.h" +#include "data/data_session.h" +#include "data/data_user.h" +#include "lang/lang_keys.h" +#include "main/main_app_config.h" +#include "main/main_session.h" #include "media/audio/media_audio_track.h" -#include "base/platform/base_platform_info.h" -#include "calls/calls_panel.h" +#include "mtproto/mtproto_config.h" +#include "mtproto/mtproto_dh_utils.h" +#include "ui/boxes/confirm_box.h" +#include "ui/boxes/rate_call_box.h" +#include "webrtc/webrtc_create_adm.h" #include "webrtc/webrtc_environment.h" #include "webrtc/webrtc_video_track.h" -#include "webrtc/webrtc_create_adm.h" -#include "data/data_user.h" -#include "data/data_session.h" +#include "window/window_controller.h" #include #include @@ -1072,6 +1070,7 @@ void Call::createAndStartController(const MTPDphoneCall &call) { Core::App().mediaDevices().setCaptureMuted(muted); }, _instanceLifetime); +#if 0 Core::App().batterySaving().value( ) | rpl::start_with_next([=](bool isSaving) { crl::on_main(weak, [=] { @@ -1080,6 +1079,7 @@ void Call::createAndStartController(const MTPDphoneCall &call) { } }); }, _instanceLifetime); +#endif } void Call::handleControllerStateChange(tgcalls::State state) { From 20a13663a6c3830b5106affe9061b051cfa713e3 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 30 Apr 2024 17:35:04 +0300 Subject: [PATCH 02/40] Slightly improved style of rpl text mapping. --- Telegram/SourceFiles/boxes/moderate_messages_box.cpp | 4 ++-- Telegram/SourceFiles/chat_helpers/ttl_media_layer_widget.cpp | 2 +- Telegram/SourceFiles/settings/settings_business.cpp | 2 +- Telegram/SourceFiles/settings/settings_premium.cpp | 4 ++-- Telegram/SourceFiles/ui/effects/premium_graphics.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp index e6079a0b72c83b..4496ed421a07aa 100644 --- a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp @@ -757,9 +757,9 @@ void DeleteChatBox(not_null box, not_null peer) { const auto label = Ui::CreateChild( line, peer->isSelf() - ? tr::lng_saved_messages() | rpl::map(Ui::Text::Bold) + ? tr::lng_saved_messages() | Ui::Text::ToBold() : maybeUser - ? tr::lng_profile_delete_conversation() | rpl::map(Ui::Text::Bold) + ? tr::lng_profile_delete_conversation() | Ui::Text::ToBold() : rpl::single(Ui::Text::Bold(peer->name())), box->getDelegate()->style().title); line->widthValue( diff --git a/Telegram/SourceFiles/chat_helpers/ttl_media_layer_widget.cpp b/Telegram/SourceFiles/chat_helpers/ttl_media_layer_widget.cpp index f2445a1668f602..d2e2e945eca8eb 100644 --- a/Telegram/SourceFiles/chat_helpers/ttl_media_layer_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/ttl_media_layer_widget.cpp @@ -224,7 +224,7 @@ PreviewWrap::PreviewWrap( lt_user, rpl::single( item->history()->peer->shortName() - ) | rpl::map(Ui::Text::RichLangValue), + ) | Ui::Text::ToRichLangValue(), Ui::Text::RichLangValue) : (isRound ? tr::lng_ttl_round_tooltip_in diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp index c11a0f3cddad9b..66b707c5fcc144 100644 --- a/Telegram/SourceFiles/settings/settings_business.cpp +++ b/Telegram/SourceFiles/settings/settings_business.cpp @@ -185,7 +185,7 @@ void AddBusinessSummary( const auto label = content->add( object_ptr( content, - std::move(entry.title) | rpl::map(Ui::Text::Bold), + std::move(entry.title) | Ui::Text::ToBold(), stLabel), titlePadding); label->setAttribute(Qt::WA_TransparentForMouseEvents); diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index 601f1f978fa805..057f36d2d99731 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -986,7 +986,7 @@ void Premium::setupContent() { object_ptr( content, tr::lng_premium_summary_bottom_subtitle( - ) | rpl::map(Ui::Text::Bold), + ) | Ui::Text::ToBold(), stLabel), st::defaultSubsectionTitlePadding); content->add( @@ -1630,7 +1630,7 @@ void AddSummaryPremium( const auto label = content->add( object_ptr( content, - std::move(entry.title) | rpl::map(Ui::Text::Bold), + std::move(entry.title) | Ui::Text::ToBold(), stLabel), titlePadding); label->setAttribute(Qt::WA_TransparentForMouseEvents); diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp index 3d3cb00ef9d33b..b701f8500034b3 100644 --- a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp @@ -1199,7 +1199,7 @@ void ShowListBox( const auto title = content->add( object_ptr( content, - base::take(entry.title) | rpl::map(Ui::Text::Bold), + base::take(entry.title) | Ui::Text::ToBold(), stLabel), entry.icon ? iconTitlePadding : titlePadding); content->add( From ef8c07e6eb0f44ca634f817e30351a39a33720fe Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 30 Apr 2024 17:35:34 +0300 Subject: [PATCH 03/40] Added ability to translate selected text in profile section. --- .../SourceFiles/info/profile/info_profile_actions.cpp | 11 +++++++++-- Telegram/lib_ui | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index c4ba6889962b2c..b7a14b484b2372 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -965,13 +965,20 @@ object_ptr DetailsFiller::setupInfo() { if (Ui::SkipTranslate(state->labelText.current())) { return; } - auto item = tr::lng_context_translate(tr::now); + auto item = (request.selection.empty() + ? tr::lng_context_translate + : tr::lng_context_translate_selected)(tr::now); request.menu->addAction(std::move(item), [=] { controller->window().show(Box( Ui::TranslateBox, peer, MsgId(), - state->labelText.current(), + request.selection.empty() + ? state->labelText.current() + : Ui::Text::Mid( + state->labelText.current(), + request.selection.from, + request.selection.to - request.selection.from), false)); }); }); diff --git a/Telegram/lib_ui b/Telegram/lib_ui index cb57bef3f01b7e..08b56a17892d4c 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit cb57bef3f01b7ec60eb0eae0ee68cd56cb3a9b1f +Subproject commit 08b56a17892d4c9c6b7ef87fcffbb64090e9ce2a From b9b7d9e33709daec348c51e81607e47c56d6e3bc Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 30 Apr 2024 14:58:32 +0400 Subject: [PATCH 04/40] Implement live location view. --- .../icons/chat/live_location_long.png | Bin 0 -> 416 bytes .../icons/chat/live_location_long@2x.png | Bin 0 -> 913 bytes .../icons/chat/live_location_long@3x.png | Bin 0 -> 1588 bytes Telegram/Resources/langs/lang.strings | 9 + .../SourceFiles/data/data_media_types.cpp | 51 ++- Telegram/SourceFiles/data/data_media_types.h | 18 +- Telegram/SourceFiles/history/history_item.cpp | 4 +- .../view/media/history_view_location.cpp | 407 ++++++++++++++++-- .../view/media/history_view_location.h | 33 +- .../history/view/media/history_view_media.cpp | 4 + .../history/view/media/history_view_media.h | 1 + Telegram/SourceFiles/ui/chat/chat.style | 6 + Telegram/SourceFiles/ui/chat/chat_style.cpp | 6 + Telegram/SourceFiles/ui/chat/chat_style.h | 1 + Telegram/build/prepare/prepare.py | 76 +++- 15 files changed, 554 insertions(+), 62 deletions(-) create mode 100644 Telegram/Resources/icons/chat/live_location_long.png create mode 100644 Telegram/Resources/icons/chat/live_location_long@2x.png create mode 100644 Telegram/Resources/icons/chat/live_location_long@3x.png diff --git a/Telegram/Resources/icons/chat/live_location_long.png b/Telegram/Resources/icons/chat/live_location_long.png new file mode 100644 index 0000000000000000000000000000000000000000..847930f549c6a70c90798213cbfdc6becb5414f6 GIT binary patch literal 416 zcmV;R0bl-!P)Kk-zFeVHkkl4mpwhD`tbFT!3US%YbWe0jHGgR;3hK zT!f8HXCadO36nC|+<=_)7{2-?2FLt6&-OljJ>Ofeh=YT}2b!k&{r*qycDsE(-_JQ+ z*W2y(?REpk<8eNp*ECH;qS0u--?uCa7!HT2RO*KX=kqymxm;GO6#$4xC=|M0ufX|y z-fT9&{eDj*61Jj-VE~;@M~#(ArCzU#$bP>Es@1CUrBVr)&1SZu>-Ac#_DZkU1Deg| zt29j$2#3SpqucHFEt<>afWzVN_Hr_r0Fg-Kd$d-o0j*X`jhRg5@pu5SSgc$wHyRD) zi^U?aSS-F5Ma1QDEtgB+bUIC^Q+239p&%l=-3~Y&kLr?^WhIkITTv0wb-ml|ss&Bc z%x1I7$K&zAV4%JkjYjEo+HUa=kH-@X20y*m>kR|~Kj$1A{sB+ZS`Y$gpUayy7nE#Tpv$Io{<=n1_+uPfwrY7Xi z&(CYf0Dw>^WVKoiPRViH`X^TN3*lD zZnxX#^QBTLwC?x&4Gy}ov4N`3%*EEff z+wB(@7f5JtZ#OI@%d(kFMq>{sit6ddu6IXO9rQU(G64b$m# zLQRsS=H}*aGmv_M78K|jw>F#2+5ShF}|m^wicZauh*L^kR-XZw3Pd!^E}_x z)s^d4RaGSt2`I0xuN#vRvbnj5a=6{@>_0xsJz0qj&tFCst z{ev%xB6`4PqbN!s5GXAz)!+KZuXz@WWps4Z;c(Q~*B2HRK0Q5MUS96(?CkCBed?E& zmy4pv^E}J4X0th&OrD>g2ZOjX0t5IudlCq{ECVSk|dv>pA|*P nyD0{P!C){L3d!FvR``=#{3jhEB0000000000000000055JKSz<2!{L;c zmSPx|NF>7H@bBNh51r@q>C^lB`y@#c1VNHyVe2ep^FpDpwzjsqx>})72!+B^r%uIU zvDMX8r_;H(xae>=*4NjW4mYHVy&DwS+DJ2_x(Zq9DEQ{xp1#pB11&z?PtCLu|3W@ct~c6McDg&+tXk0+DK znwpwqG8r`n!?1hz?ky}Vm`tX8x;PB6SZp?%(NhtLL{i&eu~;IJ=)r>r%gf8jGuhJ8 zLVZ+TUjFs#SK2B4^yyPaM~6TlpyiRtWJ5zkw80-fd?+q1KI&%ay1KeHH#gBb^m;vx z<7o%8*=&tQW4GIfhKA4^)ytPJY0cPdwrkg}9duBEKrk{gf{yokz1Oc_FLZO+jE;_? zMVQUztRYgV^yJBt)aJv74{4p7&1Uq%PK(Rs(k_;Pfq{IR$%$Ajw%KfG1!}cArwl@& zaBXc3&EWU@o12@N#A>x#G;nNe>{uUr_4W1NzkjC=@9}u3`x!&yi^z^h`F3-IQR&Etj_+uNi5=5RQfXUzvWWb?sb zFdB_gn_{t;d2DZQZ$(7~y644<7cQ5Jc?Jx_&>Lqc6x!b2W*(le%F0UG4e!Q{8%zRw zdU`^k5E?i>KA!8*@7}$OW_bVpJ)h6dy@@=~9(5BF6Inx4DwW&qMx&z9s8lMY1_%U# z%F4>jN!V<*!{I=a3=R(F+f1&sTCLyjM~l$ubhN21h0SKSx3{AYh(sb0iA1zoEj30W zkytF2FJHdgym|AWgYtMhqtS>a@p`=(#vc`_RBEwU&^mT^cXc{l>La(jy!`(C`^o7o zYIJlI9oEy+liY8&+uPgQQ*YoRk?6^jC$uCJ6BCD?-sZKEqtR%dKYvb}gxhSkj~_oK zUu?Ks?u82%Zr!?7UtdpqBA863*4EZ=IGo&UZEb!1`ZYB>4u``Qi^XcSy4~)0JdWe| zrAwD?-@aX4U5zGjxm?}d-BVLj`E+qe91drAcsP;H#>U3d(vsKfjmP7upIWU}rBaEe zdiwNf>agT0*z)o+?faEPl4Ngh?_X$}g5&tWz(6z_P5UoM6_3Zq#>P_L{FF*1ZO)h3 ze_}C%#bTW~bH-pW?Ck7ht$1f=$7nPjv;x=A(D3fvJ0{Cm8yg#aeSLp_8H=J)sb0N$ zMSIDm_ MediaContact::createView( MediaLocation::MediaLocation( not_null parent, - const LocationPoint &point) -: MediaLocation(parent, point, QString(), QString()) { + const LocationPoint &point, + TimeId livePeriod) +: MediaLocation({}, parent, point, livePeriod, QString(), QString()) { } MediaLocation::MediaLocation( @@ -1320,17 +1321,30 @@ MediaLocation::MediaLocation( const LocationPoint &point, const QString &title, const QString &description) +: MediaLocation({}, parent, point, TimeId(), title, description) { +} + +MediaLocation::MediaLocation( + PrivateTag, + not_null parent, + const LocationPoint &point, + TimeId livePeriod, + const QString &title, + const QString &description) : Media(parent) , _point(point) , _location(parent->history()->owner().location(point)) +, _livePeriod(livePeriod) , _title(title) , _description(description) { } std::unique_ptr MediaLocation::clone(not_null parent) { return std::make_unique( + PrivateTag(), parent, _point, + _livePeriod, _title, _description); } @@ -1339,8 +1353,14 @@ CloudImage *MediaLocation::location() const { return _location; } +QString MediaLocation::typeString() const { + return _livePeriod + ? tr::lng_live_location(tr::now) + : tr::lng_maps_point(tr::now); +} + ItemPreview MediaLocation::toPreview(ToPreviewOptions options) const { - const auto type = tr::lng_maps_point(tr::now); + const auto type = typeString(); const auto hasMiniImages = false; const auto text = TextWithEntities{ .text = _title }; return { @@ -1349,9 +1369,7 @@ ItemPreview MediaLocation::toPreview(ToPreviewOptions options) const { } TextWithEntities MediaLocation::notificationText() const { - return WithCaptionNotificationText( - tr::lng_maps_point(tr::now), - { .text = _title }); + return WithCaptionNotificationText(typeString(), { .text = _title }); } QString MediaLocation::pinnedTextSubstring() const { @@ -1360,7 +1378,7 @@ QString MediaLocation::pinnedTextSubstring() const { TextForMimeData MediaLocation::clipboardText() const { auto result = TextForMimeData::Simple( - u"[ "_q + tr::lng_maps_point(tr::now) + u" ]\n"_q); + u"[ "_q + typeString() + u" ]\n"_q); auto titleResult = TextUtilities::ParseEntities( _title, Ui::WebpageTextTitleOptions().flags); @@ -1389,12 +1407,19 @@ std::unique_ptr MediaLocation::createView( not_null message, not_null realParent, HistoryView::Element *replacing) { - return std::make_unique( - message, - _location, - _point, - _title, - _description); + return _livePeriod + ? std::make_unique( + message, + _location, + _point, + replacing, + _livePeriod) + : std::make_unique( + message, + _location, + _point, + _title, + _description); } MediaCall::MediaCall(not_null parent, const Call &call) diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 1aae67928ebcb2..c486799cd43a8d 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -331,10 +331,14 @@ class MediaContact final : public Media { }; class MediaLocation final : public Media { + struct PrivateTag { + }; + public: MediaLocation( not_null parent, - const LocationPoint &point); + const LocationPoint &point, + TimeId livePeriod = 0); MediaLocation( not_null parent, const LocationPoint &point, @@ -356,9 +360,21 @@ class MediaLocation final : public Media { not_null realParent, HistoryView::Element *replacing = nullptr) override; + MediaLocation( + PrivateTag, + not_null parent, + const LocationPoint &point, + TimeId livePeriod, + const QString &title, + const QString &description); + private: + + [[nodiscard]] QString typeString() const; + LocationPoint _point; not_null _location; + TimeId _livePeriod = 0; QString _title; QString _description; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 0695742bb029c2..171480ab60a9c7 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -224,10 +224,12 @@ std::unique_ptr HistoryItem::CreateMedia( return nullptr; }); }, [&](const MTPDmessageMediaGeoLive &media) -> Result { + const auto period = media.vperiod().v; return media.vgeo().match([&](const MTPDgeoPoint &point) -> Result { return std::make_unique( item, - Data::LocationPoint(point)); + Data::LocationPoint(point), + media.vperiod().v); }, [](const MTPDgeoPointEmpty &) -> Result { return nullptr; }); diff --git a/Telegram/SourceFiles/history/view/media/history_view_location.cpp b/Telegram/SourceFiles/history/view/media/history_view_location.cpp index 5fab03d159c70e..f16d65141c678e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_location.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_location.cpp @@ -7,12 +7,14 @@ For license and copyright information please follow this link: */ #include "history/view/media/history_view_location.h" +#include "base/unixtime.h" #include "history/history.h" #include "history/history_item_components.h" #include "history/history_item.h" #include "history/history_location_manager.h" #include "history/view/history_view_element.h" #include "history/view/history_view_cursor_state.h" +#include "lang/lang_keys.h" #include "ui/chat/chat_style.h" #include "ui/image/image.h" #include "ui/text/text_options.h" @@ -24,6 +26,98 @@ For license and copyright information please follow this link: #include "styles/style_chat.h" namespace HistoryView { +namespace { + +constexpr auto kUntilOffPeriod = std::numeric_limits::max(); +constexpr auto kLiveElapsedPartOpacity = 0.2; + +[[nodiscard]] TimeId ResolveUpdateDate(not_null view) { + const auto item = view->data(); + const auto edited = item->Get(); + return edited ? edited->date : item->date(); +} + +[[nodiscard]] QString RemainingTimeText( + not_null view, + TimeId period) { + if (period == kUntilOffPeriod) { + return QString(1, QChar(0x221E)); + } + const auto elapsed = base::unixtime::now() - view->data()->date(); + const auto remaining = std::clamp(period - elapsed, 0, period); + if (remaining < 10) { + return tr::lng_seconds_tiny(tr::now, lt_count, remaining); + } else if (remaining < 600) { + return tr::lng_minutes_tiny(tr::now, lt_count, remaining / 60); + } else if (remaining < 3600) { + return QString::number(remaining / 60); + } else if (remaining < 86400) { + return tr::lng_hours_tiny(tr::now, lt_count, remaining / 3600); + } + return tr::lng_days_tiny(tr::now, lt_count, remaining / 86400); +} + +[[nodiscard]] float64 RemainingTimeProgress( + not_null view, + TimeId period) { + if (period == kUntilOffPeriod) { + return 1.; + } else if (period < 1) { + return 0.; + } + const auto elapsed = base::unixtime::now() - view->data()->date(); + return std::clamp(period - elapsed, 0, period) / float64(period); +} + +} // namespace + +struct Location::Live { + explicit Live(TimeId period) : period(period) { + } + + base::Timer updateStatusTimer; + base::Timer updateRemainingTimer; + QImage previous; + QImage previousCache; + Ui::BubbleRounding previousRounding; + Ui::Animations::Simple crossfade; + TimeId period = 0; + int thumbnailHeight = 0; +}; + +Location::Location( + not_null parent, + not_null data, + Data::LocationPoint point, + Element *replacing, + TimeId livePeriod) +: Media(parent) +, _data(data) +, _live(CreateLiveTracker(parent, livePeriod)) +, _title(st::msgMinWidth) +, _description(st::msgMinWidth) +, _link(std::make_shared(point)) { + if (_live) { + _title.setText( + st::webPageTitleStyle, + tr::lng_live_location(tr::now), + Ui::WebpageTextTitleOptions()); + _live->updateStatusTimer.setCallback([=] { + updateLiveStatus(); + checkLiveFinish(); + }); + _live->updateRemainingTimer.setCallback([=] { + checkLiveFinish(); + }); + updateLiveStatus(); + if (const auto media = replacing ? replacing->media() : nullptr) { + _live->previous = media->locationTakeImage(); + if (!_live->previous.isNull()) { + history()->owner().registerHeavyViewPart(_parent); + } + } + } +} Location::Location( not_null parent, @@ -53,18 +147,106 @@ Location::Location( } Location::~Location() { - if (_media) { - _media = nullptr; + if (hasHeavyPart()) { + unloadHeavyPart(); _parent->checkHeavyPart(); } } +void Location::checkLiveFinish() { + Expects(_live != nullptr); + + const auto now = base::unixtime::now(); + const auto item = _parent->data(); + const auto start = item->date(); + if (_live->period != kUntilOffPeriod && now - start >= _live->period) { + _live = nullptr; + item->history()->owner().requestViewResize(_parent); + } else { + _parent->repaint(); + } +} + +std::unique_ptr Location::CreateLiveTracker( + not_null parent, + TimeId period) { + if (!period) { + return nullptr; + } + const auto now = base::unixtime::now(); + const auto date = parent->data()->date(); + return (now < date || now - date < period) + ? std::make_unique(period) + : nullptr; +} + +void Location::updateLiveStatus() { + const auto date = ResolveUpdateDate(_parent); + const auto now = base::unixtime::now(); + const auto elapsed = now - date; + auto next = TimeId(); + const auto text = [&] { + if (elapsed < 60) { + next = 60 - elapsed; + return tr::lng_live_location_now(tr::now); + } else if (const auto minutes = elapsed / 60; minutes < 60) { + next = 60 - (elapsed % 60); + return tr::lng_live_location_minutes(tr::now, lt_count, minutes); + } else if (const auto hours = elapsed / 3600; hours < 12) { + next = 3600 - (elapsed % 3600); + return tr::lng_live_location_hours(tr::now, lt_count, hours); + } + const auto dateFull = base::unixtime::parse(date); + const auto nowFull = base::unixtime::parse(now); + const auto nextTomorrow = [&] { + const auto tomorrow = nowFull.date().addDays(1); + next = nowFull.secsTo(QDateTime(tomorrow, QTime(0, 0))); + }; + const auto locale = QLocale(); + const auto format = QLocale::ShortFormat; + if (dateFull.date() == nowFull.date()) { + nextTomorrow(); + const auto time = locale.toString(dateFull.time(), format); + return tr::lng_live_location_today(tr::now, lt_time, time); + } else if (dateFull.date().addDays(1) == nowFull.date()) { + nextTomorrow(); + const auto time = locale.toString(dateFull.time(), format); + return tr::lng_live_location_yesterday(tr::now, lt_time, time); + } + return tr::lng_live_location_date_time( + tr::now, + lt_date, + locale.toString(dateFull.date(), format), + lt_time, + locale.toString(dateFull.time(), format)); + }(); + _description.setMarkedText( + st::webPageDescriptionStyle, + { text }, + Ui::WebpageTextDescriptionOptions()); + if (next > 0 && next < 86400) { + _live->updateStatusTimer.callOnce(next * crl::time(1000)); + } +} + +QImage Location::locationTakeImage() { + if (_media && !_media->isNull()) { + return *_media; + } else if (_live && !_live->previous.isNull()) { + return _live->previous; + } + return {}; +} + void Location::unloadHeavyPart() { _media = nullptr; + if (_live) { + _live->previous = QImage(); + } } bool Location::hasHeavyPart() const { - return (_media != nullptr); + return (_media != nullptr) || (_live && !_live->previous.isNull()); } void Location::ensureMediaCreated() const { @@ -99,8 +281,14 @@ QSize Location::countOptimalSize() { } if (!_title.isEmpty() || !_description.isEmpty()) { minHeight += st::mediaInBubbleSkip; - if (isBubbleTop()) { - minHeight += st::msgPadding.top(); + if (_live) { + if (isBubbleBottom()) { + minHeight += st::msgPadding.bottom(); + } + } else { + if (isBubbleTop()) { + minHeight += st::msgPadding.top(); + } } } } @@ -128,6 +316,9 @@ QSize Location::countCurrentSize(int newWidth) { std::min(newWidth, st::maxMediaSize)); accumulate_max(newWidth, minWidth); accumulate_max(newHeight, st::minPhotoSize); + if (_live) { + _live->thumbnailHeight = newHeight; + } if (_parent->hasBubble()) { if (!_title.isEmpty()) { newHeight += qMin(_title.countHeight(newWidth - st::msgPadding.left() - st::msgPadding.right()), st::webPageTitleFont->height * 2); @@ -137,8 +328,14 @@ QSize Location::countCurrentSize(int newWidth) { } if (!_title.isEmpty() || !_description.isEmpty()) { newHeight += st::mediaInBubbleSkip; - if (isBubbleTop()) { - newHeight += st::msgPadding.top(); + if (_live) { + if (isBubbleBottom()) { + newHeight += st::msgPadding.bottom(); + } + } else { + if (isBubbleTop()) { + newHeight += st::msgPadding.top(); + } } } } @@ -156,20 +353,27 @@ TextSelection Location::fromDescriptionSelection( } void Location::draw(Painter &p, const PaintContext &context) const { - if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; + if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) { + return; + } auto paintx = 0, painty = 0, paintw = width(), painth = height(); bool bubble = _parent->hasBubble(); const auto st = context.st; const auto stm = context.messageStyle(); const auto hasText = !_title.isEmpty() || !_description.isEmpty(); - const auto rounding = adjustedBubbleRounding( - hasText ? RectPart::FullTop : RectPart()); - if (bubble) { - if (hasText) { - if (isBubbleTop()) { - painty += st::msgPadding.top(); - } + const auto rounding = adjustedBubbleRounding(_live + ? RectPart::FullBottom + : hasText + ? RectPart::FullTop + : RectPart()); + const auto paintText = [&] { + if (_live) { + painty += st::mediaInBubbleSkip; + } else if (!hasText) { + return; + } else if (isBubbleTop()) { + painty += st::msgPadding.top(); } auto textw = width() - st::msgPadding.left() - st::msgPadding.right(); @@ -180,24 +384,45 @@ void Location::draw(Painter &p, const PaintContext &context) const { painty += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height); } if (!_description.isEmpty()) { + if (_live) { + p.setPen(stm->msgDateFg); + } _description.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 3, style::al_left, 0, -1, 0, false, toDescriptionSelection(context.selection)); painty += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height); } - if (!_title.isEmpty() || !_description.isEmpty()) { + if (!_live) { painty += st::mediaInBubbleSkip; + painth -= painty; } - painth -= painty; + }; + if (!_live) { + paintText(); } - auto rthumb = QRect(paintx, painty, paintw, painth); + const auto thumbh = _live ? _live->thumbnailHeight : painth; + auto rthumb = QRect(paintx, painty, paintw, thumbh); if (!bubble) { fillImageShadow(p, rthumb, rounding, context); } ensureMediaCreated(); validateImageCache(rthumb.size(), rounding); - if (!_imageCache.isNull()) { + const auto paintPrevious = _live && !_live->previous.isNull(); + auto opacity = _imageCache.isNull() ? 0. : 1.; + if (paintPrevious) { + opacity = _live->crossfade.value(opacity); + if (opacity < 1.) { + p.drawImage(rthumb.topLeft(), _live->previousCache); + if (opacity > 0.) { + p.setOpacity(opacity); + } + } + } + if (!_imageCache.isNull() && opacity > 0.) { p.drawImage(rthumb.topLeft(), _imageCache); - } else if (!bubble) { + if (opacity < 1.) { + p.setOpacity(1.); + } + } else if (!bubble && !paintPrevious) { Ui::PaintBubble( p, Ui::SimpleBubble{ @@ -223,8 +448,12 @@ void Location::draw(Painter &p, const PaintContext &context) const { if (context.selected()) { fillImageOverlay(p, rthumb, rounding, context); } - - if (_parent->media() == this) { + if (_live) { + painty += _live->thumbnailHeight; + painth -= _live->thumbnailHeight; + paintLiveRemaining(p, context, { paintx, painty, paintw, painth }); + paintText(); + } else if (_parent->media() == this) { auto fullRight = paintx + paintw; auto fullBottom = height(); _parent->drawInfo( @@ -244,25 +473,114 @@ void Location::draw(Painter &p, const PaintContext &context) const { } } +void Location::paintLiveRemaining( + QPainter &p, + const PaintContext &context, + QRect bottom) const { + const auto size = st::liveLocationRemainingSize; + const auto skip = (bottom.height() - size) / 2; + const auto rect = QRect( + bottom.x() + bottom.width() - size - skip, + bottom.y() + skip, + size, + size); + auto hq = PainterHighQualityEnabler(p); + const auto stm = context.messageStyle(); + const auto color = stm->msgServiceFg->c; + const auto untiloff = (_live->period == kUntilOffPeriod); + const auto progress = RemainingTimeProgress(_parent, _live->period); + const auto part = 1. / 360; + const auto full = (progress >= 1. - part); + auto elapsed = color; + if (!full) { + elapsed.setAlphaF(elapsed.alphaF() * kLiveElapsedPartOpacity); + } + auto pen = QPen(elapsed); + const auto stroke = style::ConvertScaleExact(2.); + pen.setWidthF(stroke); + p.setPen(pen); + p.setBrush(Qt::NoBrush); + p.drawEllipse(rect); + + if (untiloff) { + stm->liveLocationLongIcon.paintInCenter(p, rect); + } else { + if (!full && progress > part) { + auto pen = QPen(color); + pen.setWidthF(stroke); + p.setPen(pen); + p.drawArc(rect, 90 * 16, int(base::SafeRound(360 * 16 * progress))); + } + + p.setPen(stm->msgServiceFg); + p.setFont(st::semiboldFont); + const auto text = RemainingTimeText(_parent, _live->period); + p.drawText(rect, text, style::al_center); + const auto each = std::clamp(_live->period / 360, 1, 86400); + _live->updateRemainingTimer.callOnce(each * crl::time(1000)); + } +} + void Location::validateImageCache( QSize outer, Ui::BubbleRounding rounding) const { Expects(_media != nullptr); + if (_live && !_live->previous.isNull()) { + validateImageCache( + _live->previous, + _live->previousCache, + _live->previousRounding, + outer, + rounding); + } + validateImageCache( + *_media, + _imageCache, + _imageCacheRounding, + outer, + rounding); + checkLiveCrossfadeStart(); +} + +void Location::checkLiveCrossfadeStart() const { + if (!_live + || _live->previous.isNull() + || !_media + || _media->isNull() + || _live->crossfade.animating()) { + return; + } + _live->crossfade.start([=] { + if (!_live->crossfade.animating()) { + _live->previous = QImage(); + _live->previousCache = QImage(); + } + _parent->repaint(); + }, 0., 1., st::fadeWrapDuration); +} + +void Location::validateImageCache( + const QImage &source, + QImage &cache, + Ui::BubbleRounding &cacheRounding, + QSize outer, + Ui::BubbleRounding rounding) const { + if (source.isNull()) { + return; + } const auto ratio = style::DevicePixelRatio(); - if ((_imageCache.size() == (outer * ratio) - && _imageCacheRounding == rounding) - || _media->isNull()) { + if (cache.size() == (outer * ratio) && cacheRounding == rounding) { return; } - _imageCache = Images::Round( - _media->scaled( + cache = Images::Round( + source.scaled( outer * ratio, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), MediaRoundingMask(rounding)); - _imageCache.setDevicePixelRatio(ratio); - _imageCacheRounding = rounding; + cache.setDevicePixelRatio(ratio); + cacheRounding = rounding; } TextState Location::textState(QPoint point, StateRequest request) const { @@ -275,11 +593,13 @@ TextState Location::textState(QPoint point, StateRequest request) const { auto paintx = 0, painty = 0, paintw = width(), painth = height(); bool bubble = _parent->hasBubble(); - if (bubble) { - if (!_title.isEmpty() || !_description.isEmpty()) { - if (isBubbleTop()) { - painty += st::msgPadding.top(); - } + auto checkText = [&] { + if (_live) { + painty += st::mediaInBubbleSkip; + } else if (_title.isEmpty() && _description.isEmpty()) { + return false; + } else if (isBubbleTop()) { + painty += st::msgPadding.top(); } auto textw = width() - st::msgPadding.left() - st::msgPadding.right(); @@ -292,7 +612,7 @@ TextState Location::textState(QPoint point, StateRequest request) const { textw, width(), request.forText())); - return result; + return true; } else if (point.y() >= painty + titleh) { symbolAdd += _title.length(); } @@ -306,6 +626,8 @@ TextState Location::textState(QPoint point, StateRequest request) const { textw, width(), request.forText())); + result.symbol += symbolAdd; + return true; } else if (point.y() >= painty + descriptionh) { symbolAdd += _description.length(); } @@ -315,11 +637,22 @@ TextState Location::textState(QPoint point, StateRequest request) const { painty += st::mediaInBubbleSkip; } painth -= painty; + return false; + }; + if (!_live && checkText()) { + return result; } - if (QRect(paintx, painty, paintw, painth).contains(point) && _data) { + const auto thumbh = _live ? _live->thumbnailHeight : painth; + if (QRect(paintx, painty, paintw, thumbh).contains(point) && _data) { result.link = _link; } - if (_parent->media() == this) { + if (_live) { + painty += _live->thumbnailHeight; + painth -= _live->thumbnailHeight; + if (checkText()) { + return result; + } + } else if (_parent->media() == this) { auto fullRight = paintx + paintw; auto fullBottom = height(); const auto bottomInfoResult = _parent->bottomInfoTextState( diff --git a/Telegram/SourceFiles/history/view/media/history_view_location.h b/Telegram/SourceFiles/history/view/media/history_view_location.h index ec412fd00c58d7..9e431ef740244a 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_location.h +++ b/Telegram/SourceFiles/history/view/media/history_view_location.h @@ -22,8 +22,14 @@ class Location : public Media { not_null parent, not_null data, Data::LocationPoint point, - const QString &title = QString(), - const QString &description = QString()); + Element *replacing = nullptr, + TimeId livePeriod = 0); + Location( + not_null parent, + not_null data, + Data::LocationPoint point, + const QString &title, + const QString &description); ~Location(); void draw(Painter &p, const PaintContext &context) const override; @@ -58,15 +64,33 @@ class Location : public Media { return isRoundedInBubbleBottom(); } + QImage locationTakeImage() override; + void unloadHeavyPart() override; bool hasHeavyPart() const override; private: + struct Live; + [[nodiscard]] static std::unique_ptr CreateLiveTracker( + not_null parent, + TimeId period); + void ensureMediaCreated() const; void validateImageCache( QSize outer, Ui::BubbleRounding rounding) const; + void validateImageCache( + const QImage &source, + QImage &cache, + Ui::BubbleRounding &cacheRounding, + QSize outer, + Ui::BubbleRounding rounding) const; + + void paintLiveRemaining( + QPainter &p, + const PaintContext &context, + QRect bottom) const; QSize countOptimalSize() override; QSize countCurrentSize(int newWidth) override; @@ -79,7 +103,12 @@ class Location : public Media { [[nodiscard]] int fullWidth() const; [[nodiscard]] int fullHeight() const; + void checkLiveCrossfadeStart() const; + void updateLiveStatus(); + void checkLiveFinish(); + const not_null _data; + mutable std::unique_ptr _live; mutable std::shared_ptr _media; Ui::Text::String _title, _description; ClickHandlerPtr _link; diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.cpp b/Telegram/SourceFiles/history/view/media/history_view_media.cpp index f64cb808b19992..56720e83f8a77d 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media.cpp @@ -356,6 +356,10 @@ std::unique_ptr Media::stickerTakePlayer( return nullptr; } +QImage Media::locationTakeImage() { + return QImage(); +} + TextState Media::getStateGrouped( const QRect &geometry, RectParts sides, diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.h b/Telegram/SourceFiles/history/view/media/history_view_media.h index 1b835dcce4d067..f9ad5c987d4564 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media.h @@ -190,6 +190,7 @@ class Media : public Object, public base::has_weak_ptr { virtual std::unique_ptr stickerTakePlayer( not_null data, const Lottie::ColorReplacements *replacements); + virtual QImage locationTakeImage(); virtual void checkAnimation() { } diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 2cab84839cdc71..5dd77998a2da13 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -1070,3 +1070,9 @@ chatIntroWidth: 224px; chatIntroTitleMargin: margins(11px, 16px, 11px, 4px); chatIntroMargin: margins(11px, 0px, 11px, 0px); chatIntroStickerPadding: margins(10px, 8px, 10px, 16px); + +liveLocationLongInIcon: icon {{ "chat/live_location_long", msgInServiceFg }}; +liveLocationLongInIconSelected: icon {{ "chat/live_location_long", msgInServiceFgSelected }}; +liveLocationLongOutIcon: icon {{ "chat/live_location_long", msgOutServiceFg }}; +liveLocationLongOutIconSelected: icon {{ "chat/live_location_long", msgOutServiceFgSelected }}; +liveLocationRemainingSize: 28px; diff --git a/Telegram/SourceFiles/ui/chat/chat_style.cpp b/Telegram/SourceFiles/ui/chat/chat_style.cpp index 9470d71d687faa..a532a31805dcb9 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style.cpp @@ -543,6 +543,12 @@ ChatStyle::ChatStyle(rpl::producer colorIndices) { st::historyVoiceMessageInTTLSelected, st::historyVoiceMessageOutTTL, st::historyVoiceMessageOutTTLSelected); + make( + &MessageStyle::liveLocationLongIcon, + st::liveLocationLongInIcon, + st::liveLocationLongInIconSelected, + st::liveLocationLongOutIcon, + st::liveLocationLongOutIconSelected); updateDarkValue(); } diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index 1053d630a8d2af..6b8cc459ba059f 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -92,6 +92,7 @@ struct MessageStyle { style::icon historyTranscribeLock = { Qt::Uninitialized }; style::icon historyTranscribeHide = { Qt::Uninitialized }; style::icon historyVoiceMessageTTL = { Qt::Uninitialized }; + style::icon liveLocationLongIcon = { Qt::Uninitialized }; std::array< std::unique_ptr, kColorPatternsCount> quoteCache; diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index ce00e278ef620a..6351f95c6ab2c4 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -1510,22 +1510,82 @@ def runStages(): """) if buildQt6: - stage('qt_6_2_8', """ + qt6version = '6.2.8' if mac else '6.7.0' + qt6version_ = '6_2_8' if mac else '6_7_0' + arg0 = 'qt_' + qt6version_ + arg1 = qt6version + + stage('qt_' + qt6version_, """ +win: + git clone -b v{1} https://github.com/qt/qt5.git {0} mac: - git clone -b v6.2.8-lts-lgpl https://github.com/qt/qt5.git qt_6_2_8 - cd qt_6_2_8 + git clone -b v{1}-lts-lgpl https://github.com/qt/qt5.git {0} +common: + cd {0} git submodule update --init --recursive qtbase qtimageformats qtsvg -depends:patches/qtbase_6.2.8/*.patch +depends:patches/qtbase_{1}/*.patch cd qtbase - find ../../patches/qtbase_6.2.8 -type f -print0 | sort -z | xargs -0 git apply -v +win: + for /r %%i in (..\\..\\patches\\qtbase_{1}\\*) do git apply %%i -v + cd .. + + SET CONFIGURATIONS=-debug +release: + SET CONFIGURATIONS=-debug-and-release +win: + """.format(arg0, arg1) + removeDir("\"%LIBS_DIR%\\Qt-\"" + qt6version) + """ + SET ANGLE_DIR=%LIBS_DIR%\\tg_angle + SET ANGLE_LIBS_DIR=%ANGLE_DIR%\\out + SET MOZJPEG_DIR=%LIBS_DIR%\\mozjpeg + SET OPENSSL_DIR=%LIBS_DIR%\\openssl3 + SET OPENSSL_LIBS_DIR=%OPENSSL_DIR%\\out + SET ZLIB_LIBS_DIR=%LIBS_DIR%\\zlib + SET WEBP_DIR=%LIBS_DIR%\\libwebp + configure -prefix "%LIBS_DIR%\\Qt-{1}" ^ + %CONFIGURATIONS% ^ + -force-debug-info ^ + -opensource ^ + -confirm-license ^ + -static ^ + -static-runtime ^ + -opengl es2 -no-angle ^ + -I "%ANGLE_DIR%\\include" ^ + -D "KHRONOS_STATIC=" ^ + -D "DESKTOP_APP_QT_STATIC_ANGLE=" ^ + QMAKE_LIBS_OPENGL_ES2_DEBUG="%ANGLE_LIBS_DIR%\\Debug\\tg_angle.lib %ZLIB_LIBS_DIR%\\Debug\\zlibstaticd.lib d3d9.lib dxgi.lib dxguid.lib" ^ + QMAKE_LIBS_OPENGL_ES2_RELEASE="%ANGLE_LIBS_DIR%\\Release\\tg_angle.lib %ZLIB_LIBS_DIR%\\Release\\zlibstatic.lib d3d9.lib dxgi.lib dxguid.lib" ^ + -egl ^ + QMAKE_LIBS_EGL_DEBUG="%ANGLE_LIBS_DIR%\\Debug\\tg_angle.lib %ZLIB_LIBS_DIR%\\Debug\\zlibstaticd.lib d3d9.lib dxgi.lib dxguid.lib Gdi32.lib User32.lib" ^ + QMAKE_LIBS_EGL_RELEASE="%ANGLE_LIBS_DIR%\\Release\\tg_angle.lib %ZLIB_LIBS_DIR%\\Release\\zlibstatic.lib d3d9.lib dxgi.lib dxguid.lib Gdi32.lib User32.lib" ^ + -openssl-linked ^ + -I "%OPENSSL_DIR%\\include" ^ + OPENSSL_LIBS_DEBUG="%OPENSSL_LIBS_DIR%.dbg\\libssl.lib %OPENSSL_LIBS_DIR%.dbg\\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" ^ + OPENSSL_LIBS_RELEASE="%OPENSSL_LIBS_DIR%\\libssl.lib %OPENSSL_LIBS_DIR%\\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" ^ + -I "%MOZJPEG_DIR%" ^ + LIBJPEG_LIBS_DEBUG="%MOZJPEG_DIR%\\Debug\\jpeg-static.lib" ^ + LIBJPEG_LIBS_RELEASE="%MOZJPEG_DIR%\\Release\\jpeg-static.lib" ^ + -system-webp ^ + -I "%WEBP_DIR%\\src" ^ + -L "%WEBP_DIR%\\out\\release-static\\$X8664\\lib" ^ + -mp ^ + -no-feature-netlistmgr ^ + -nomake examples ^ + -nomake tests ^ + -platform win32-msvc + + jom -j%NUMBER_OF_PROCESSORS% + jom -j%NUMBER_OF_PROCESSORS% install + +mac: + find ../../patches/qtbase_{1} -type f -print0 | sort -z | xargs -0 git apply -v cd .. - sed -i.bak 's/tqtc-//' {qtimageformats,qtsvg}/dependencies.yaml + sed -i.bak 's/tqtc-//' {{qtimageformats,qtsvg}}/dependencies.yaml CONFIGURATIONS=-debug release: CONFIGURATIONS=-debug-and-release mac: - ./configure -prefix "$USED_PREFIX/Qt-6.2.8" \ + ./configure -prefix "$USED_PREFIX/Qt-{1}" \ $CONFIGURATIONS \ -force-debug-info \ -opensource \ @@ -1546,7 +1606,7 @@ def runStages(): ninja ninja install -""") +""".format(arg0, arg1)) stage('tg_owt', """ git clone https://github.com/desktop-app/tg_owt.git From 63e1731d7ca320a98f4e3bd087e45d6a7e8a7e65 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 30 Apr 2024 18:37:31 +0400 Subject: [PATCH 05/40] Fix premium preview bullet-dots. Regression was introduced in 88751896af. --- Telegram/SourceFiles/boxes/premium_preview_box.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.cpp b/Telegram/SourceFiles/boxes/premium_preview_box.cpp index da1ab9064bf3a6..30f3127451b556 100644 --- a/Telegram/SourceFiles/boxes/premium_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_preview_box.cpp @@ -913,7 +913,7 @@ void PreviewBox( auto businessOrder = Settings::BusinessFeaturesOrder(&show->session()); state->order = ranges::contains(businessOrder, descriptor.section) ? std::move(businessOrder) - : ranges::contains(businessOrder, descriptor.section) + : ranges::contains(premiumOrder, descriptor.section) ? std::move(premiumOrder) : std::vector{ descriptor.section }; From bb6fd4bc4dbe36f3988d0baf5a60a32893d874f1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 30 Apr 2024 22:30:39 +0400 Subject: [PATCH 06/40] Update scheme, new sticker categories. --- Telegram/SourceFiles/api/api_statistics.cpp | 8 +- .../chat_helpers/emoji_list_widget.cpp | 7 +- .../chat_helpers/gifs_list_widget.cpp | 2 +- .../chat_helpers/stickers_list_widget.cpp | 30 +++- .../chat_helpers/tabbed_selector.cpp | 9 +- .../chat_helpers/tabbed_selector.h | 9 +- .../SourceFiles/data/data_emoji_statuses.cpp | 38 +++-- .../SourceFiles/data/data_emoji_statuses.h | 3 + .../data/stickers/data_stickers.cpp | 135 ++++++++++++++++++ .../SourceFiles/data/stickers/data_stickers.h | 2 + Telegram/SourceFiles/mtproto/scheme/api.tl | 7 +- .../SourceFiles/ui/controls/tabbed_search.cpp | 9 +- .../SourceFiles/ui/controls/tabbed_search.h | 9 ++ 13 files changed, 238 insertions(+), 30 deletions(-) diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index 7139c5ebb10549..ce1966623cd2bb 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -760,14 +760,14 @@ rpl::producer EarnStatistics::request() { channel()->inputChannel )).done([=](const MTPstats_BroadcastRevenueStats &result) { const auto &data = result.data(); - + const auto &balances = data.vbalances().data(); _data = Data::EarnStatistics{ .topHoursGraph = StatisticalGraphFromTL( data.vtop_hours_graph()), .revenueGraph = StatisticalGraphFromTL(data.vrevenue_graph()), - .currentBalance = data.vcurrent_balance().v, - .availableBalance = data.vavailable_balance().v, - .overallRevenue = data.voverall_revenue().v, + .currentBalance = balances.vcurrent_balance().v, + .availableBalance = balances.vavailable_balance().v, + .overallRevenue = balances.voverall_revenue().v, .usdRate = data.vusd_rate().v, }; diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index c8148ae8e48758..2157e916827ed4 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -573,12 +573,17 @@ EmojiListWidget::~EmojiListWidget() { void EmojiListWidget::setupSearch() { const auto session = &_show->session(); + const auto type = (_mode == Mode::EmojiStatus) + ? TabbedSearchType::Status + : (_mode == Mode::UserpicBuilder) + ? TabbedSearchType::ProfilePhoto + : TabbedSearchType::Emoji; _search = MakeSearch(this, st(), [=](std::vector &&query) { _nextSearchQuery = std::move(query); InvokeQueued(this, [=] { applyNextSearchQuery(); }); - }, session, (_mode == Mode::EmojiStatus), _mode == Mode::UserpicBuilder); + }, session, type); } void EmojiListWidget::applyNextSearchQuery() { diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 72794de39b259f..609f21989d9776 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -820,7 +820,7 @@ void GifsListWidget::setupSearch() { : SearchEmojiSectionSetId(); refreshIcons(); searchForGifs(accumulated); - }, session); + }, session, TabbedSearchType::Emoji); } int32 GifsListWidget::showInlineRows(bool newResults) { diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 42ea6b934b5108..07a02caa92b4c9 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -547,6 +547,15 @@ void StickersListWidget::sendSearchRequest() { } _search->setLoading(true); + + if (_searchQuery == Ui::PremiumGroupFakeEmoticon()) { + _search->setLoading(false); + _searchRequestId = 0; + _searchCache.emplace(_searchQuery, std::vector()); + showSearchResults(); + return; + } + const auto hash = uint64(0); _searchRequestId = _api.request(MTPmessages_SearchStickerSets( MTP_flags(0), @@ -570,10 +579,14 @@ void StickersListWidget::searchForSets( return; } - _filteredStickers = session().data().stickers().getListByEmoji( - std::move(emoji), - 0, - true); + if (query == Ui::PremiumGroupFakeEmoticon()) { + _filteredStickers = session().data().stickers().getPremiumList(0); + } else { + _filteredStickers = session().data().stickers().getListByEmoji( + std::move(emoji), + 0, + true); + } if (_searchQuery != cleaned) { _search->setLoading(false); if (const auto requestId = base::take(_searchRequestId)) { @@ -2604,15 +2617,18 @@ void StickersListWidget::beforeHiding() { void StickersListWidget::setupSearch() { const auto session = &_show->session(); + const auto type = (_mode == Mode::UserpicBuilder) + ? TabbedSearchType::ProfilePhoto + : TabbedSearchType::Stickers; _search = MakeSearch(this, st(), [=](std::vector &&query) { auto set = base::flat_set(); auto text = ranges::accumulate(query, QString(), []( - QString a, - QString b) { + QString a, + QString b) { return a.isEmpty() ? b : (a + ' ' + b); }); searchForSets(std::move(text), SearchEmoji(query, set)); - }, session, false, (_mode == Mode::UserpicBuilder)); + }, session, type); } void StickersListWidget::displaySet(uint64 setId) { diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index e57b3adc8a75f0..7f8a915c7d55e7 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -307,16 +307,17 @@ std::unique_ptr MakeSearch( const style::EmojiPan &st, Fn&&)> callback, not_null session, - bool statusCategories, - bool profilePhotoCategories) { + TabbedSearchType type) { using Descriptor = Ui::SearchDescriptor; const auto owner = &session->data(); auto result = std::make_unique(parent, st, Descriptor{ .st = st.search, - .groups = (profilePhotoCategories + .groups = ((type == TabbedSearchType::ProfilePhoto) ? owner->emojiStatuses().profilePhotoGroupsValue() - : statusCategories + : (type == TabbedSearchType::Status) ? owner->emojiStatuses().statusGroupsValue() + : (type == TabbedSearchType::Stickers) + ? owner->emojiStatuses().stickerGroupsValue() : owner->emojiStatuses().emojiGroupsValue()), .customEmojiFactory = owner->customEmojiManager().factory( Data::CustomEmojiManager::SizeTag::SetIcon, diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index 606f1a92640dab..f663d470a4d64b 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -98,13 +98,18 @@ struct TabbedSelectorDescriptor { ComposeFeatures features; }; +enum class TabbedSearchType { + Emoji, + Status, + ProfilePhoto, + Stickers, +}; [[nodiscard]] std::unique_ptr MakeSearch( not_null parent, const style::EmojiPan &st, Fn&&)> callback, not_null session, - bool statusCategories = false, - bool profilePhotoCategories = false); + TabbedSearchType type); class TabbedSelector : public Ui::RpWidget { public: diff --git a/Telegram/SourceFiles/data/data_emoji_statuses.cpp b/Telegram/SourceFiles/data/data_emoji_statuses.cpp index dd1ad583bc06d4..e00dd8910bc943 100644 --- a/Telegram/SourceFiles/data/data_emoji_statuses.cpp +++ b/Telegram/SourceFiles/data/data_emoji_statuses.cpp @@ -153,6 +153,11 @@ auto EmojiStatuses::statusGroupsValue() const -> rpl::producer { return _statusGroups.data.value(); } +auto EmojiStatuses::stickerGroupsValue() const -> rpl::producer { + const_cast(this)->requestStickerGroups(); + return _stickerGroups.data.value(); +} + auto EmojiStatuses::profilePhotoGroupsValue() const -> rpl::producer { const_cast(this)->requestProfilePhotoGroups(); @@ -172,6 +177,12 @@ void EmojiStatuses::requestStatusGroups() { MTPmessages_GetEmojiStatusGroups(MTP_int(_statusGroups.hash))); } +void EmojiStatuses::requestStickerGroups() { + requestGroups( + &_stickerGroups, + MTPmessages_GetEmojiStickerGroups(MTP_int(_stickerGroups.hash))); +} + void EmojiStatuses::requestProfilePhotoGroups() { requestGroups( &_profilePhotoGroups, @@ -185,15 +196,24 @@ void EmojiStatuses::requestProfilePhotoGroups() { auto result = std::vector(); result.reserve(list.size()); for (const auto &group : list) { - const auto &data = group.data(); - auto emoticons = ranges::views::all( - data.vemoticons().v - ) | ranges::views::transform([](const MTPstring &emoticon) { - return qs(emoticon); - }) | ranges::to_vector; - result.push_back({ - .iconId = QString::number(data.vicon_emoji_id().v), - .emoticons = std::move(emoticons), + group.match([&](const MTPDemojiGroupPremium &data) { + result.push_back({ + .iconId = QString::number(data.vicon_emoji_id().v), + .type = Ui::EmojiGroupType::Premium, + }); + }, [&](const auto &data) { + auto emoticons = ranges::views::all( + data.vemoticons().v + ) | ranges::views::transform([](const MTPstring &emoticon) { + return qs(emoticon); + }) | ranges::to_vector; + result.push_back({ + .iconId = QString::number(data.vicon_emoji_id().v), + .emoticons = std::move(emoticons), + .type = (MTPDemojiGroupGreeting::Is() + ? Ui::EmojiGroupType::Greeting + : Ui::EmojiGroupType::Normal), + }); }); } return result; diff --git a/Telegram/SourceFiles/data/data_emoji_statuses.h b/Telegram/SourceFiles/data/data_emoji_statuses.h index 5501eb8bc0437a..0a0b75ba222735 100644 --- a/Telegram/SourceFiles/data/data_emoji_statuses.h +++ b/Telegram/SourceFiles/data/data_emoji_statuses.h @@ -60,9 +60,11 @@ class EmojiStatuses final { using Groups = std::vector; [[nodiscard]] rpl::producer emojiGroupsValue() const; [[nodiscard]] rpl::producer statusGroupsValue() const; + [[nodiscard]] rpl::producer stickerGroupsValue() const; [[nodiscard]] rpl::producer profilePhotoGroupsValue() const; void requestEmojiGroups(); void requestStatusGroups(); + void requestStickerGroups(); void requestProfilePhotoGroups(); private: @@ -124,6 +126,7 @@ class EmojiStatuses final { GroupsType _emojiGroups; GroupsType _statusGroups; + GroupsType _stickerGroups; GroupsType _profilePhotoGroups; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.cpp b/Telegram/SourceFiles/data/stickers/data_stickers.cpp index f6da8553d79743..057e1d8cf30041 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.cpp +++ b/Telegram/SourceFiles/data/stickers/data_stickers.cpp @@ -1132,6 +1132,141 @@ void Stickers::gifsReceived(const QVector &items, uint64 hash) { notifySavedGifsUpdated(); } +std::vector> Stickers::getPremiumList(uint64 seed) { + struct StickerWithDate { + not_null document; + TimeId date = 0; + }; + auto result = std::vector(); + auto &sets = setsRef(); + auto setsToRequest = base::flat_map(); + + const auto add = [&](not_null document, TimeId date) { + if (ranges::find(result, document, [](const StickerWithDate &data) { + return data.document; + }) == result.end()) { + result.push_back({ document, date }); + } + }; + + constexpr auto kSlice = 65536; + const auto CreateSortKey = [&]( + not_null document, + int base) { + if (document->sticker() && document->sticker()->isAnimated()) { + base += kSlice; + } + return TimeId(base + int((document->id ^ seed) % kSlice)); + }; + const auto CreateRecentSortKey = [&](not_null document) { + return CreateSortKey(document, kSlice * 6); + }; + auto myCounter = 0; + const auto CreateMySortKey = [&](not_null document) { + auto base = kSlice * 6; + if (!document->sticker() || !document->sticker()->isAnimated()) { + base -= kSlice; + } + return (base - (++myCounter)); + }; + const auto CreateFeaturedSortKey = [&](not_null document) { + return CreateSortKey(document, kSlice * 2); + }; + const auto CreateOtherSortKey = [&](not_null document) { + return CreateSortKey(document, 0); + }; + const auto InstallDateAdjusted = [&]( + TimeId date, + not_null document) { + return (document->sticker() && document->sticker()->isAnimated()) + ? date + : date / 2; + }; + const auto RecentInstallDate = [&](not_null document) { + Expects(document->sticker() != nullptr); + + const auto sticker = document->sticker(); + if (sticker->set.id) { + const auto setIt = sets.find(sticker->set.id); + if (setIt != sets.end()) { + return InstallDateAdjusted(setIt->second->installDate, document); + } + } + return TimeId(0); + }; + + auto recentIt = sets.find(Stickers::CloudRecentSetId); + if (recentIt != sets.cend()) { + const auto recent = recentIt->second.get(); + const auto count = int(recent->stickers.size()); + result.reserve(count); + for (auto i = 0; i != count; ++i) { + const auto document = recent->stickers[i]; + auto index = i; + if (!document->isPremiumSticker()) { + continue; + } else { + index = recent->stickers.indexOf(document); + } + const auto usageDate = (recent->dates.empty() || index < 0) + ? 0 + : recent->dates[index]; + const auto date = usageDate + ? usageDate + : RecentInstallDate(document); + result.push_back({ + document, + date ? date : CreateRecentSortKey(document) }); + } + } + const auto addList = [&]( + const StickersSetsOrder &order, + SetFlag skip) { + for (const auto setId : order) { + auto it = sets.find(setId); + if (it == sets.cend() || (it->second->flags & skip)) { + continue; + } + const auto set = it->second.get(); + if (set->emoji.empty()) { + setsToRequest.emplace(set->id, set->accessHash); + set->flags |= SetFlag::NotLoaded; + continue; + } + const auto my = (set->flags & SetFlag::Installed); + result.reserve(result.size() + set->stickers.size()); + for (const auto document : set->stickers) { + if (!document->isPremiumSticker()) { + continue; + } + const auto installDate = my ? set->installDate : TimeId(0); + const auto date = (installDate > 1) + ? InstallDateAdjusted(installDate, document) + : my + ? CreateMySortKey(document) + : CreateFeaturedSortKey(document); + add(document, date); + } + } + }; + + addList(setsOrder(), SetFlag::Archived); + addList(featuredSetsOrder(), SetFlag::Installed); + + if (!setsToRequest.empty()) { + for (const auto &[setId, accessHash] : setsToRequest) { + session().api().scheduleStickerSetRequest(setId, accessHash); + } + session().api().requestStickerSets(); + } + + ranges::sort(result, std::greater<>(), &StickerWithDate::date); + + return result + | ranges::views::transform(&StickerWithDate::document) + | ranges::to_vector; +} + std::vector> Stickers::getListByEmoji( std::vector emoji, uint64 seed, diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.h b/Telegram/SourceFiles/data/stickers/data_stickers.h index d5a192b4be46f4..426b034aaae800 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.h +++ b/Telegram/SourceFiles/data/stickers/data_stickers.h @@ -235,6 +235,8 @@ class Stickers final { const MTPmessages_FeaturedStickers &result); void gifsReceived(const QVector &items, uint64 hash); + [[nodiscard]] std::vector> getPremiumList( + uint64 seed); [[nodiscard]] std::vector> getListByEmoji( std::vector emoji, uint64 seed, diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl index a75781792ad818..963785e0b413e8 100644 --- a/Telegram/SourceFiles/mtproto/scheme/api.tl +++ b/Telegram/SourceFiles/mtproto/scheme/api.tl @@ -1519,6 +1519,8 @@ emojiListNotModified#481eadfa = EmojiList; emojiList#7a1e11d1 hash:long document_id:Vector = EmojiList; emojiGroup#7a9abda9 title:string icon_emoji_id:long emoticons:Vector = EmojiGroup; +emojiGroupGreeting#80d26cc7 title:string icon_emoji_id:long emoticons:Vector = EmojiGroup; +emojiGroupPremium#93bcf34 title:string icon_emoji_id:long = EmojiGroup; messages.emojiGroupsNotModified#6fb4ad87 = messages.EmojiGroups; messages.emojiGroups#881fb94b hash:int groups:Vector = messages.EmojiGroups; @@ -1762,7 +1764,7 @@ channels.sponsoredMessageReportResultChooseOption#846f9e42 title:string options: channels.sponsoredMessageReportResultAdsHidden#3e3bcf2f = channels.SponsoredMessageReportResult; channels.sponsoredMessageReportResultReported#ad798849 = channels.SponsoredMessageReportResult; -stats.broadcastRevenueStats#d07b4bad top_hours_graph:StatsGraph revenue_graph:StatsGraph current_balance:long available_balance:long overall_revenue:long usd_rate:double = stats.BroadcastRevenueStats; +stats.broadcastRevenueStats#5407e297 top_hours_graph:StatsGraph revenue_graph:StatsGraph balances:BroadcastRevenueBalances usd_rate:double = stats.BroadcastRevenueStats; stats.broadcastRevenueWithdrawalUrl#ec659737 url:string = stats.BroadcastRevenueWithdrawalUrl; @@ -1777,6 +1779,8 @@ reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom; reactionsNotifySettings#56e34970 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings; +broadcastRevenueBalances#8438f1c6 current_balance:long available_balance:long overall_revenue:long = BroadcastRevenueBalances; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -2165,6 +2169,7 @@ messages.sendQuickReplyMessages#6c750de1 peer:InputPeer shortcut_id:int id:Vecto messages.deleteQuickReplyMessages#e105e910 shortcut_id:int id:Vector = Updates; messages.toggleDialogFilterTags#fd2dda49 enabled:Bool = Bool; messages.getMyStickers#d0b5e1fc offset_id:long limit:int = messages.MyStickers; +messages.getEmojiStickerGroups#1dd840f5 hash:int = messages.EmojiGroups; updates.getState#edd4882a = updates.State; updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference; diff --git a/Telegram/SourceFiles/ui/controls/tabbed_search.cpp b/Telegram/SourceFiles/ui/controls/tabbed_search.cpp index 6aaea70dc4895f..7acc65a8ffacfc 100644 --- a/Telegram/SourceFiles/ui/controls/tabbed_search.cpp +++ b/Telegram/SourceFiles/ui/controls/tabbed_search.cpp @@ -273,6 +273,11 @@ void GroupsStrip::fireChosenGroup() { } // namespace +const QString &PremiumGroupFakeEmoticon() { + static const auto result = u"*premium"_q; + return result; +} + SearchWithGroups::SearchWithGroups( QWidget *parent, SearchDescriptor descriptor) @@ -359,7 +364,9 @@ void SearchWithGroups::initGroups() { widget->chosen( ) | rpl::start_with_next([=](const GroupsStrip::Chosen &chosen) { _chosenGroup = chosen.group->iconId; - _query = chosen.group->emoticons; + _query = (chosen.group->type == EmojiGroupType::Premium) + ? std::vector{ PremiumGroupFakeEmoticon() } + : chosen.group->emoticons; _debouncedQuery = chosen.group->emoticons; _debounceTimer.cancel(); scrollGroupsToIcon(chosen.iconLeft, chosen.iconRight); diff --git a/Telegram/SourceFiles/ui/controls/tabbed_search.h b/Telegram/SourceFiles/ui/controls/tabbed_search.h index 000cbccea1765f..2a5d67e40e8d28 100644 --- a/Telegram/SourceFiles/ui/controls/tabbed_search.h +++ b/Telegram/SourceFiles/ui/controls/tabbed_search.h @@ -31,15 +31,24 @@ class RpWidget; template class FadeWrap; +enum class EmojiGroupType { + Normal, + Greeting, + Premium, +}; + struct EmojiGroup { QString iconId; std::vector emoticons; + EmojiGroupType type = EmojiGroupType::Normal; friend inline auto operator<=>( const EmojiGroup &a, const EmojiGroup &b) = default; }; +[[nodiscard]] const QString &PremiumGroupFakeEmoticon(); + struct SearchDescriptor { const style::TabbedSearch &st; rpl::producer> groups; From 16ce5ef046521bbbdb8e7def1f91379b6378520b Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 1 May 2024 12:47:12 +0400 Subject: [PATCH 07/40] Greeting category first in ChatIntro setup. --- .../chat_helpers/stickers_list_widget.cpp | 2 ++ .../chat_helpers/stickers_list_widget.h | 1 + .../chat_helpers/tabbed_selector.cpp | 27 ++++++++++++++++--- .../chat_helpers/tabbed_selector.h | 2 ++ .../settings/business/settings_chat_intro.cpp | 2 +- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 07a02caa92b4c9..e44b8aaefee162 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -2619,6 +2619,8 @@ void StickersListWidget::setupSearch() { const auto session = &_show->session(); const auto type = (_mode == Mode::UserpicBuilder) ? TabbedSearchType::ProfilePhoto + : (_mode == Mode::ChatIntro) + ? TabbedSearchType::Greeting : TabbedSearchType::Stickers; _search = MakeSearch(this, st(), [=](std::vector &&query) { auto set = base::flat_set(); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index 2e1ef9c1cdf531..aff2cf287ef936 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -65,6 +65,7 @@ enum class StickersListMode { Full, Masks, UserpicBuilder, + ChatIntro, }; struct StickersListDescriptor { diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 7f8a915c7d55e7..7de254badbebcb 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -302,6 +302,21 @@ void TabbedSelector::Tab::saveScrollTop() { _scrollTop = widget()->getVisibleTop(); } +[[nodiscard]] rpl::producer> GreetingGroupFirst( + not_null owner) { + return owner->emojiStatuses().stickerGroupsValue( + ) | rpl::map([](std::vector &&groups) { + const auto i = ranges::find( + groups, + Ui::EmojiGroupType::Greeting, + &Ui::EmojiGroup::type); + if (i != begin(groups) && i != end(groups)) { + ranges::rotate(begin(groups), i, i + 1); + } + return std::move(groups); + }); +} + std::unique_ptr MakeSearch( not_null parent, const style::EmojiPan &st, @@ -318,6 +333,8 @@ std::unique_ptr MakeSearch( ? owner->emojiStatuses().statusGroupsValue() : (type == TabbedSearchType::Stickers) ? owner->emojiStatuses().stickerGroupsValue() + : (type == TabbedSearchType::Greeting) + ? GreetingGroupFirst(owner) : owner->emojiStatuses().emojiGroupsValue()), .customEmojiFactory = owner->customEmojiManager().factory( Data::CustomEmojiManager::SizeTag::SetIcon, @@ -379,7 +396,7 @@ TabbedSelector::TabbedSelector( tabs.reserve(2); tabs.push_back(createTab(SelectorTab::Stickers, 0)); tabs.push_back(createTab(SelectorTab::Masks, 1)); - } else if (_mode == Mode::StickersOnly) { + } else if (_mode == Mode::StickersOnly || _mode == Mode::ChatIntro) { tabs.reserve(1); tabs.push_back(createTab(SelectorTab::Stickers, 0)); } else { @@ -390,7 +407,9 @@ TabbedSelector::TabbedSelector( }()) , _currentTabType(full() ? session().settings().selectorTab() - : (mediaEditor() || _mode == Mode::StickersOnly) + : (mediaEditor() + || _mode == Mode::StickersOnly + || _mode == Mode::ChatIntro) ? SelectorTab::Stickers : SelectorTab::Emoji) , _hasEmojiTab(ranges::contains(_tabs, SelectorTab::Emoji, &Tab::type)) @@ -555,7 +574,9 @@ TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) { using Descriptor = StickersListDescriptor; return object_ptr(this, Descriptor{ .show = _show, - .mode = StickersMode::Full, + .mode = (_mode == Mode::ChatIntro + ? StickersMode::ChatIntro + : StickersMode::Full), .paused = paused, .st = &_st, .features = _features, diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index f663d470a4d64b..b9b03b33b33133 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -87,6 +87,7 @@ enum class TabbedSelectorMode { FullReactions, RecentReactions, PeerTitle, + ChatIntro, }; struct TabbedSelectorDescriptor { @@ -103,6 +104,7 @@ enum class TabbedSearchType { Status, ProfilePhoto, Stickers, + Greeting, }; [[nodiscard]] std::unique_ptr MakeSearch( not_null parent, diff --git a/Telegram/SourceFiles/settings/business/settings_chat_intro.cpp b/Telegram/SourceFiles/settings/business/settings_chat_intro.cpp index 7bd61ca9032fc3..b8a7607f62d420 100644 --- a/Telegram/SourceFiles/settings/business/settings_chat_intro.cpp +++ b/Telegram/SourceFiles/settings/business/settings_chat_intro.cpp @@ -476,7 +476,7 @@ void StickerPanel::create(const Descriptor &descriptor) { .show = controller->uiShow(), .st = st::backgroundEmojiPan, .level = Window::GifPauseReason::Layer, - .mode = Mode::StickersOnly, + .mode = Mode::ChatIntro, .features = { .megagroupSet = false, .stickersSettings = false, From bc649af941e1d2824eff5c95ceac87a84c566737 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 28 Apr 2024 22:42:48 +0400 Subject: [PATCH 08/40] Schedule no-response action runs at different times To avoid rate limits --- .../{no-response.yml => cant-reproduce.yml} | 25 ++----------------- .github/workflows/needs-user-action.yml | 16 ++++++++++++ .github/workflows/waiting-for-answer.yml | 16 ++++++++++++ 3 files changed, 34 insertions(+), 23 deletions(-) rename .github/workflows/{no-response.yml => cant-reproduce.yml} (57%) create mode 100644 .github/workflows/needs-user-action.yml create mode 100644 .github/workflows/waiting-for-answer.yml diff --git a/.github/workflows/no-response.yml b/.github/workflows/cant-reproduce.yml similarity index 57% rename from .github/workflows/no-response.yml rename to .github/workflows/cant-reproduce.yml index f414a55fd6c584..5c4edcba0fb0c5 100644 --- a/.github/workflows/no-response.yml +++ b/.github/workflows/cant-reproduce.yml @@ -1,32 +1,11 @@ -name: No Response +name: Can't reproduce. -# Both `issue_comment` and `scheduled` event types are required for this Action -# to work properly. on: - issue_comment: - types: [created] schedule: - - cron: '0 0 * * *' + - cron: '0 3 * * *' jobs: - waiting-for-answer: - runs-on: ubuntu-latest - steps: - - uses: lee-dohm/no-response@v0.5.0 - with: - token: ${{ github.token }} - responseRequiredLabel: waiting for answer - - needs-user-action: - runs-on: ubuntu-latest - steps: - - uses: lee-dohm/no-response@v0.5.0 - with: - token: ${{ github.token }} - responseRequiredLabel: needs user action - cant-reproduce: - if: github.event_name != 'issue_comment' runs-on: ubuntu-latest steps: - uses: lee-dohm/no-response@v0.5.0 diff --git a/.github/workflows/needs-user-action.yml b/.github/workflows/needs-user-action.yml new file mode 100644 index 00000000000000..46ad9f87d82d16 --- /dev/null +++ b/.github/workflows/needs-user-action.yml @@ -0,0 +1,16 @@ +name: Needs user action. + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 2 * * *' + +jobs: + needs-user-action: + runs-on: ubuntu-latest + steps: + - uses: lee-dohm/no-response@v0.5.0 + with: + token: ${{ github.token }} + responseRequiredLabel: needs user action diff --git a/.github/workflows/waiting-for-answer.yml b/.github/workflows/waiting-for-answer.yml new file mode 100644 index 00000000000000..657c45e92f7573 --- /dev/null +++ b/.github/workflows/waiting-for-answer.yml @@ -0,0 +1,16 @@ +name: Waiting for answer. + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 0 * * *' + +jobs: + waiting-for-answer: + runs-on: ubuntu-latest + steps: + - uses: lee-dohm/no-response@v0.5.0 + with: + token: ${{ github.token }} + responseRequiredLabel: waiting for answer From 6fa0afff377a9360479ea469b4658870296c12fb Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 1 May 2024 00:24:16 +0000 Subject: [PATCH 09/40] Update User-Agent for DNS to Chrome 124.0.0.0. --- .../SourceFiles/mtproto/details/mtproto_domain_resolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp index 1491174e33b740..93865a58746f26 100644 --- a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp +++ b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp @@ -65,7 +65,7 @@ QByteArray DnsUserAgent() { static const auto kResult = QByteArray( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/123.0.0.0 Safari/537.36"); + "Chrome/124.0.0.0 Safari/537.36"); return kResult; } From 9166a1c3a6e48ac09691eadb30475285ff6d3fb6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 1 May 2024 12:56:57 +0400 Subject: [PATCH 10/40] Update Linux Qt patches. Hope fixes crashes. --- Telegram/build/docker/centos_env/Dockerfile | 2 +- snap/snapcraft.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 2333305d7301f9..f6e4335350c86f 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -51,7 +51,7 @@ FROM builder AS patches RUN git init patches \ && cd patches \ && git remote add origin {{ GIT }}/desktop-app/patches.git \ - && git fetch --depth=1 origin f81dfc6f7061d7f70c7e952cee9c1c2689f937a2 \ + && git fetch --depth=1 origin ce5098d9bd638ec264fd8cf69ef3ef31d8139d18 \ && git reset --hard FETCH_HEAD \ && rm -rf .git diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index ee538420ac78df..41559536c85192 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -171,7 +171,7 @@ parts: patches: source: https://github.com/desktop-app/patches.git source-depth: 1 - source-commit: f81dfc6f7061d7f70c7e952cee9c1c2689f937a2 + source-commit: ce5098d9bd638ec264fd8cf69ef3ef31d8139d18 plugin: dump override-pull: | craftctl default From 32483fa13b46ba593fc7a302973878615428c4d1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 1 May 2024 12:57:47 +0400 Subject: [PATCH 11/40] Just focus the search field on Space. --- Telegram/SourceFiles/dialogs/dialogs_widget.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 13b83753c315d6..30848c491d232c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -3270,7 +3270,9 @@ void Widget::keyPressEvent(QKeyEvent *e) { // query while still show _suggestions animated, if it is a space. _postponeProcessSearchFocusChange = true; _search->setFocusFast(); - QCoreApplication::sendEvent(_search->rawTextEdit(), e); + if (e->key() != Qt::Key_Space) { + QCoreApplication::sendEvent(_search->rawTextEdit(), e); + } _postponeProcessSearchFocusChange = false; processSearchFocusChange(); } else { From 3170a45158e239dc5a589c93a43a1d84c169f5b4 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 1 May 2024 14:03:30 +0400 Subject: [PATCH 12/40] Increase snapcraft verbosity --- .github/workflows/snap.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml index 0c2bc4148cd491..badcf6cc9fed8a 100644 --- a/.github/workflows/snap.yml +++ b/.github/workflows/snap.yml @@ -64,7 +64,7 @@ jobs: uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8 - name: Telegram Desktop snap build. - run: sg lxd -c 'snap run snapcraft -v' + run: sg lxd -c 'snap run snapcraft --verbosity=debug' - name: Move artifact. if: env.UPLOAD_ARTIFACT == 'true' From b3eb1dbc14f589897d2d1928f27dde92afa1ff78 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 1 May 2024 13:31:58 +0400 Subject: [PATCH 13/40] Revert "Toggle search focus by escape." This reverts commit 72d5a9b3e00e8f76fabbfb3e130efe371d2d5aaa. --- Telegram/SourceFiles/dialogs/dialogs_widget.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 30848c491d232c..b8e3ccad6934ff 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -1795,8 +1795,6 @@ void Widget::escape() { const auto first = list.empty() ? FilterId() : list.front().id(); if (controller()->activeChatsFilterCurrent() != first) { controller()->setActiveChatsFilter(first); - } else { - _search->setFocus(); } } } else if (!_searchInChat From a62d1dfa6361bb371d40308960c52b02a6a2c2dd Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 1 May 2024 14:36:24 +0400 Subject: [PATCH 14/40] Focus chats search by Ctrl[Cmd]+F. --- Telegram/SourceFiles/dialogs/dialogs_widget.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index b8e3ccad6934ff..6d8a226b472b24 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -995,11 +995,17 @@ void Widget::setupShortcuts() { }) | rpl::start_with_next([=](not_null request) { using Command = Shortcuts::Command; - if (_openedForum && !controller()->activeChatCurrent()) { + if (!controller()->activeChatCurrent()) { request->check(Command::Search) && request->handle([=] { - const auto history = _openedForum->history(); - controller()->searchInChat(history); - return true; + if (const auto forum = _openedForum) { + const auto history = forum->history(); + controller()->searchInChat(history); + return true; + } else if (!_openedFolder && _search->isVisible()) { + _search->setFocus(); + return true; + } + return false; }); } }, lifetime()); From 7e071c770f7691ffdbbbd38ac3e17c9aae4d21b3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 1 May 2024 14:37:52 +0400 Subject: [PATCH 15/40] Fix possible crash with custom wallpapered peers. --- Telegram/SourceFiles/window/section_widget.cpp | 6 ++++-- Telegram/SourceFiles/window/window_session_controller.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/window/section_widget.cpp b/Telegram/SourceFiles/window/section_widget.cpp index 18f3f7d0596009..ae24ad4d3e3a2c 100644 --- a/Telegram/SourceFiles/window/section_widget.cpp +++ b/Telegram/SourceFiles/window/section_widget.cpp @@ -201,10 +201,12 @@ rpl::producer WallPaperResolved( return result; } themes->refreshChatThemes(); - return themes->chatThemesUpdated( + return rpl::single( + nullptr + ) | rpl::then(themes->chatThemesUpdated( ) | rpl::take(1) | rpl::map([=] { return fromThemes(true); - }) | rpl::flatten_latest(); + }) | rpl::flatten_latest()); } AbstractSectionWidget::AbstractSectionWidget( diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 241567b5ed28b7..cf4033ce45d2c9 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -700,7 +700,7 @@ class SessionController : public SessionNavigation { rpl::event_stream<> _filtersMenuChanged; - std::shared_ptr _defaultChatTheme; + const std::shared_ptr _defaultChatTheme; base::flat_map _customChatThemes; rpl::event_stream> _cachedThemesStream; const std::unique_ptr _chatStyle; From ad6321d3ae57b9b4ccb41c781b037458aa9db3da Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 1 May 2024 14:09:56 +0300 Subject: [PATCH 16/40] Fixed possible crash in moderate box. --- .../boxes/moderate_messages_box.cpp | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp index 4496ed421a07aa..dbb9e7cb18dba7 100644 --- a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp @@ -227,12 +227,12 @@ void CreateModerateMessagesBox( ? QMargins() : QMargins(0, 0, Button::ComputeSize(users.size()).width(), 0); + const auto session = &items.front()->history()->session(); + const auto historyPeerId = items.front()->history()->peer->id; + using Request = Fn, not_null)>; const auto sequentiallyRequest = [=](Request request, Users users) { constexpr auto kSmallDelayMs = 5; - const auto session = &items.front()->history()->session(); - const auto history = items.front()->history(); - const auto peerId = history->peer->id; const auto userIds = ranges::views::all( users ) | ranges::views::transform([](not_null user) { @@ -243,7 +243,7 @@ void CreateModerateMessagesBox( const auto timer = lifetime->make_state(); timer->setCallback(crl::guard(session, [=] { if ((*counter) < userIds.size()) { - const auto peer = session->data().peer(peerId); + const auto peer = session->data().peer(historyPeerId); const auto channel = peer ? peer->asChannel() : nullptr; const auto from = session->data().peer(userIds[*counter]); if (const auto user = from->asUser(); channel && user) { @@ -697,17 +697,19 @@ void CreateModerateMessagesBox( } const auto close = crl::guard(box, [=] { box->closeBox(); }); - box->addButton(tr::lng_box_delete(), [=] { - confirms->fire({}); + { const auto data = &users.front()->session().data(); - const auto ids = data->itemsToIds(items); - if (confirmed) { - confirmed(); - } - data->histories().deleteMessages(ids, true); - data->sendHistoryChangeNotifications(); - close(); - }); + const auto ids = users.front()->session().data().itemsToIds(items); + box->addButton(tr::lng_box_delete(), [=] { + confirms->fire({}); + if (confirmed) { + confirmed(); + } + data->histories().deleteMessages(ids, true); + data->sendHistoryChangeNotifications(); + close(); + }); + } box->addButton(tr::lng_cancel(), close); } From 12a24dd473fffc48263bd786ace5d6f7e06a8968 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 1 May 2024 15:02:35 +0400 Subject: [PATCH 17/40] Fix possible crash in common groups list. Currently Data::Session::processChat() may change adminRights(), that may change Data::CanSendAnyOf(peer, ...), that may lead to Window::SessionController::updateThirdColumnToCurrentChat() call, that destroys current third column to replace it with another one. If common groups list was opened in the third column this will crash. Fixes #27640. --- .../info_common_groups_inner_widget.cpp | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp b/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp index 720f47eba8c36b..37defed0d9a4d2 100644 --- a/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp +++ b/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp @@ -7,6 +7,7 @@ For license and copyright information please follow this link: */ #include "info/common_groups/info_common_groups_inner_widget.h" +#include "base/weak_ptr.h" #include "info/common_groups/info_common_groups_widget.h" #include "info/info_controller.h" #include "lang/lang_keys.h" @@ -27,7 +28,9 @@ namespace { constexpr auto kCommonGroupsPerPage = 40; constexpr auto kCommonGroupsSearchAfter = 20; -class ListController final : public PeerListController { +class ListController final + : public PeerListController + , public base::has_weak_ptr { public: ListController( not_null controller, @@ -108,16 +111,30 @@ void ListController::loadMoreRows() { return data.vchats().v; }); if (!chats.empty()) { + auto add = std::vector>(); + auto allLoaded = _allLoaded; + auto preloadGroupId = _preloadGroupId; + const auto owner = &_user->owner(); + const auto weak = base::make_weak(this); for (const auto &chat : chats) { - if (const auto peer = _user->owner().processChat(chat)) { + if (const auto peer = owner->processChat(chat)) { if (!peer->migrateTo()) { - delegate()->peerListAppendRow( - createRow(peer)); + add.push_back(peer); } - _preloadGroupId = peer->id; - _allLoaded = false; + preloadGroupId = peer->id; + allLoaded = false; } } + if (!weak) { + return; + } + for (const auto &peer : add) { + if (!delegate()->peerListFindRow(peer->id.value)) { + delegate()->peerListAppendRow(createRow(peer)); + } + } + _preloadGroupId = preloadGroupId; + _allLoaded = allLoaded; delegate()->peerListRefreshRows(); } auto fullCount = delegate()->peerListFullRowsCount(); From 47800ee02d7b4fa431b19002ad8d46c10de44c32 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 1 May 2024 19:09:54 +0400 Subject: [PATCH 18/40] Improve font size selection a bit. --- Telegram/SourceFiles/ui/boxes/choose_font_box.cpp | 1 + Telegram/lib_ui | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/ui/boxes/choose_font_box.cpp b/Telegram/SourceFiles/ui/boxes/choose_font_box.cpp index 9ef7e24e0d5b2e..a5e8fb20325e87 100644 --- a/Telegram/SourceFiles/ui/boxes/choose_font_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/choose_font_box.cpp @@ -314,6 +314,7 @@ void Selector::validateCache(Entry &row) { } else if (row.paletteVersion == version) { return; } + row.paletteVersion = version; row.cache.fill(Qt::transparent); auto font = style::ResolveFont(row.id, 0, st::boxFontSize); auto p = QPainter(&row.cache); diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 08b56a17892d4c..4ddff63a9b6cd2 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 08b56a17892d4c9c6b7ef87fcffbb64090e9ce2a +Subproject commit 4ddff63a9b6cd2e2787995611f1ee4b145bdb504 From 26e8c29f40de6da59ade55908a9882908fd3095c Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 2 May 2024 01:44:52 +0300 Subject: [PATCH 19/40] Moved formatting of dialog last date to td_ui. --- .../SourceFiles/dialogs/ui/dialogs_layout.cpp | 32 +++---------------- .../SourceFiles/ui/text/format_values.cpp | 18 +++++++++++ Telegram/SourceFiles/ui/text/format_values.h | 1 + 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp index a625d8aca2bc0e..f7cbff178fe1dc 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp @@ -18,6 +18,7 @@ For license and copyright information please follow this link: #include "styles/style_window.h" #include "storage/localstorage.h" #include "ui/empty_userpic.h" +#include "ui/text/format_values.h" #include "ui/text/text_options.h" #include "ui/text/text_utilities.h" #include "ui/unread_badge.h" @@ -44,8 +45,6 @@ For license and copyright information please follow this link: namespace Dialogs::Ui { namespace { -// Show all dates that are in the last 20 hours in time format. -constexpr int kRecentlyInSeconds = 20 * 3600; const auto kPsaBadgePrefix = "cloud_lng_badge_psa_"; [[nodiscard]] bool ShowUserBotIcon(not_null user) { @@ -81,29 +80,6 @@ void PaintRowTopRight( text); } -void PaintRowDate( - QPainter &p, - QDateTime date, - QRect &rectForName, - const PaintContext &context) { - const auto now = QDateTime::currentDateTime(); - const auto &lastTime = date; - const auto nowDate = now.date(); - const auto lastDate = lastTime.date(); - - const auto dt = [&] { - if ((lastDate == nowDate) - || (qAbs(lastTime.secsTo(now)) < kRecentlyInSeconds)) { - return QLocale().toString(lastTime.time(), QLocale::ShortFormat); - } else if (qAbs(lastDate.daysTo(nowDate)) < 7) { - return langDayOfWeek(lastDate); - } else { - return QLocale().toString(lastDate, QLocale::ShortFormat); - } - }(); - PaintRowTopRight(p, dt, rectForName, context); -} - int PaintBadges( QPainter &p, const PaintContext &context, @@ -444,7 +420,8 @@ void PaintRow( || (supportMode && entry->session().supportHelper().isOccupiedBySomeone(history))) { if (!promoted) { - PaintRowDate(p, date, rectForName, context); + const auto dateString = Ui::FormatDialogsDate(date); + PaintRowTopRight(p, dateString, rectForName, context); } auto availableWidth = namewidth; @@ -566,7 +543,8 @@ void PaintRow( } } else if (!item->isEmpty()) { if ((thread || sublist) && !promoted) { - PaintRowDate(p, date, rectForName, context); + const auto dateString = Ui::FormatDialogsDate(date); + PaintRowTopRight(p, dateString, rectForName, context); } paintItemCallback(nameleft, namewidth); diff --git a/Telegram/SourceFiles/ui/text/format_values.cpp b/Telegram/SourceFiles/ui/text/format_values.cpp index 41e985723a17b0..a5115acf2cb058 100644 --- a/Telegram/SourceFiles/ui/text/format_values.cpp +++ b/Telegram/SourceFiles/ui/text/format_values.cpp @@ -484,4 +484,22 @@ QString FormatResetCloudPasswordIn(float64 sec) { return (sec >= 3600) ? FormatTTL(sec) : FormatDurationText(sec); } +QString FormatDialogsDate(const QDateTime &lastTime) { + // Show all dates that are in the last 20 hours in time format. + constexpr int kRecentlyInSeconds = 20 * 3600; + + const auto now = QDateTime::currentDateTime(); + const auto nowDate = now.date(); + const auto lastDate = lastTime.date(); + + if ((lastDate == nowDate) + || (std::abs(lastTime.secsTo(now)) < kRecentlyInSeconds)) { + return QLocale().toString(lastTime.time(), QLocale::ShortFormat); + } else if (std::abs(lastDate.daysTo(nowDate)) < 7) { + return langDayOfWeek(lastDate); + } else { + return QLocale().toString(lastDate, QLocale::ShortFormat); + } +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/text/format_values.h b/Telegram/SourceFiles/ui/text/format_values.h index d4ac31b9ead794..b73775118af274 100644 --- a/Telegram/SourceFiles/ui/text/format_values.h +++ b/Telegram/SourceFiles/ui/text/format_values.h @@ -31,6 +31,7 @@ inline constexpr auto FileStatusSizeFailed = 0xFFFFFFF2LL; [[nodiscard]] QString FormatMuteFor(float64 sec); [[nodiscard]] QString FormatMuteForTiny(float64 sec); [[nodiscard]] QString FormatResetCloudPasswordIn(float64 sec); +[[nodiscard]] QString FormatDialogsDate(const QDateTime &lastTime); struct CurrencyRule { const char *international = ""; From 6becaaa95364d25f88f4172556f3d13c31bc08c9 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 2 May 2024 01:45:02 +0300 Subject: [PATCH 20/40] Slightly improved view style of giveaway results in dialogs list. --- Telegram/SourceFiles/data/data_media_types.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 50e063da570a6d..3a8dc19434af76 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -2367,9 +2367,7 @@ const GiveawayResults *MediaGiveawayResults::giveawayResults() const { } TextWithEntities MediaGiveawayResults::notificationText() const { - return { - .text = tr::lng_prizes_results_title(tr::now), - }; + return Ui::Text::Colorized({ tr::lng_prizes_results_title(tr::now) }); } QString MediaGiveawayResults::pinnedTextSubstring() const { From ab85d18cc801ae572980d5825e0b6fd1ed188892 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 2 May 2024 02:38:52 +0300 Subject: [PATCH 21/40] Added support of personal channel with message. --- Telegram/SourceFiles/info/info.style | 19 + .../info/profile/info_profile_actions.cpp | 361 +++++++++++++----- 2 files changed, 295 insertions(+), 85 deletions(-) diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 04bb9b1b60ec56..329a79158ff1de 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -10,6 +10,7 @@ using "ui/basic.style"; using "boxes/boxes.style"; using "ui/widgets/widgets.style"; using "ui/chat/chat.style"; // GroupCallUserpics. +using "dialogs/dialogs.style"; // dialogsDateFont. InfoToggle { color: color; @@ -469,6 +470,24 @@ infoGroupMembersIconPosition: point(20px, 10px); infoChannelMembersIconPosition: point(20px, 19px); infoPersonalChannelIconPosition: point(25px, 20px); +infoPersonalChannelNameLabel: FlatLabel(infoProfileStatus) { + textFg: windowBoldFg; + style: semiboldTextStyle; + maxHeight: 20px; +} +infoPersonalChannelDateSkip: 22px; +infoPersonalChannelDateLabel: FlatLabel(infoProfileStatus) { + textFg: dialogsDateFg; + style: TextStyle(semiboldTextStyle) { + font: dialogsDateFont; + } + maxHeight: 20px; +} +infoPersonalChannelUserpicSkip: 3px; +infoPersonalChannelUserpic: UserpicButton(defaultUserpicButton) { + size: size(42px, 42px); + photoSize: 42px; +} infoBlockHeaderLabel: FlatLabel(infoProfileStatus) { textFg: windowBoldFg; diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index b7a14b484b2372..096e4f80253efe 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -7,66 +7,70 @@ For license and copyright information please follow this link: */ #include "info/profile/info_profile_actions.h" +#include "api/api_blocked_peers.h" #include "api/api_chat_participants.h" +#include "apiwrap.h" #include "base/options.h" #include "base/timer_rpl.h" #include "base/unixtime.h" +#include "boxes/peers/add_bot_to_chat_box.h" +#include "boxes/peers/edit_contact_box.h" +#include "boxes/report_messages_box.h" +#include "boxes/share_box.h" +#include "boxes/translate_box.h" +#include "core/application.h" +#include "core/click_handler_types.h" #include "data/business/data_business_common.h" #include "data/business/data_business_info.h" -#include "data/data_peer_values.h" -#include "data/data_session.h" -#include "data/data_folder.h" -#include "data/data_forum_topic.h" -#include "data/data_channel.h" #include "data/data_changes.h" +#include "data/data_channel.h" #include "data/data_chat.h" +#include "data/data_folder.h" +#include "data/data_forum_topic.h" +#include "data/data_peer_values.h" +#include "data/data_session.h" #include "data/data_user.h" #include "data/notify/data_notify_settings.h" -#include "ui/vertical_list.h" -#include "ui/wrap/vertical_layout.h" -#include "ui/wrap/padding_wrap.h" -#include "ui/wrap/slide_wrap.h" -#include "ui/widgets/checkbox.h" -#include "ui/widgets/shadow.h" -#include "ui/widgets/labels.h" -#include "ui/widgets/buttons.h" -#include "ui/widgets/popup_menu.h" -#include "ui/boxes/report_box.h" -#include "ui/layers/generic_box.h" -#include "ui/toast/toast.h" -#include "ui/text/text_utilities.h" // Ui::Text::ToUpper -#include "ui/text/text_variant.h" -#include "history/history_location_manager.h" // LocationClickHandler. -#include "history/view/history_view_context_menu.h" // HistoryView::ShowReportPeerBox -#include "boxes/peers/add_bot_to_chat_box.h" -#include "boxes/peers/edit_contact_box.h" -#include "boxes/report_messages_box.h" -#include "boxes/share_box.h" -#include "boxes/translate_box.h" -#include "lang/lang_keys.h" -#include "menu/menu_mute.h" +#include "dialogs/ui/dialogs_layout.h" +#include "dialogs/ui/dialogs_message_view.h" #include "history/history.h" +#include "history/history_item.h" +#include "history/history_item_helpers.h" +#include "history/view/history_view_context_menu.h" // HistoryView::ShowReportPeerBox +#include "history/view/history_view_item_preview.h" #include "info/info_controller.h" #include "info/info_memento.h" #include "info/profile/info_profile_icon.h" #include "info/profile/info_profile_phone_menu.h" -#include "info/profile/info_profile_values.h" #include "info/profile/info_profile_text.h" +#include "info/profile/info_profile_values.h" #include "info/profile/info_profile_widget.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" +#include "menu/menu_mute.h" #include "support/support_helper.h" -#include "window/window_session_controller.h" +#include "ui/boxes/report_box.h" +#include "ui/controls/userpic_button.h" +#include "ui/painter.h" +#include "ui/rect.h" +#include "ui/text/format_values.h" +#include "ui/text/text_utilities.h" // Ui::Text::ToUpper +#include "ui/text/text_variant.h" +#include "ui/toast/toast.h" +#include "ui/vertical_list.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/popup_menu.h" +#include "ui/widgets/shadow.h" +#include "ui/wrap/padding_wrap.h" +#include "ui/wrap/slide_wrap.h" +#include "ui/wrap/vertical_layout.h" #include "window/window_controller.h" // Window::Controller::show. #include "window/window_peer_menu.h" -#include "mainwidget.h" -#include "mainwindow.h" // MainWindow::controller. -#include "main/main_session.h" -#include "core/application.h" -#include "core/click_handler_types.h" -#include "apiwrap.h" -#include "api/api_blocked_peers.h" +#include "window/window_session_controller.h" #include "styles/style_info.h" #include "styles/style_layers.h" -#include "styles/style_boxes.h" #include "styles/style_menu_icons.h" #include @@ -1228,63 +1232,250 @@ object_ptr DetailsFiller::setupPersonalChannel( _wrap, object_ptr(_wrap)); const auto container = result->entity(); + const auto window = _controller->parentController(); result->toggleOn(PersonalChannelValue( user ) | rpl::map(rpl::mappers::_1 != nullptr)); result->finishAnimating(); - Ui::AddDivider(container); - + auto channelToggleValue = PersonalChannelValue( + user + ) | rpl::map([=] { return !!user->personalChannelId(); }); auto channel = PersonalChannelValue( user ) | rpl::start_spawning(result->lifetime()); - auto text = rpl::duplicate( - channel - ) | rpl::map([=](ChannelData *channel) { - return channel ? NameValue(channel) : rpl::single(QString()); - }) | rpl::flatten_latest() | rpl::map([](const QString &name) { - return name.isEmpty() ? TextWithEntities() : Ui::Text::Link(name); - }); - auto label = rpl::combine( - tr::lng_info_personal_channel_label(Ui::Text::WithEntities), - rpl::duplicate(channel) - ) | rpl::map([](TextWithEntities &&text, ChannelData *channel) { - const auto count = channel ? channel->membersCount() : 0; - if (count > 1) { - text.append( - QString::fromUtf8(" \xE2\x80\xA2 ") - ).append(tr::lng_chat_status_subscribers( - tr::now, - lt_count_decimal, - count)); - } - return text; - }); - auto line = CreateTextWithLabel( - result, - std::move(label), - std::move(text), - st::infoLabel, - st::infoLabeled, - st::infoProfileLabeledPadding); - container->add(std::move(line.wrap)); - - line.text->setClickHandlerFilter([ - =, - window = _controller->parentController()]( - const ClickHandlerPtr &handler, - Qt::MouseButton button) { - if (const auto channelId = user->personalChannelId()) { - window->showPeerInfo(peerFromChannel(channelId)); - } - return false; - }); - object_ptr( - result, - st::infoIconMediaChannel, - st::infoPersonalChannelIconPosition); + const auto channelLabelFactory = [=](rpl::producer c) { + return rpl::combine( + tr::lng_info_personal_channel_label(Ui::Text::WithEntities), + std::move(c) + ) | rpl::map([](TextWithEntities &&text, ChannelData *channel) { + const auto count = channel ? channel->membersCount() : 0; + if (count > 1) { + text.append( + QString::fromUtf8(" \xE2\x80\xA2 ") + ).append(tr::lng_chat_status_subscribers( + tr::now, + lt_count_decimal, + count)); + } + return text; + }); + }; + + { + const auto onlyChannelWrap = container->add( + object_ptr>( + container, + object_ptr(container))); + onlyChannelWrap->toggleOn(rpl::duplicate(channelToggleValue) + | rpl::map(!rpl::mappers::_1)); + onlyChannelWrap->finishAnimating(); + + Ui::AddDivider(onlyChannelWrap->entity()); + + auto text = rpl::duplicate( + channel + ) | rpl::map([=](ChannelData *channel) { + return channel ? NameValue(channel) : rpl::single(QString()); + }) | rpl::flatten_latest() | rpl::map([](const QString &name) { + return name.isEmpty() ? TextWithEntities() : Ui::Text::Link(name); + }); + auto line = CreateTextWithLabel( + result, + channelLabelFactory(rpl::duplicate(channel)), + std::move(text), + st::infoLabel, + st::infoLabeled, + st::infoProfileLabeledPadding); + onlyChannelWrap->entity()->add(std::move(line.wrap)); + + line.text->setClickHandlerFilter([=]( + const ClickHandlerPtr &handler, + Qt::MouseButton button) { + if (const auto channelId = user->personalChannelId()) { + window->showPeerInfo(peerFromChannel(channelId)); + } + return false; + }); + + object_ptr( + onlyChannelWrap, + st::infoIconMediaChannel, + st::infoPersonalChannelIconPosition); + } + + { + const auto messageChannelWrap = container->add( + object_ptr>( + container, + object_ptr(container))); + messageChannelWrap->toggleOn(rpl::duplicate(channelToggleValue)); + messageChannelWrap->finishAnimating(); + + const auto clear = [=] { + while (messageChannelWrap->entity()->count()) { + delete messageChannelWrap->entity()->widgetAt(0); + } + }; + + const auto rebuild = [=]( + not_null item, + anim::type animated) { + const auto &stUserpic = st::infoPersonalChannelUserpic; + const auto &stLabeled = st::infoProfileLabeledPadding; + + messageChannelWrap->toggle(false, anim::type::instant); + clear(); + Ui::AddDivider(messageChannelWrap->entity()); + Ui::AddSkip(messageChannelWrap->entity()); + + const auto inner = messageChannelWrap->entity()->add( + object_ptr(messageChannelWrap->entity())); + + const auto line = inner->add( + object_ptr( + inner, + stUserpic.photoSize + rect::m::sum::v(stLabeled))); + const auto userpic = Ui::CreateChild( + line, + item->history()->peer, + st::infoPersonalChannelUserpic); + + userpic->moveToLeft( + -st::infoPersonalChannelUserpicSkip + + (stLabeled.left() - stUserpic.photoSize) / 2, + stLabeled.top()); + userpic->setAttribute(Qt::WA_TransparentForMouseEvents); + + const auto date = Ui::CreateChild( + line, + Ui::FormatDialogsDate(ItemDateTime(item)), + st::infoPersonalChannelDateLabel); + + const auto name = Ui::CreateChild( + line, + NameValue(item->history()->peer), + st::infoPersonalChannelNameLabel); + + const auto preview = Ui::CreateChild(line); + auto &lifetime = preview->lifetime(); + using namespace Dialogs::Ui; + const auto previewView = lifetime.make_state(); + preview->resize(0, st::infoLabeled.style.font->height); + preview->paintRequest( + ) | rpl::start_with_next([=](const QRect &rect) { + auto p = Painter(preview); + if (previewView->prepared(item, nullptr)) { + previewView->paint(p, preview->rect(), { + .st = &st::defaultDialogRow, + .currentBg = st::boxBg->b, + }); + } else if (!previewView->dependsOn(item)) { + p.setPen(st::infoPersonalChannelDateLabel.textFg); + p.setBrush(Qt::NoBrush); + p.setFont(st::infoPersonalChannelDateLabel.style.font); + p.drawText( + preview->rect(), + tr::lng_contacts_loading(tr::now), + style::al_left); + previewView->prepare( + item, + nullptr, + [=] { preview->update(); }, + {}); + preview->update(); + } + }, preview->lifetime()); + + line->sizeValue( + ) | rpl::start_with_next([=](const QSize &size) { + const auto left = stLabeled.left(); + const auto right = st::infoPersonalChannelDateSkip; + const auto top = stLabeled.top(); + date->moveToRight(right, top); + + name->resizeToWidth(size.width() + - left + - date->width() + - st::defaultVerticalListSkip + - right); + name->moveToLeft(left, top); + + preview->resize( + size.width() - left - right, + st::infoLabeled.style.font->height); + preview->moveToLeft( + left, + size.height() - stLabeled.bottom() - preview->height()); + }, preview->lifetime()); + + { + inner->add( + object_ptr( + inner, + channelLabelFactory( + rpl::single(item->history()->peer->asChannel())), + st::infoLabel), + QMargins( + st::infoProfileLabeledPadding.left(), + 0, + st::infoProfileLabeledPadding.right(), + st::infoProfileLabeledPadding.bottom())); + } + { + const auto button = Ui::CreateChild( + messageChannelWrap->entity(), + st::defaultRippleAnimation); + button->paintRequest( + ) | rpl::start_with_next([=](const QRect &rect) { + auto p = QPainter(button); + button->paintRipple(p, 0, 0); + }, button->lifetime()); + inner->geometryValue( + ) | rpl::start_with_next([=](const QRect &rect) { + button->setGeometry(rect); + }, button->lifetime()); + button->setClickedCallback([=, msg = item->fullId().msg] { + window->showPeerHistory( + item->history()->peer, + Window::SectionShow::Way::Forward, + msg); + }); + button->lower(); + } + inner->setAttribute(Qt::WA_TransparentForMouseEvents); + Ui::AddSkip(messageChannelWrap->entity()); + + Ui::ToggleChildrenVisibility(messageChannelWrap->entity(), true); + Ui::ToggleChildrenVisibility(line, true); + messageChannelWrap->toggle(true, animated); + }; + + rpl::duplicate( + channel + ) | rpl::start_with_next([=](ChannelData *channel) { + clear(); + if (!channel) { + return; + } + const auto id = FullMsgId( + channel->id, + user->personalChannelMessageId()); + if (const auto item = user->session().data().message(id)) { + return rebuild(item, anim::type::instant); + } + user->session().api().requestMessageData( + channel, + user->personalChannelMessageId(), + [=] { + if (const auto i = user->session().data().message(id)) { + rebuild(i, anim::type::normal); + } + }); + }, messageChannelWrap->lifetime()); + } return result; } From d40951f068c80d78d915e3dba6a76e5e0950bac5 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 2 May 2024 03:37:26 +0300 Subject: [PATCH 22/40] Added ability to moderate non-users to moderation box. --- .../boxes/moderate_messages_box.cpp | 103 +++++++++--------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp index dbb9e7cb18dba7..974f3bbf80824d 100644 --- a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp @@ -46,12 +46,12 @@ For license and copyright information please follow this link: namespace { -using Users = std::vector>; +using Participants = std::vector>; struct ModerateOptions final { bool allCanBan = false; bool allCanDelete = false; - Users users; + Participants participants; }; ModerateOptions CalculateModerateOptions(const HistoryItemsList &items) { @@ -76,9 +76,9 @@ ModerateOptions CalculateModerateOptions(const HistoryItemsList &items) { if (!item->suggestDeleteAllReport()) { result.allCanDelete = false; } - if (const auto user = item->from()->asUser()) { - if (!ranges::contains(result.users, not_null{ user })) { - result.users.push_back(user); + if (const auto p = item->from()) { + if (!ranges::contains(result.participants, not_null{ p })) { + result.participants.push_back(p); } } } @@ -212,42 +212,45 @@ void CreateModerateMessagesBox( rpl::event_stream toggleRequestsFromTop; rpl::event_stream toggleRequestsFromInner; rpl::event_stream checkAllRequests; - Fn collectRequests; + Fn collectRequests; }; - const auto [allCanBan, allCanDelete, users] = CalculateModerateOptions( - items); + const auto [allCanBan, allCanDelete, participants] + = CalculateModerateOptions(items); const auto inner = box->verticalLayout(); - Assert(!users.empty()); + Assert(!participants.empty()); const auto confirms = inner->lifetime().make_state>(); - const auto isSingle = users.size() == 1; + const auto isSingle = participants.size() == 1; const auto buttonPadding = isSingle ? QMargins() - : QMargins(0, 0, Button::ComputeSize(users.size()).width(), 0); + : QMargins(0, 0, Button::ComputeSize(participants.size()).width(), 0); const auto session = &items.front()->history()->session(); const auto historyPeerId = items.front()->history()->peer->id; - using Request = Fn, not_null)>; - const auto sequentiallyRequest = [=](Request request, Users users) { + using Request = Fn, not_null)>; + const auto sequentiallyRequest = [=]( + Request request, + Participants participants) { constexpr auto kSmallDelayMs = 5; - const auto userIds = ranges::views::all( - users - ) | ranges::views::transform([](not_null user) { - return user->id; + const auto participantIds = ranges::views::all( + participants + ) | ranges::views::transform([](not_null peer) { + return peer->id; }) | ranges::to_vector; const auto lifetime = std::make_shared(); const auto counter = lifetime->make_state(0); const auto timer = lifetime->make_state(); timer->setCallback(crl::guard(session, [=] { - if ((*counter) < userIds.size()) { + if ((*counter) < participantIds.size()) { const auto peer = session->data().peer(historyPeerId); const auto channel = peer ? peer->asChannel() : nullptr; - const auto from = session->data().peer(userIds[*counter]); - if (const auto user = from->asUser(); channel && user) { - request(user, channel); + const auto from = session->data().peer( + participantIds[*counter]); + if (channel && from) { + request(from, channel); } (*counter)++; } else { @@ -304,7 +307,8 @@ void CreateModerateMessagesBox( }); }; - const auto createUsersList = [&](not_null controller) { + const auto createParticipantsList = [&]( + not_null controller) { const auto wrap = inner->add( object_ptr>( inner, @@ -322,8 +326,8 @@ void CreateModerateMessagesBox( auto &lifetime = wrap->lifetime(); const auto clicks = lifetime.make_state>(); const auto checkboxes = ranges::views::all( - users - ) | ranges::views::transform([&](not_null user) { + participants + ) | ranges::views::transform([&](not_null peer) { const auto line = container->add( object_ptr(container)); const auto &st = st::moderateBoxUserpic; @@ -331,11 +335,11 @@ void CreateModerateMessagesBox( const auto userpic = Ui::CreateChild( line, - user, + peer, st); const auto checkbox = Ui::CreateChild( line, - user->name(), + peer->name(), false, st::defaultBoxCheckbox); line->widthValue( @@ -381,10 +385,10 @@ void CreateModerateMessagesBox( }, container->lifetime()); controller->collectRequests = [=] { - auto result = Users(); + auto result = Participants(); for (auto i = 0; i < checkboxes.size(); i++) { if (checkboxes[i]->checked()) { - result.push_back(users[i]); + result.push_back(participants[i]); } } return result; @@ -395,12 +399,13 @@ void CreateModerateMessagesBox( not_null checkbox, not_null controller) { if (isSingle) { - const auto user = users.front(); - controller->collectRequests = [=] { return Users{ user }; }; + const auto p = participants.front(); + controller->collectRequests = [=] { return Participants{ p }; }; return; } - const auto button = Ui::CreateChild