From 6008d9e12ba877de5f6964752f93508d9b85fa70 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 13 Apr 2025 20:53:21 +0300 Subject: [PATCH] Added birthday sublist to contact list in gift box. --- Telegram/Resources/langs/lang.strings | 3 + Telegram/SourceFiles/boxes/star_gift_box.cpp | 229 +++++++++++++++---- 2 files changed, 190 insertions(+), 42 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 231f2e352d..f1089b24c1 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3455,6 +3455,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_show_on_channel" = "Display in channel's Gifts"; "lng_gift_availability" = "Availability"; "lng_gift_from_hidden" = "Hidden User"; +"lng_gift_subtitle_birthdays" = "Birthdays"; +"lng_gift_list_birthday_status_today" = "{emoji} Birthday today"; +"lng_gift_list_birthday_status_yesterday" = "Birthday yesterday"; "lng_gift_self_status" = "buy yourself a gift"; "lng_gift_self_title" = "Buy a Gift"; "lng_gift_self_about" = "Buy yourself a gift to display on your page or reserve for later.\n\nLimited-edition gifts upgraded to collectibles can be gifted to others later."; diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index e20967b2e9..092fa9c2d2 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/tabbed_panel.h" #include "chat_helpers/tabbed_selector.h" #include "core/ui_integration.h" +#include "data/data_birthday.h" #include "data/data_changes.h" #include "data/data_channel.h" #include "data/data_credits.h" @@ -2257,10 +2258,11 @@ void GiftBox( } } -struct SelfOption { +struct CustomList { object_ptr content = { nullptr }; Fn overrideKey; Fn activate; + Fn hasSelection; }; class Controller final : public ContactsBoxController { @@ -2284,32 +2286,39 @@ private: void rowClicked(not_null row) override; const Fn)> _choose; - SelfOption _selfOption; + const std::vector _contactBirthdays; + CustomList _selfOption; + CustomList _birthdayOptions; + + bool _skipUpDirectionSelect = false; }; -[[nodiscard]] SelfOption MakeSelfOption( +[[nodiscard]] CustomList MakeCustomList( not_null session, - Fn activate) { + Fn)> fill, + Fn)> activate, + rpl::producer below) { class SelfController final : public PeerListController { public: SelfController( not_null session, - Fn activate) + Fn)> fill, + Fn)> activate) : _session(session) - , _activate(std::move(activate)) { + , _activate(std::move(activate)) + , _fill(std::move(fill)) { } void prepare() override { - auto row = std::make_unique(_session->user()); - row->setCustomStatus(tr::lng_gift_self_status(tr::now)); - delegate()->peerListAppendRow(std::move(row)); - delegate()->peerListRefreshRows(); + if (_fill) { + _fill(this); + } } void loadMoreRows() override { } void rowClicked(not_null row) override { - _activate(); + _activate(row->peer()); } Main::Session &session() const override { return *_session; @@ -2317,7 +2326,8 @@ private: private: const not_null _session; - Fn _activate; + Fn)> _activate; + Fn)> _fill; }; @@ -2329,9 +2339,12 @@ private: const auto delegate = container->lifetime().make_state< PeerListContentDelegateSimple >(); - const auto controller = container->lifetime().make_state< - SelfController - >(session, activate); + const auto controller + = container->lifetime().make_state( + session, + fill, + activate); + controller->setStyleOverrides(&st::peerListSingleRow); const auto content = container->add(object_ptr( container, @@ -2339,10 +2352,12 @@ private: delegate->setContent(content); controller->setDelegate(delegate); - Ui::AddSkip(container); - container->add(CreatePeerListSectionSubtitle( - container, - tr::lng_contacts_header())); + if (below) { + Ui::AddSkip(container); + container->add(CreatePeerListSectionSubtitle( + container, + std::move(below))); + } const auto overrideKey = [=](int direction, int from, int to) { if (!content->isVisible()) { @@ -2368,11 +2383,19 @@ private: } return false; }; + const auto hasSelection = [=] { + return content->isVisible() && content->hasSelection(); + }; return { .content = std::move(result), .overrideKey = overrideKey, - .activate = activate, + .activate = [=] { + if (content->hasSelection()) { + activate(content->rowAt(content->selectedIndex())->peer()); + } + }, + .hasSelection = hasSelection, }; } @@ -2381,7 +2404,73 @@ Controller::Controller( Fn)> choose) : ContactsBoxController(session) , _choose(std::move(choose)) -, _selfOption(MakeSelfOption(session, [=] { _choose(session->user()); })) { +, _contactBirthdays( + session->data().knownContactBirthdays().value_or(std::vector{})) +, _selfOption( + MakeCustomList( + session, + [=](not_null controller) { + auto row = std::make_unique(session->user()); + row->setCustomStatus(tr::lng_gift_self_status(tr::now)); + controller->delegate()->peerListAppendRow(std::move(row)); + controller->delegate()->peerListRefreshRows(); + }, + [=](not_null peer) { _choose(peer); }, + _contactBirthdays.empty() + ? tr::lng_contacts_header() + : tr::lng_gift_subtitle_birthdays())) +, _birthdayOptions( + MakeCustomList( + session, + [=](not_null controller) { + const auto status = [=](const Data::Birthday &date) { + if (Data::IsBirthdayToday(date)) { + return tr::lng_gift_list_birthday_status_today( + tr::now, + lt_emoji, + Data::BirthdayCake()); + } + const auto yesterday = QDate::currentDate().addDays(-1); + return (date.day() == yesterday.day() + && date.month() == yesterday.month()) + ? tr::lng_gift_list_birthday_status_yesterday(tr::now) + : QString(); + }; + + auto usersWithBirthdays = ranges::views::all( + _contactBirthdays + ) | ranges::views::transform([&](UserId userId) { + return session->data().user(userId); + }) | ranges::to_vector; + + ranges::sort(usersWithBirthdays, [](UserData *a, UserData *b) { + const auto aBirthday = a->birthday(); + const auto bBirthday = b->birthday(); + const auto aIsToday = Data::IsBirthdayToday(aBirthday); + const auto bIsToday = Data::IsBirthdayToday(bBirthday); + if (aIsToday != bIsToday) { + return aIsToday > bIsToday; + } + if (aBirthday.month() != bBirthday.month()) { + return aBirthday.month() < bBirthday.month(); + } + return aBirthday.day() < bBirthday.day(); + }); + + for (const auto user : usersWithBirthdays) { + auto row = std::make_unique(user); + if (auto s = status(user->birthday()); !s.isEmpty()) { + row->setCustomStatus(std::move(s)); + } + controller->delegate()->peerListAppendRow(std::move(row)); + } + + controller->delegate()->peerListRefreshRows(); + }, + [=](not_null peer) { _choose(peer); }, + _contactBirthdays.empty() + ? rpl::producer(nullptr) + : tr::lng_contacts_header())) { setStyleOverrides(&st::peerListSmallSkips); } @@ -2389,18 +2478,65 @@ void Controller::noSearchSubmit() { if (const auto onstack = _selfOption.activate) { onstack(); } + if (const auto onstack = _birthdayOptions.activate) { + onstack(); + } } bool Controller::overrideKeyboardNavigation( int direction, - int fromIndex, - int toIndex) { - return _selfOption.overrideKey - && _selfOption.overrideKey(direction, fromIndex, toIndex); + int from, + int to) { + if (direction == -1 && from == -1 && to == -1 && _skipUpDirectionSelect) { + return true; + } + _skipUpDirectionSelect = false; + if (direction > 0) { + if (!_selfOption.hasSelection() && !_birthdayOptions.hasSelection()) { + return _selfOption.overrideKey(direction, from, to); + } + if (_selfOption.hasSelection() && !_birthdayOptions.hasSelection()) { + if (_selfOption.overrideKey(direction, from, to)) { + return true; + } else { + return _birthdayOptions.overrideKey(direction, from, to); + } + } + if (!_selfOption.hasSelection() && _birthdayOptions.hasSelection()) { + if (_birthdayOptions.overrideKey(direction, from, to)) { + return true; + } + } + } else if (direction < 0) { + if (!_selfOption.hasSelection() && !_birthdayOptions.hasSelection()) { + return _birthdayOptions.overrideKey(direction, from, to); + } + if (!_selfOption.hasSelection() && _birthdayOptions.hasSelection()) { + if (_birthdayOptions.overrideKey(direction, from, to)) { + return true; + } else if (!_birthdayOptions.hasSelection()) { + const auto res = _selfOption.overrideKey(direction, from, to); + _skipUpDirectionSelect = _selfOption.hasSelection(); + return res; + } + } + if (_selfOption.hasSelection() && !_birthdayOptions.hasSelection()) { + if (_selfOption.overrideKey(direction, from, to)) { + _skipUpDirectionSelect = _selfOption.hasSelection(); + return true; + } + } + } + return false; } std::unique_ptr Controller::createRow( not_null user) { + if (const auto birthday = user->owner().knownContactBirthdays()) { + if (ranges::contains(*birthday, peerToUser(user->id))) { + return nullptr; + } + } if (user->isSelf() || user->isBot() || user->isServiceUser() @@ -2411,7 +2547,10 @@ std::unique_ptr Controller::createRow( } void Controller::prepareViewHook() { - delegate()->peerListSetAboveWidget(std::move(_selfOption.content)); + auto list = object_ptr((QWidget*)nullptr); + list->add(std::move(_selfOption.content)); + list->add(std::move(_birthdayOptions.content)); + delegate()->peerListSetAboveWidget(std::move(list)); } void Controller::rowClicked(not_null row) { @@ -2422,23 +2561,29 @@ void Controller::rowClicked(not_null row) { void ChooseStarGiftRecipient( not_null window) { - auto controller = std::make_unique( - &window->session(), - [=](not_null peer) { - ShowStarGiftBox(window, peer); - }); - const auto controllerRaw = controller.get(); - auto initBox = [=](not_null box) { - box->setTitle(tr::lng_gift_premium_or_stars()); - box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + const auto session = &window->session(); + const auto lifetime = std::make_shared(); + session->data().contactBirthdays( + ) | rpl::start_with_next(crl::guard(session, [=] { + lifetime->destroy(); + auto controller = std::make_unique( + session, + [=](not_null peer) { + ShowStarGiftBox(window, peer); + }); + const auto controllerRaw = controller.get(); + auto initBox = [=](not_null box) { + box->setTitle(tr::lng_gift_premium_or_stars()); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); - box->noSearchSubmits() | rpl::start_with_next([=] { - controllerRaw->noSearchSubmit(); - }, box->lifetime()); - }; - window->show( - Box(std::move(controller), std::move(initBox)), - LayerOption::KeepOther); + box->noSearchSubmits() | rpl::start_with_next([=] { + controllerRaw->noSearchSubmit(); + }, box->lifetime()); + }; + window->show( + Box(std::move(controller), std::move(initBox)), + LayerOption::KeepOther); + }), *lifetime); } void ShowStarGiftBox(