From 43e6431b4ebcafd88fa8005f21b5dbba5332e147 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 26 Dec 2025 18:11:24 +0400 Subject: [PATCH] Identify auctions by id instead of slug. --- .../boxes/star_gift_auction_box.cpp | 22 +-- .../SourceFiles/boxes/star_gift_auction_box.h | 2 +- Telegram/SourceFiles/boxes/star_gift_box.cpp | 2 +- .../data/components/gift_auctions.cpp | 155 +++++++++++++----- .../data/components/gift_auctions.h | 15 +- .../window/window_session_controller.cpp | 22 ++- .../window/window_session_controller.h | 1 + 7 files changed, 159 insertions(+), 60 deletions(-) diff --git a/Telegram/SourceFiles/boxes/star_gift_auction_box.cpp b/Telegram/SourceFiles/boxes/star_gift_auction_box.cpp index 5e6ead6fb5..89d6aeebcb 100644 --- a/Telegram/SourceFiles/boxes/star_gift_auction_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_auction_box.cpp @@ -1607,7 +1607,7 @@ base::weak_qptr ChooseAndShowAuctionBox( rpl::lifetime ShowStarGiftAuction( not_null controller, PeerData *peer, - QString slug, + uint64 giftId, Fn finishRequesting, Fn boxClosed) { const auto weak = base::make_weak(controller); @@ -1618,7 +1618,7 @@ rpl::lifetime ShowStarGiftAuction( }; const auto state = std::make_shared(); auto result = session->giftAuctions().state( - slug + giftId ) | rpl::on_next([=](Data::GiftAuctionState &&value) { if (const auto onstack = finishRequesting) { onstack(); @@ -1912,7 +1912,7 @@ rpl::producer ActiveAuctionsButton( } struct Single { - QString slug; + uint64 giftId = 0; not_null document; int round = 0; int total = 0; @@ -1926,7 +1926,7 @@ object_ptr MakeActiveAuctionRow( not_null parent, not_null window, not_null document, - const QString &slug, + uint64 giftId, rpl::producer value) { auto result = object_ptr(parent); const auto raw = result.data(); @@ -2023,7 +2023,7 @@ object_ptr MakeActiveAuctionRow( Ui::Text::Colorized(NiceCountdownText(seconds))); })); button->setClickedCallback([=] { - window->showStarGiftAuction(slug); + window->showStarGiftAuction(giftId); }); button->setFullRadius(true); raw->widthValue() | rpl::on_next([=](int width) { @@ -2039,9 +2039,9 @@ Fn ActiveAuctionsCallback( const auto &list = auctions.list; const auto count = int(list.size()); if (count == 1) { - const auto slug = list.front()->gift->auctionSlug; + const auto giftId = list.front()->gift->id; return [=] { - window->showStarGiftAuction(slug); + window->showStarGiftAuction(giftId); }; } struct Auctions { @@ -2050,7 +2050,7 @@ Fn ActiveAuctionsCallback( const auto state = std::make_shared(); const auto singleFrom = [](const Data::GiftAuctionState &state) { return Single{ - .slug = state.gift->auctionSlug, + .giftId = state.gift->id, .document = state.gift->document, .round = state.currentRound, .total = state.totalRounds, @@ -2080,7 +2080,7 @@ Fn ActiveAuctionsCallback( const auto &now = entry.current(); entry = auctions->state( - now.slug + now.giftId ) | rpl::filter([=](const GiftAuctionState &state) { return state.my.bid != 0; }) | rpl::map(singleFrom); @@ -2091,12 +2091,12 @@ Fn ActiveAuctionsCallback( box, window, now.document, - now.slug, + now.giftId, entry.value()), st::boxRowPadding + QMargins(0, skip, 0, skip)); auctions->state( - now.slug + now.giftId ) | rpl::on_next([=](const GiftAuctionState &state) { if (!state.my.bid) { delete row; diff --git a/Telegram/SourceFiles/boxes/star_gift_auction_box.h b/Telegram/SourceFiles/boxes/star_gift_auction_box.h index 880abafb89..d059adda1f 100644 --- a/Telegram/SourceFiles/boxes/star_gift_auction_box.h +++ b/Telegram/SourceFiles/boxes/star_gift_auction_box.h @@ -34,7 +34,7 @@ class GenericBox; [[nodiscard]] rpl::lifetime ShowStarGiftAuction( not_null controller, PeerData *peer, - QString slug, + uint64 giftId, Fn finishRequesting, Fn boxClosed); diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index cf2018ad78..76da78d8f2 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -1850,7 +1850,7 @@ void CheckMaybeGiftLocked( state->resaleLifetime = ShowStarGiftAuction( window, peer, - star->info.auctionSlug, + id, [=] { state->resaleRequestingId = 0; }, crl::guard(raw, [=] { state->resaleLifetime.destroy(); diff --git a/Telegram/SourceFiles/data/components/gift_auctions.cpp b/Telegram/SourceFiles/data/components/gift_auctions.cpp index 8ad720e550..33faaaad65 100644 --- a/Telegram/SourceFiles/data/components/gift_auctions.cpp +++ b/Telegram/SourceFiles/data/components/gift_auctions.cpp @@ -33,11 +33,85 @@ GiftAuctions::GiftAuctions(not_null session) GiftAuctions::~GiftAuctions() = default; -rpl::producer GiftAuctions::state(const QString &slug) { +void GiftAuctions::resolveSlug(const QString &slug, Fn done) { + const auto it = _slugToGiftId.find(slug); + if (it != _slugToGiftId.end()) { + done(it->second); + return; + } + + auto &request = _slugRequests[slug]; + request.callbacks.push_back(std::move(done)); + if (request.callbacks.size() > 1) { + return; + } + + _session->api().request(MTPpayments_GetStarGiftAuctionState( + MTP_inputStarGiftAuctionSlug(MTP_string(slug)), + MTP_int(0) + )).done([=](const MTPpayments_StarGiftAuctionState &result) { + const auto &data = result.data(); + + _session->data().processUsers(data.vusers()); + _session->data().processChats(data.vchats()); + + auto gift = Api::FromTL(_session, data.vgift()); + if (!gift || !gift->id) { + _slugRequests.erase(slug); + return; + } + const auto giftId = gift->id; + + _slugToGiftId.emplace(slug, giftId); + + auto &entry = _map[giftId]; + if (!entry) { + entry = std::make_unique(); + } + const auto raw = entry.get(); + raw->state.gift = std::move(gift); + + applyStateResponse(raw, result); + + auto callbacks = std::move(_slugRequests[slug].callbacks); + _slugRequests.erase(slug); + for (const auto &callback : callbacks) { + callback(giftId); + } + }).fail([=] { + _slugRequests.erase(slug); + }).send(); +} + +void GiftAuctions::applyStateResponse( + not_null entry, + const MTPpayments_StarGiftAuctionState &result) { + const auto &data = result.data(); + + const auto timeout = data.vtimeout().v; + const auto ms = timeout * crl::time(1000); + entry->state.subscribedTill = ms ? (crl::now() + ms) : -1; + + const auto was = myStateKey(entry->state); + apply(entry, data.vstate()); + apply(entry, data.vuser_state()); + + if (entry->changes.has_consumers()) { + entry->changes.fire({}); + if (ms && (!_timer.isActive() || _timer.remainingTime() > ms)) { + _timer.callOnce(ms); + } + } + if (was != myStateKey(entry->state)) { + _activeChanged.fire({}); + } +} + +rpl::producer GiftAuctions::state(uint64 giftId) { return [=](auto consumer) { auto lifetime = rpl::lifetime(); - auto &entry = _map[slug]; + auto &entry = _map[giftId]; if (!entry) { entry = std::make_unique(); } @@ -52,7 +126,7 @@ rpl::producer GiftAuctions::state(const QString &slug) { || raw->state.subscribedTill >= now) { consumer.put_next_copy(raw->state); } else if (raw->state.subscribedTill >= 0) { - request(slug); + request(giftId); } return lifetime; @@ -196,7 +270,7 @@ rpl::producer GiftAuctions::hasActiveChanges() const { } bool GiftAuctions::hasActive() const { - for (const auto &[slug, entry] : _map) { + for (const auto &[giftId, entry] : _map) { if (myStateKey(entry->state)) { return true; } @@ -207,13 +281,13 @@ bool GiftAuctions::hasActive() const { void GiftAuctions::checkSubscriptions() { const auto now = crl::now(); auto next = crl::time(); - for (const auto &[slug, entry] : _map) { + for (const auto &[giftId, entry] : _map) { const auto raw = entry.get(); const auto till = raw->state.subscribedTill; if (till <= 0 || !raw->changes.has_consumers()) { continue; } else if (till <= now) { - request(slug); + request(giftId); } else { const auto timeout = till - now; if (!next || timeout < next) { @@ -249,7 +323,7 @@ auto GiftAuctions::myStateKey(const GiftAuctionState &state) const ActiveAuctions GiftAuctions::collectActive() const { auto result = ActiveAuctions(); result.list.reserve(_map.size()); - for (const auto &[slug, entry] : _map) { + for (const auto &[giftId, entry] : _map) { const auto raw = &entry->state; if (raw->gift && raw->my.date) { result.list.push_back(raw); @@ -274,23 +348,24 @@ void GiftAuctions::requestActive() { _activeRequestId = _session->api().request( MTPpayments_GetStarGiftActiveAuctions(MTP_long(countActiveHash())) ).done([=](const MTPpayments_StarGiftActiveAuctions &result) { + _activeRequestId = 0; result.match([=](const MTPDpayments_starGiftActiveAuctions &data) { const auto owner = &_session->data(); owner->processUsers(data.vusers()); owner->processChats(data.vchats()); - auto giftsFound = base::flat_set(); + auto giftsFound = base::flat_set(); const auto &list = data.vauctions().v; giftsFound.reserve(list.size()); for (const auto &auction : list) { const auto &data = auction.data(); auto gift = Api::FromTL(_session, data.vgift()); - const auto slug = gift ? gift->auctionSlug : QString(); - if (slug.isEmpty()) { + if (!gift || !gift->id) { LOG(("Api Error: Bad auction gift.")); continue; } - auto &entry = _map[slug]; + const auto giftId = gift->id; + auto &entry = _map[giftId]; if (!entry) { entry = std::make_unique(); } @@ -300,21 +375,21 @@ void GiftAuctions::requestActive() { } apply(raw, data.vstate()); apply(raw, data.vuser_state()); - giftsFound.emplace(slug); + giftsFound.emplace(giftId); } - for (const auto &[slug, entry] : _map) { + for (const auto &[giftId, entry] : _map) { const auto my = &entry->state.my; - if (my->date && !giftsFound.contains(slug)) { + if (my->date && !giftsFound.contains(giftId)) { my->to = nullptr; my->minBidAmount = 0; my->bid = 0; my->date = 0; my->returned = false; - giftsFound.emplace(slug); + giftsFound.emplace(giftId); } } - for (const auto &slug : giftsFound) { - _map[slug]->changes.fire({}); + for (const auto &giftId : giftsFound) { + _map[giftId]->changes.fire({}); } _activeChanged.fire({}); }, [](const MTPDpayments_starGiftActiveAuctionsNotModified &) { @@ -322,20 +397,27 @@ void GiftAuctions::requestActive() { }).send(); } -void GiftAuctions::request(const QString &slug) { - auto &entry = _map[slug]; - Assert(entry != nullptr); - - const auto raw = entry.get(); +void GiftAuctions::request(uint64 giftId) { + auto it = _map.find(giftId); + if (it == _map.end()) { + return; + } + const auto raw = it->second.get(); if (raw->requested) { return; } raw->requested = true; _session->api().request(MTPpayments_GetStarGiftAuctionState( - MTP_inputStarGiftAuctionSlug(MTP_string(slug)), + MTP_inputStarGiftAuction(MTP_long(giftId)), MTP_int(raw->state.version) )).done([=](const MTPpayments_StarGiftAuctionState &result) { + auto it = _map.find(giftId); + if (it == _map.end()) { + return; + } + const auto raw = it->second.get(); raw->requested = false; + const auto &data = result.data(); _session->data().processUsers(data.vusers()); @@ -345,32 +427,19 @@ void GiftAuctions::request(const QString &slug) { if (!raw->state.gift) { return; } - const auto timeout = data.vtimeout().v; - const auto ms = timeout * crl::time(1000); - raw->state.subscribedTill = ms ? (crl::now() + ms) : -1; - const auto was = myStateKey(raw->state); - apply(raw, data.vstate()); - apply(raw, data.vuser_state()); - if (raw->changes.has_consumers()) { - raw->changes.fire({}); - if (ms && (!_timer.isActive() || _timer.remainingTime() > ms)) { - _timer.callOnce(ms); - } - } - if (was != myStateKey(raw->state)) { - _activeChanged.fire({}); + applyStateResponse(raw, result); + }).fail([=] { + auto it = _map.find(giftId); + if (it != _map.end()) { + it->second->requested = false; } }).send(); } GiftAuctions::Entry *GiftAuctions::find(uint64 giftId) const { - for (const auto &[slug, entry] : _map) { - if (entry->state.gift && entry->state.gift->id == giftId) { - return entry.get(); - } - } - return nullptr; + const auto it = _map.find(giftId); + return (it != _map.end()) ? it->second.get() : nullptr; } void GiftAuctions::apply( diff --git a/Telegram/SourceFiles/data/components/gift_auctions.h b/Telegram/SourceFiles/data/components/gift_auctions.h index 48f186e54c..a569b0fd01 100644 --- a/Telegram/SourceFiles/data/components/gift_auctions.h +++ b/Telegram/SourceFiles/data/components/gift_auctions.h @@ -80,7 +80,8 @@ public: explicit GiftAuctions(not_null session); ~GiftAuctions(); - [[nodiscard]] rpl::producer state(const QString &slug); + [[nodiscard]] rpl::producer state(uint64 giftId); + void resolveSlug(const QString &slug, Fn done); void apply(const MTPDupdateStarGiftAuctionState &data); void apply(const MTPDupdateStarGiftAuctionUserState &data); @@ -117,9 +118,15 @@ private: Data::UniqueGiftAttributes lists; std::vector> waiters; }; + struct SlugRequest { + std::vector> callbacks; + }; - void request(const QString &slug); + void request(uint64 giftId); Entry *find(uint64 giftId) const; + void applyStateResponse( + not_null entry, + const MTPpayments_StarGiftAuctionState &result); void apply( not_null entry, const MTPStarGiftAuctionState &state); @@ -142,7 +149,9 @@ private: const not_null _session; base::Timer _timer; - base::flat_map> _map; + base::flat_map> _map; + base::flat_map _slugRequests; + base::flat_map _slugToGiftId; base::flat_map _attributes; rpl::event_stream<> _activeChanged; diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 3680712171..7210a4ef89 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_subsection_tabs.h" #include "media/player/media_player_instance.h" #include "media/view/media_view_open_common.h" +#include "data/components/gift_auctions.h" #include "data/components/recent_peers.h" #include "data/stickers/data_custom_emoji.h" #include "data/data_document_resolver.h" @@ -3652,10 +3653,29 @@ void SessionController::dropSubsectionTabs() { void SessionController::showStarGiftAuction(const QString &slug) { _starGiftAuctionLifetime.destroy(); + + const auto requesting = _starGiftAuctionLifetime.make_state< + base::has_weak_ptr + >(); + const auto guard = base::make_weak(requesting); + const auto weak = base::make_weak(this); + session().giftAuctions().resolveSlug(slug, [=](uint64 giftId) { + if (!guard || !weak) { + return; + } + _starGiftAuctionLifetime.destroy(); + if (giftId) { + showStarGiftAuction(giftId); + } + }); +} + +void SessionController::showStarGiftAuction(uint64 giftId) { + _starGiftAuctionLifetime.destroy(); _starGiftAuctionLifetime = Ui::ShowStarGiftAuction( this, nullptr, - slug, + giftId, [] {}, [=] { _starGiftAuctionLifetime.destroy(); }); } diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index ab1ef01e5c..cf0e7abfbb 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -697,6 +697,7 @@ public: void dropSubsectionTabs(); void showStarGiftAuction(const QString &slug); + void showStarGiftAuction(uint64 giftId); [[nodiscard]] rpl::lifetime &lifetime() { return _lifetime;