From 448356f53441b2737d7f300c1bbc031e24f49a4c Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 24 Jul 2025 16:57:54 +0400 Subject: [PATCH] Allow editing/deleting collections. --- Telegram/Resources/langs/lang.strings | 2 + .../info_peer_gifts_collections.cpp | 76 ++++++++++--- .../peer_gifts/info_peer_gifts_collections.h | 8 ++ .../peer_gifts/info_peer_gifts_widget.cpp | 106 ++++++++++++++++++ Telegram/SourceFiles/ui/controls/sub_tabs.cpp | 10 ++ Telegram/SourceFiles/ui/controls/sub_tabs.h | 3 + 6 files changed, 189 insertions(+), 16 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 42bdae3036..ac5bd51b7f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3765,6 +3765,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_collection_limit_title" = "Limit Reached"; "lng_gift_collection_limit_text" = "Please remove one of the existing collections to add a new one."; "lng_gift_collection_delete" = "Delete Collection"; +"lng_gift_collection_delete_sure" = "Are you sure you want to delete this collection?"; +"lng_gift_collection_delete_button" = "Delete"; "lng_gift_collection_add_to" = "Add to Collection"; "lng_accounts_limit_title" = "Limit Reached"; diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_collections.cpp b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_collections.cpp index c9c552814a..9a1cafb401 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_collections.cpp +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_collections.cpp @@ -25,26 +25,29 @@ namespace { constexpr auto kCollectionNameLimit = 12; -} // namespace - -void NewCollectionBox( +void EditCollectionBox( not_null box, not_null navigation, not_null peer, + int id, Data::SavedStarGiftId addId, - Fn added) { + QString currentName, + Fn finished) { box->setTitle(tr::lng_gift_collection_new_title()); box->addRow( object_ptr( box, - tr::lng_gift_collection_new_text(), + (id + ? tr::lng_gift_collection_edit() + : tr::lng_gift_collection_new_text()), st::collectionAbout)); const auto title = box->addRow( object_ptr( box, st::collectionNameField, - tr::lng_gift_collection_new_ph())); + tr::lng_gift_collection_new_ph(), + currentName)); title->setMaxLength(kCollectionNameLimit * 2); box->setFocusCallback([=] { title->setFocusFast(); @@ -56,7 +59,10 @@ void NewCollectionBox( const auto session = &peer->session(); const auto creating = std::make_shared(false); - box->addButton(tr::lng_gift_collection_new_create(), [=] { + auto text = id + ? tr::lng_settings_save() + : tr::lng_gift_collection_new_create(); + box->addButton(std::move(text), [=] { if (*creating) { return; } @@ -72,22 +78,19 @@ void NewCollectionBox( ids.push_back(Api::InputSavedStarGiftId(addId)); } const auto weak = base::make_weak(box); - session->api().request(MTPpayments_CreateStarGiftCollection( - peer->input, - MTP_string(text), - MTP_vector(ids) - )).done([=](const MTPStarGiftCollection &result) { + const auto done = [=](const MTPStarGiftCollection &result) { *creating = false; - if (const auto onstack = added) { + if (const auto onstack = finished) { onstack(result); } if (const auto strong = weak.get()) { strong->closeBox(); } - }).fail([=](const MTP::Error &error) { + }; + const auto fail = [=](const MTP::Error &error) { *creating = false; const auto &type = error.type(); - if (type == u""_q) { + if (type == u"COLLECTIONS_TOO_MANY"_q) { show->show(Ui::MakeInformBox({ .text = tr::lng_gift_collection_limit_text(), .confirmText = tr::lng_box_ok(), @@ -99,13 +102,54 @@ void NewCollectionBox( } else { show->showToast(error.type()); } - }).send(); + }; + if (id) { + using Flag = MTPpayments_UpdateStarGiftCollection::Flag; + session->api().request(MTPpayments_UpdateStarGiftCollection( + MTP_flags(Flag::f_title), + peer->input, + MTP_int(id), + MTP_string(text), + MTPVector(), + MTPVector(), + MTPVector() + )).done(done).fail(fail).send(); + } else { + session->api().request(MTPpayments_CreateStarGiftCollection( + peer->input, + MTP_string(text), + MTP_vector(ids) + )).done(done).fail(fail).send(); + } }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} +} // namespace + +void NewCollectionBox( + not_null box, + not_null navigation, + not_null peer, + Data::SavedStarGiftId addId, + Fn added) { + EditCollectionBox(box, navigation, peer, 0, addId, QString(), added); +} + +void EditCollectionNameBox( + not_null box, + not_null navigation, + not_null peer, + int id, + QString current, + Fn done) { + EditCollectionBox(box, navigation, peer, id, {}, current, [=]( + const MTPStarGiftCollection &result) { + done(qs(result.data().vtitle())); + }); } } // namespace Info::PeerGifts diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_collections.h b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_collections.h index a59a1cde0b..c60c32e312 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_collections.h +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_collections.h @@ -28,4 +28,12 @@ void NewCollectionBox( Data::SavedStarGiftId addId, Fn added); +void EditCollectionNameBox( + not_null box, + not_null navigation, + not_null peer, + int id, + QString current, + Fn done); + } // namespace Info::PeerGifts diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp index f2cd9454c2..80f4c66f9f 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/peer_gifts/info_peer_gifts_collections.h" #include "info/peer_gifts/info_peer_gifts_common.h" #include "info/info_controller.h" +#include "ui/boxes/confirm_box.h" #include "ui/controls/sub_tabs.h" #include "ui/layers/generic_box.h" #include "ui/text/text_utilities.h" @@ -173,9 +174,15 @@ private: void validateButtons(); void showGift(int index); void showMenuFor(not_null button, QPoint point); + void showMenuForCollection(int id); + void editCollectionName(int id); + void confirmDeleteCollection(int id); void refreshAbout(); void refreshCollectionsTabs(); + void collectionRenamed(int id, QString name); + void collectionRemoved(int id); + void markPinned(std::vector::iterator i); void markUnpinned(std::vector::iterator i); @@ -776,6 +783,60 @@ auto InnerWidget::pinnedSavedGifts() }; } +void InnerWidget::showMenuForCollection(int id) { + if (_menu || _addingToCollectionId) { + return; + } + _menu = base::make_unique_q(this, st::popupMenuWithIcons); + const auto addAction = Ui::Menu::CreateAddActionCallback(_menu); + addAction(tr::lng_gift_collection_add_title(tr::now), [=] { + editCollectionGifts(id); + }, &st::menuIconGiftPremium); + addAction(tr::lng_gift_collection_edit(tr::now), [=] { + editCollectionName(id); + }, &st::menuIconEdit); + addAction({ + .text = tr::lng_gift_collection_delete(tr::now), + .handler = [=] { confirmDeleteCollection(id); }, + .icon = &st::menuIconDeleteAttention, + .isAttention = true, + }); + _menu->popup(QCursor::pos()); +} + +void InnerWidget::editCollectionName(int id) { + const auto done = [=](QString name) { + collectionRenamed(id, name); + }; + const auto i = ranges::find(_collections, id, &Data::GiftCollection::id); + if (i == end(_collections)) { + return; + } + _window->uiShow()->show(Box( + EditCollectionNameBox, + _window, + peer(), + id, + i->title, + done)); +} + +void InnerWidget::confirmDeleteCollection(int id) { + const auto done = [=](Fn close) { + _window->session().api().request( + MTPpayments_DeleteStarGiftCollection(_peer->input, MTP_int(id)) + ).send(); + collectionRemoved(id); + close(); + }; + _window->uiShow()->show(Ui::MakeConfirmBox({ + .text = tr::lng_gift_collection_delete_sure(), + .confirmed = crl::guard(this, done), + .confirmText = tr::lng_gift_collection_delete_button(), + .confirmStyle = &st::attentionBoxButton, + })); +} + void InnerWidget::showMenuFor(not_null button, QPoint point) { if (_menu || _addingToCollectionId) { return; @@ -1126,12 +1187,57 @@ void InnerWidget::refreshCollectionsTabs() { _descriptorChanges.fire(std::move(now)); } }, _collectionsTabs->lifetime()); + + _collectionsTabs->contextMenuRequests( + ) | rpl::start_with_next([=](const QString &id) { + if (id == u"add"_q + || id == u"all"_q + || !_peer->canManageGifts()) { + return; + } + showMenuForCollection(id.toInt()); + }, _collectionsTabs->lifetime()); } else { _collectionsTabs->setTabs(std::move(tabs), context); } resizeToWidth(width()); } +void InnerWidget::collectionRenamed(int id, QString name) { + const auto i = ranges::find(_collections, id, &Data::GiftCollection::id); + if (i != end(_collections)) { + i->title = name; + refreshCollectionsTabs(); + } +} + +void InnerWidget::collectionRemoved(int id) { + auto now = _descriptor.current(); + if (now.collectionId == id) { + now.collectionId = 0; + _descriptorChanges.fire(std::move(now)); + } + Assert(_entries != &_perCollection[id]); + _perCollection.remove(id); + const auto removeFrom = [&](Entries &entries) { + for (auto &entry : entries.list) { + entry.gift.collectionIds.erase( + ranges::remove(entry.gift.collectionIds, id), + end(entry.gift.collectionIds)); + } + }; + removeFrom(_all); + for (auto &[_, entries] : _perCollection) { + removeFrom(entries); + } + + const auto i = ranges::find(_collections, id, &Data::GiftCollection::id); + if (i != end(_collections)) { + _collections.erase(i); + refreshCollectionsTabs(); + } +} + int InnerWidget::resizeGetHeight(int width) { const auto padding = st::giftBoxPadding; const auto count = int(_list->size()); diff --git a/Telegram/SourceFiles/ui/controls/sub_tabs.cpp b/Telegram/SourceFiles/ui/controls/sub_tabs.cpp index 41e6edafc0..4d8a4abc93 100644 --- a/Telegram/SourceFiles/ui/controls/sub_tabs.cpp +++ b/Telegram/SourceFiles/ui/controls/sub_tabs.cpp @@ -95,6 +95,10 @@ rpl::producer SubTabs::activated() const { return _activated.events(); } +rpl::producer SubTabs::contextMenuRequests() const { + return _contextMenuRequests.events(); +} + void SubTabs::setSelected(int index) { const auto was = (_selected >= 0); const auto now = (index >= 0); @@ -195,6 +199,12 @@ void SubTabs::mouseReleaseEvent(QMouseEvent *e) { } } +void SubTabs::contextMenuEvent(QContextMenuEvent *e) { + if (_selected >= 0 && _selected < _buttons.size()) { + _contextMenuRequests.fire_copy(_buttons[_selected].tab.id); + } +} + void SubTabs::paintEvent(QPaintEvent *e) { auto p = QPainter(this); auto hq = PainterHighQualityEnabler(p); diff --git a/Telegram/SourceFiles/ui/controls/sub_tabs.h b/Telegram/SourceFiles/ui/controls/sub_tabs.h index 3e98c23bc2..31c509b85d 100644 --- a/Telegram/SourceFiles/ui/controls/sub_tabs.h +++ b/Telegram/SourceFiles/ui/controls/sub_tabs.h @@ -43,6 +43,7 @@ public: void setActiveTab(const QString &id); [[nodiscard]] rpl::producer activated() const; + [[nodiscard]] rpl::producer contextMenuRequests() const; private: struct Button { @@ -57,6 +58,7 @@ private: void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; + void contextMenuEvent(QContextMenuEvent *e) override; void paintEvent(QPaintEvent *e) override; bool eventHook(QEvent *e) override; @@ -66,6 +68,7 @@ private: std::vector