Added birthday sublist to contact list in gift box.

This commit is contained in:
23rd 2025-04-13 20:53:21 +03:00 committed by John Preston
parent 81da453ec4
commit 6008d9e12b
2 changed files with 190 additions and 42 deletions

View file

@ -3455,6 +3455,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_show_on_channel" = "Display in channel's Gifts"; "lng_gift_show_on_channel" = "Display in channel's Gifts";
"lng_gift_availability" = "Availability"; "lng_gift_availability" = "Availability";
"lng_gift_from_hidden" = "Hidden User"; "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_status" = "buy yourself a gift";
"lng_gift_self_title" = "Buy 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."; "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.";

View file

@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/tabbed_panel.h" #include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h" #include "chat_helpers/tabbed_selector.h"
#include "core/ui_integration.h" #include "core/ui_integration.h"
#include "data/data_birthday.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_credits.h" #include "data/data_credits.h"
@ -2257,10 +2258,11 @@ void GiftBox(
} }
} }
struct SelfOption { struct CustomList {
object_ptr<Ui::RpWidget> content = { nullptr }; object_ptr<Ui::RpWidget> content = { nullptr };
Fn<bool(int, int, int)> overrideKey; Fn<bool(int, int, int)> overrideKey;
Fn<void()> activate; Fn<void()> activate;
Fn<bool()> hasSelection;
}; };
class Controller final : public ContactsBoxController { class Controller final : public ContactsBoxController {
@ -2284,32 +2286,39 @@ private:
void rowClicked(not_null<PeerListRow*> row) override; void rowClicked(not_null<PeerListRow*> row) override;
const Fn<void(not_null<PeerData*>)> _choose; const Fn<void(not_null<PeerData*>)> _choose;
SelfOption _selfOption; const std::vector<UserId> _contactBirthdays;
CustomList _selfOption;
CustomList _birthdayOptions;
bool _skipUpDirectionSelect = false;
}; };
[[nodiscard]] SelfOption MakeSelfOption( [[nodiscard]] CustomList MakeCustomList(
not_null<Main::Session*> session, not_null<Main::Session*> session,
Fn<void()> activate) { Fn<void(not_null<PeerListController*>)> fill,
Fn<void(not_null<PeerData*>)> activate,
rpl::producer<QString> below) {
class SelfController final : public PeerListController { class SelfController final : public PeerListController {
public: public:
SelfController( SelfController(
not_null<Main::Session*> session, not_null<Main::Session*> session,
Fn<void()> activate) Fn<void(not_null<PeerListController*>)> fill,
Fn<void(not_null<PeerData*>)> activate)
: _session(session) : _session(session)
, _activate(std::move(activate)) { , _activate(std::move(activate))
, _fill(std::move(fill)) {
} }
void prepare() override { void prepare() override {
auto row = std::make_unique<PeerListRow>(_session->user()); if (_fill) {
row->setCustomStatus(tr::lng_gift_self_status(tr::now)); _fill(this);
delegate()->peerListAppendRow(std::move(row)); }
delegate()->peerListRefreshRows();
} }
void loadMoreRows() override { void loadMoreRows() override {
} }
void rowClicked(not_null<PeerListRow*> row) override { void rowClicked(not_null<PeerListRow*> row) override {
_activate(); _activate(row->peer());
} }
Main::Session &session() const override { Main::Session &session() const override {
return *_session; return *_session;
@ -2317,7 +2326,8 @@ private:
private: private:
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
Fn<void()> _activate; Fn<void(not_null<PeerData*>)> _activate;
Fn<void(not_null<PeerListController*>)> _fill;
}; };
@ -2329,9 +2339,12 @@ private:
const auto delegate = container->lifetime().make_state< const auto delegate = container->lifetime().make_state<
PeerListContentDelegateSimple PeerListContentDelegateSimple
>(); >();
const auto controller = container->lifetime().make_state< const auto controller
SelfController = container->lifetime().make_state<SelfController>(
>(session, activate); session,
fill,
activate);
controller->setStyleOverrides(&st::peerListSingleRow); controller->setStyleOverrides(&st::peerListSingleRow);
const auto content = container->add(object_ptr<PeerListContent>( const auto content = container->add(object_ptr<PeerListContent>(
container, container,
@ -2339,10 +2352,12 @@ private:
delegate->setContent(content); delegate->setContent(content);
controller->setDelegate(delegate); controller->setDelegate(delegate);
Ui::AddSkip(container); if (below) {
container->add(CreatePeerListSectionSubtitle( Ui::AddSkip(container);
container, container->add(CreatePeerListSectionSubtitle(
tr::lng_contacts_header())); container,
std::move(below)));
}
const auto overrideKey = [=](int direction, int from, int to) { const auto overrideKey = [=](int direction, int from, int to) {
if (!content->isVisible()) { if (!content->isVisible()) {
@ -2368,11 +2383,19 @@ private:
} }
return false; return false;
}; };
const auto hasSelection = [=] {
return content->isVisible() && content->hasSelection();
};
return { return {
.content = std::move(result), .content = std::move(result),
.overrideKey = overrideKey, .overrideKey = overrideKey,
.activate = activate, .activate = [=] {
if (content->hasSelection()) {
activate(content->rowAt(content->selectedIndex())->peer());
}
},
.hasSelection = hasSelection,
}; };
} }
@ -2381,7 +2404,73 @@ Controller::Controller(
Fn<void(not_null<PeerData*>)> choose) Fn<void(not_null<PeerData*>)> choose)
: ContactsBoxController(session) : ContactsBoxController(session)
, _choose(std::move(choose)) , _choose(std::move(choose))
, _selfOption(MakeSelfOption(session, [=] { _choose(session->user()); })) { , _contactBirthdays(
session->data().knownContactBirthdays().value_or(std::vector<UserId>{}))
, _selfOption(
MakeCustomList(
session,
[=](not_null<PeerListController*> controller) {
auto row = std::make_unique<PeerListRow>(session->user());
row->setCustomStatus(tr::lng_gift_self_status(tr::now));
controller->delegate()->peerListAppendRow(std::move(row));
controller->delegate()->peerListRefreshRows();
},
[=](not_null<PeerData*> peer) { _choose(peer); },
_contactBirthdays.empty()
? tr::lng_contacts_header()
: tr::lng_gift_subtitle_birthdays()))
, _birthdayOptions(
MakeCustomList(
session,
[=](not_null<PeerListController*> 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<PeerListRow>(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<PeerData*> peer) { _choose(peer); },
_contactBirthdays.empty()
? rpl::producer<QString>(nullptr)
: tr::lng_contacts_header())) {
setStyleOverrides(&st::peerListSmallSkips); setStyleOverrides(&st::peerListSmallSkips);
} }
@ -2389,18 +2478,65 @@ void Controller::noSearchSubmit() {
if (const auto onstack = _selfOption.activate) { if (const auto onstack = _selfOption.activate) {
onstack(); onstack();
} }
if (const auto onstack = _birthdayOptions.activate) {
onstack();
}
} }
bool Controller::overrideKeyboardNavigation( bool Controller::overrideKeyboardNavigation(
int direction, int direction,
int fromIndex, int from,
int toIndex) { int to) {
return _selfOption.overrideKey if (direction == -1 && from == -1 && to == -1 && _skipUpDirectionSelect) {
&& _selfOption.overrideKey(direction, fromIndex, toIndex); 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<PeerListRow> Controller::createRow( std::unique_ptr<PeerListRow> Controller::createRow(
not_null<UserData*> user) { not_null<UserData*> user) {
if (const auto birthday = user->owner().knownContactBirthdays()) {
if (ranges::contains(*birthday, peerToUser(user->id))) {
return nullptr;
}
}
if (user->isSelf() if (user->isSelf()
|| user->isBot() || user->isBot()
|| user->isServiceUser() || user->isServiceUser()
@ -2411,7 +2547,10 @@ std::unique_ptr<PeerListRow> Controller::createRow(
} }
void Controller::prepareViewHook() { void Controller::prepareViewHook() {
delegate()->peerListSetAboveWidget(std::move(_selfOption.content)); auto list = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
list->add(std::move(_selfOption.content));
list->add(std::move(_birthdayOptions.content));
delegate()->peerListSetAboveWidget(std::move(list));
} }
void Controller::rowClicked(not_null<PeerListRow*> row) { void Controller::rowClicked(not_null<PeerListRow*> row) {
@ -2422,23 +2561,29 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
void ChooseStarGiftRecipient( void ChooseStarGiftRecipient(
not_null<Window::SessionController*> window) { not_null<Window::SessionController*> window) {
auto controller = std::make_unique<Controller>( const auto session = &window->session();
&window->session(), const auto lifetime = std::make_shared<rpl::lifetime>();
[=](not_null<PeerData*> peer) { session->data().contactBirthdays(
ShowStarGiftBox(window, peer); ) | rpl::start_with_next(crl::guard(session, [=] {
}); lifetime->destroy();
const auto controllerRaw = controller.get(); auto controller = std::make_unique<Controller>(
auto initBox = [=](not_null<PeerListBox*> box) { session,
box->setTitle(tr::lng_gift_premium_or_stars()); [=](not_null<PeerData*> peer) {
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); ShowStarGiftBox(window, peer);
});
const auto controllerRaw = controller.get();
auto initBox = [=](not_null<PeerListBox*> box) {
box->setTitle(tr::lng_gift_premium_or_stars());
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
box->noSearchSubmits() | rpl::start_with_next([=] { box->noSearchSubmits() | rpl::start_with_next([=] {
controllerRaw->noSearchSubmit(); controllerRaw->noSearchSubmit();
}, box->lifetime()); }, box->lifetime());
}; };
window->show( window->show(
Box<PeerListBox>(std::move(controller), std::move(initBox)), Box<PeerListBox>(std::move(controller), std::move(initBox)),
LayerOption::KeepOther); LayerOption::KeepOther);
}), *lifetime);
} }
void ShowStarGiftBox( void ShowStarGiftBox(