From 2dfa58aae2d0e73fbbbee727b3bb31d96025e2a9 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 20 Nov 2024 07:44:36 +0300 Subject: [PATCH 01/39] Moved out some constant color indices to separated style file. --- .../boosts/create_giveaway_box.cpp | 6 +++--- .../boosts/giveaway/giveaway_type_row.cpp | 6 ++---- .../boosts/info_boosts_inner_widget.cpp | 4 ++-- .../info_statistics_list_controllers.cpp | 10 ++++------ Telegram/SourceFiles/ui/color_indices.style | 16 ++++++++++++++++ Telegram/cmake/td_ui.cmake | 1 + 6 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 Telegram/SourceFiles/ui/color_indices.style diff --git a/Telegram/SourceFiles/info/channel_statistics/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/channel_statistics/boosts/create_giveaway_box.cpp index b78da5c17..1fbf97797 100644 --- a/Telegram/SourceFiles/info/channel_statistics/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/channel_statistics/boosts/create_giveaway_box.cpp @@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/wrap/slide_wrap.h" #include "ui/ui_utility.h" +#include "styles/style_color_indices.h" #include "styles/style_credits.h" #include "styles/style_giveaway.h" #include "styles/style_info.h" @@ -59,7 +60,6 @@ namespace { constexpr auto kDoneTooltipDuration = 5 * crl::time(1000); constexpr auto kAdditionalPrizeLengthMax = 128; -constexpr auto kColorIndexCredits = int(1); [[nodiscard]] QDateTime ThreeDaysAfterToday() { auto dateNow = QDateTime::currentDateTime(); @@ -370,7 +370,7 @@ void CreateGiveawayBox( prepaid->credits ? GiveawayType::PrepaidCredits : GiveawayType::Prepaid, - prepaid->credits ? kColorIndexCredits : prepaid->id, + prepaid->credits ? st::colorIndexOrange : prepaid->id, tr::lng_boosts_prepaid_giveaway_single(), prepaid->credits ? tr::lng_boosts_prepaid_giveaway_credits_status( @@ -508,7 +508,7 @@ void CreateGiveawayBox( object_ptr( box, GiveawayType::Credits, - kColorIndexCredits, + st::colorIndexOrange, tr::lng_credits_summary_title(), tr::lng_giveaway_create_subtitle(), std::move(badge))); diff --git a/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway_type_row.cpp b/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway_type_row.cpp index b97184dba..60c1814ab 100644 --- a/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway_type_row.cpp +++ b/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway_type_row.cpp @@ -16,14 +16,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/checkbox.h" #include "styles/style_boxes.h" #include "styles/style_chat.h" +#include "styles/style_color_indices.h" #include "styles/style_giveaway.h" #include "styles/style_statistics.h" namespace Giveaway { -constexpr auto kColorIndexSpecific = int(4); -constexpr auto kColorIndexRandom = int(2); - GiveawayTypeRow::GiveawayTypeRow( not_null parent, Type type, @@ -32,7 +30,7 @@ GiveawayTypeRow::GiveawayTypeRow( : GiveawayTypeRow( parent, type, - (type == Type::SpecificUsers) ? kColorIndexSpecific : kColorIndexRandom, + (type == Type::SpecificUsers) ? st::colorIndexBlue : st::colorIndexGreen, (type == Type::SpecificUsers) ? tr::lng_giveaway_award_option() : (type == Type::Random) diff --git a/Telegram/SourceFiles/info/channel_statistics/boosts/info_boosts_inner_widget.cpp b/Telegram/SourceFiles/info/channel_statistics/boosts/info_boosts_inner_widget.cpp index e2436585c..5b6648220 100644 --- a/Telegram/SourceFiles/info/channel_statistics/boosts/info_boosts_inner_widget.cpp +++ b/Telegram/SourceFiles/info/channel_statistics/boosts/info_boosts_inner_widget.cpp @@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/slider_natural_width.h" #include "ui/wrap/slide_wrap.h" #include "ui/ui_utility.h" +#include "styles/style_color_indices.h" #include "styles/style_dialogs.h" // dialogsSearchTabs #include "styles/style_giveaway.h" #include "styles/style_info.h" @@ -339,7 +340,6 @@ void InnerWidget::fill() { Ui::AddSkip(inner); if (!status.prepaidGiveaway.empty()) { - constexpr auto kColorIndexCredits = int(1); const auto multiplier = Api::PremiumGiftCodeOptions(_peer) .giveawayBoostsPerPremium(); Ui::AddSkip(inner); @@ -352,7 +352,7 @@ void InnerWidget::fill() { g.credits ? GiveawayTypeRow::Type::PrepaidCredits : GiveawayTypeRow::Type::Prepaid, - g.credits ? kColorIndexCredits : g.id, + g.credits ? st::colorIndexOrange : g.id, g.credits ? tr::lng_boosts_prepaid_giveaway_single() : tr::lng_boosts_prepaid_giveaway_quantity( diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index abbd690c7..1a21f3fd5 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" #include "styles/style_boxes.h" +#include "styles/style_color_indices.h" #include "styles/style_credits.h" #include "styles/style_dialogs.h" // dialogsStoriesFull. #include "styles/style_layers.h" // boxRowPadding. @@ -47,9 +48,6 @@ namespace Info::Statistics { namespace { using BoostCallback = Fn; -constexpr auto kColorIndexCredits = int(1); -constexpr auto kColorIndexUnclaimed = int(3); -constexpr auto kColorIndexPending = int(4); [[nodiscard]] PeerListRowId UniqueRowIdFromEntry( const Data::CreditsHistoryEntry &entry) { @@ -475,10 +473,10 @@ BoostRow::BoostRow(const Data::Boost &boost) , _boost(boost) , _userpic( Ui::EmptyUserpic::UserpicColor(boost.credits - ? kColorIndexCredits + ? st::colorIndexOrange : boost.isUnclaimed - ? kColorIndexUnclaimed - : kColorIndexPending), + ? st::colorIndexSea + : st::colorIndexBlue), QString()) { init(); } diff --git a/Telegram/SourceFiles/ui/color_indices.style b/Telegram/SourceFiles/ui/color_indices.style new file mode 100644 index 000000000..3acd347ab --- /dev/null +++ b/Telegram/SourceFiles/ui/color_indices.style @@ -0,0 +1,16 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ + +colorIndexRed: 0; +colorIndexOrange: 1; +colorIndexGreen: 2; +colorIndexSea: 3; +colorIndexBlue: 4; +colorIndexPurple: 5; +colorIndexPink: 6; +colorIndexYellow: 7; diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 716d4d6b8..54d8bc92b 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -18,6 +18,7 @@ set(style_files ui/chat/chat.style ui/effects/credits.style ui/effects/premium.style + ui/color_indices.style boxes/boxes.style dialogs/dialogs.style chat_helpers/chat_helpers.style From e70f50d837287bdc4c59a70efcc9e2346a993872 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 20 Nov 2024 16:35:53 +0300 Subject: [PATCH 02/39] Changed engine of chats filters in forward box with restoring state. --- .../SourceFiles/window/window_peer_menu.cpp | 204 +++++------------- 1 file changed, 57 insertions(+), 147 deletions(-) diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 4a3abe2ea..b25df4709 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -2061,7 +2061,17 @@ QPointer ShowForwardMessagesBox( class ListBox final : public PeerListBox { public: - using PeerListBox::PeerListBox; + ListBox( + QWidget *parent, + std::unique_ptr controller, + Fn)> init) + : PeerListBox( + parent, + std::move(controller), + [=](not_null box) { + init(static_cast(box.get())); + }) { + } void setBottomSkip(int bottomSkip) { PeerListBox::setInnerBottomSkip(bottomSkip); @@ -2086,9 +2096,21 @@ QPointer ShowForwardMessagesBox( _forwardOptions = forwardOptions; } + not_null peerListContent() const { + return PeerListBox::content(); + } + + void setFilterId(FilterId filterId) { + _filterId = filterId; + } + [[nodiscard]] FilterId filterId() const { + return _filterId; + } + private: rpl::event_stream<> _focusRequests; Ui::ForwardOptions _forwardOptions; + FilterId _filterId = 0; }; @@ -2106,6 +2128,12 @@ QPointer ShowForwardMessagesBox( }) { } + std::unique_ptr createRestoredRow( + not_null peer) override final { + return ChooseRecipientBoxController::createRow( + peer->owner().history(peer)); + } + using PeerListController::setSearchNoResultsText; void rowClicked(not_null row) override final { @@ -2158,166 +2186,48 @@ QPointer ShowForwardMessagesBox( base::unique_qptr menu; }; - const auto applyFilter = [=](not_null box, FilterId id) { + const auto applyFilter = [=](not_null box, FilterId id) { box->scrollToY(0); auto &filters = session->data().chatsFilters(); const auto &list = filters.list(); if (list.size() <= 1) { return; } - const auto pinnedList = [&]( - not_null list, - bool foundSelf) { - const auto pinned = list->pinned()->order(); - auto peers = std::vector>(); - peers.reserve(pinned.size()); - for (const auto &pin : pinned) { - if (!foundSelf && pin.peer()->isSelf()) { - peers.insert(peers.begin(), pin.peer()); - foundSelf = true; - } else { - peers.push_back(pin.peer()); - } - } - if (!foundSelf) { - peers.insert(peers.begin(), session->user()); - } - return peers; - }; - const auto folder = session->data().folderLoaded( - Data::Folder::kId); - const auto pinned = pinnedList( - id - ? filters.chatsList(id) - : session->data().chatsList(nullptr), - !!id); - const auto pinnedInFolder = (!id && folder) - ? pinnedList(folder->chatsList(), true) - : std::vector>(); - box->peerListSortRows([&]( - const PeerListRow &r1, - const PeerListRow &r2) { - { // Pinned to top. - auto it1 = pinned.end(); - auto it2 = pinned.end(); - for (auto it = pinned.begin(); it != pinned.end(); ++it) { - if ((*it) == r1.peer()) { - it1 = it; - } - if ((*it) == r2.peer()) { - it2 = it; - } - if (it1 != pinned.end() && it2 != pinned.end()) { - break; - } - } - if (it1 == pinned.end() && it2 != pinned.end()) { - return false; - } else if (it2 == pinned.end() && it1 != pinned.end()) { - return true; - } else if (it1 != pinned.end() && it2 != pinned.end()) { - return it1 < it2; - } - } - { // Pinned to bottom. - const auto &indexed = session->data().contactsNoChatsList(); - auto it1 = indexed->end(); - auto it2 = indexed->end(); - for (auto it = indexed->begin(); it != indexed->end(); ++it) { - if (it->get()->key().peer() == r1.peer()) { - it1 = it; - } - if (it->get()->key().peer() == r2.peer()) { - it2 = it; - } - if (it1 != indexed->end() && it2 != indexed->end()) { - break; - } - } - if (it1 == indexed->end() && it2 != indexed->end()) { - return true; - } else if (it2 == indexed->end() && it1 != indexed->end()) { - return false; - } else if (it1 != indexed->end() && it2 != indexed->end()) { - return it1 > it2; - } - } - if (folder) { - const auto pinned1 = ranges::find(pinnedInFolder, r1.peer()); - const auto pinned2 = ranges::find(pinnedInFolder, r2.peer()); - const auto isPinned1 = pinned1 != pinnedInFolder.end(); - const auto isPinned2 = pinned2 != pinnedInFolder.end(); - if (isPinned1 && isPinned2) { - return pinned1 < pinned2; - } - - const auto &indexed = folder->chatsList()->indexed(); - auto it1 = indexed->end(); - auto it2 = indexed->end(); - for (auto it = indexed->begin(); it != indexed->end(); ++it) { - if (it->get()->key().peer() == r1.peer()) { - it1 = it; - } - if (it->get()->key().peer() == r2.peer()) { - it2 = it; - } - if (it1 != indexed->end() && it2 != indexed->end()) { - break; - } - } - const auto isFoldered1 = it1 != indexed->end(); - const auto isFoldered2 = it2 != indexed->end(); - if (isPinned1 && !isPinned2) { - return isFoldered2; - } - if (isPinned2 && !isPinned1) { - return !isFoldered1; - } - if (!isPinned1 && !isPinned2) { - if (!isFoldered1 && isFoldered2) { - return true; - } else if (!isFoldered2 && isFoldered1) { - return false; - } else if (isFoldered1 && isFoldered2) { - return it1 < it2; - } - } - } - const auto history1 = session->data().history(r1.peer()); - const auto history2 = session->data().history(r2.peer()); - const auto date1 = history1->lastMessage() - ? history1->lastMessage()->date() - : TimeId(0); - const auto date2 = history2->lastMessage() - ? history2->lastMessage()->date() - : TimeId(0); - return date1 > date2; - }); - const auto filter = ranges::find( - list, - id, - &Data::ChatFilter::id); - if (filter == list.end()) { + if (box->filterId() == id) { return; } - box->peerListPartitionRows([&](const PeerListRow &row) { - const auto rowPtr = const_cast(&row); - if (!filter->id()) { - box->peerListSetRowHidden(rowPtr, false); - } else { - const auto result = filter->contains( - session->data().history(row.peer())); - box->peerListSetRowHidden(rowPtr, !result); + box->setFilterId(id); + + using SavedState = PeerListController::SavedStateBase; + auto state = std::make_unique(); + state->controllerState = std::make_unique(); + + const auto addList = [&](auto chats) { + for (const auto &row : chats->all()) { + if (const auto history = row->history()) { + state->list.push_back(history->peer); + } } - return false; - }); - box->peerListRefreshRows(); + }; + + if (!id) { + state->list.push_back(session->user()); + addList(session->data().chatsList()->indexed()); + const auto folderId = Data::Folder::kId; + if (const auto folder = session->data().folderLoaded(folderId)) { + addList(folder->chatsList()->indexed()); + } + addList(session->data().contactsNoChatsList()); + } else { + addList(session->data().chatsFilters().chatsList(id)->indexed()); + } + box->peerListContent()->restoreState(std::move(state)); }; const auto state = [&] { auto controller = std::make_unique(session); const auto controllerRaw = controller.get(); - auto init = [=](not_null box) { + auto init = [=](not_null box) { controllerRaw->setSearchNoResultsText( tr::lng_bot_chats_not_found(tr::now)); box->setSpecialTabMode(true); From 9532a2e3dab815cbd2299a56731f61064a4f239f Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 21 Nov 2024 10:01:31 +0400 Subject: [PATCH 03/39] Fix emoji status set cancel. Fixes #28674. Fixes https://bugs.telegram.org/c/45691. --- Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index df1111869..52fda97bf 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -639,12 +639,13 @@ void ConfirmEmojiStatusBox( done(true); }); box->addButton(tr::lng_cancel(), [=] { - const auto was = *set; box->closeBox(); - if (!was) { + }); + box->boxClosing() | rpl::start_with_next([=] { + if (!*set) { done(false); } - }); + }, box->lifetime()); } class BotAction final : public Ui::Menu::ItemBase { From e3d9216b10ec21a445c6a185567e0b36dc431ebc Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 21 Nov 2024 11:41:46 +0400 Subject: [PATCH 04/39] Fix switch_inline_query with same_peer in topics. Fixes #27290. --- .../SourceFiles/history/history_widget.cpp | 32 ++++++------------- .../window/window_session_controller.cpp | 4 +++ 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 33eec2d34..e9d097a27 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -1947,24 +1947,12 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived( UserData *samePeerBot, MsgId samePeerReplyTo) { if (samePeerBot) { - if (_history) { - const auto textWithTags = TextWithTags{ - '@' + samePeerBot->username() + ' ' + query, - TextWithTags::Tags(), - }; - MessageCursor cursor = { - int(textWithTags.text.size()), - int(textWithTags.text.size()), - Ui::kQFixedMax, - }; - _history->setLocalDraft(std::make_unique( - textWithTags, - FullReplyTo(), - cursor, - Data::WebPageDraft())); - applyDraft(); - return true; + const auto to = controller()->currentDialogsEntryState(); + if (!to.key.owningHistory()) { + return false; } + controller()->switchInlineQuery(to, samePeerBot, query); + return true; } else if (const auto bot = _peer ? _peer->asUser() : nullptr) { const auto to = bot->isBot() ? bot->botInfo->inlineReturnTo @@ -2192,7 +2180,7 @@ void HistoryWidget::showHistory( _showAtMsgHighlightPart = {}; _showAtMsgHighlightPartOffsetHint = 0; - const auto wasDialogsEntryState = computeDialogsEntryState(); + const auto wasState = controller()->currentDialogsEntryState(); const auto startBot = (showAtMsgId == ShowAndStartBotMsgId); if (startBot) { showAtMsgId = ShowAtTheEndMsgId; @@ -2297,8 +2285,8 @@ void HistoryWidget::showHistory( if (const auto user = _peer->asUser()) { if (const auto &info = user->botInfo) { if (startBot) { - if (wasDialogsEntryState.key) { - info->inlineReturnTo = wasDialogsEntryState; + if (wasState.key) { + info->inlineReturnTo = wasState; } sendBotStartCommand(); _history->clearLocalDraft({}); @@ -2533,8 +2521,8 @@ void HistoryWidget::showHistory( if (const auto user = _peer->asUser()) { if (const auto &info = user->botInfo) { if (startBot) { - if (wasDialogsEntryState.key) { - info->inlineReturnTo = wasDialogsEntryState; + if (wasState.key) { + info->inlineReturnTo = wasState; } sendBotStartCommand(); } diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 912d663aa..afc99326b 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1866,6 +1866,10 @@ bool SessionController::switchInlineQuery( int(textWithTags.text.size()), Ui::kQFixedMax }; + if (to.currentReplyTo.messageId.msg == to.currentReplyTo.topicRootId + && to.currentReplyTo.quote.empty()) { + to.currentReplyTo.messageId.msg = MsgId(); + } auto draft = std::make_unique( textWithTags, to.currentReplyTo, From af78e4ea29a6ce9395847fe45ca9dd042a6e1596 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 21 Nov 2024 13:10:59 +0400 Subject: [PATCH 05/39] Show scheduled limit error. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/apiwrap.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 14cc25536..846e92e63 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -298,6 +298,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_error_nocopy_group" = "Sorry, copying from this group is disabled by admins."; "lng_error_nocopy_channel" = "Sorry, copying from this channel is disabled by admins."; "lng_error_nocopy_story" = "Sorry, the creator of this story disabled copying."; +"lng_error_schedule_limit" = "Sorry, you can't schedule more than 100 messages."; "lng_sure_add_admin_invite" = "This user is not a member of this group. Add them to the group and promote them to admin?"; "lng_sure_add_admin_invite_channel" = "This user is not a subscriber of this channel. Add them to the channel and promote them to admin?"; "lng_sure_add_admin_unremove" = "This user is currently restricted or removed. Are you sure you want to promote them?"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index d206261da..316aed080 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -559,6 +559,14 @@ void ApiWrap::sendMessageFail( : tr::lng_error_noforwards_group(tr::now), kJoinErrorDuration); } else if (error == u"PREMIUM_ACCOUNT_REQUIRED"_q) { Settings::ShowPremium(&session(), "premium_stickers"); + } else if (error == u"SCHEDULE_TOO_MUCH"_q) { + auto &scheduled = _session->scheduledMessages(); + if (const auto item = scheduled.lookupItem(peer->id, itemId.msg)) { + scheduled.removeSending(item); + } + if (show) { + show->showToast(tr::lng_error_schedule_limit(tr::now)); + } } if (const auto item = _session->data().message(itemId)) { Assert(randomId != 0); From 23464ac55f95f712612dccaed89c2f4b3a1eac8b Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 21 Nov 2024 13:13:06 +0400 Subject: [PATCH 06/39] Don't repeat premium effect on chat reopen. --- Telegram/SourceFiles/history/view/media/history_view_sticker.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp index 69e8bc0af..88e59cf95 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp @@ -250,7 +250,6 @@ DocumentData *Sticker::document() { void Sticker::stickerClearLoopPlayed() { _oncePlayed = false; - _premiumEffectPlayed = false; _premiumEffectSkipped = false; } From 9211e338e78f46ba2738f7521b894d97849c9014 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 21 Nov 2024 16:18:16 +0400 Subject: [PATCH 07/39] Fix tooltip render glitches on macOS. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index f48bc506d..64f99667a 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit f48bc506d1e24395b8245f663affaa72c782bd60 +Subproject commit 64f99667a6f45e9a896624c2d1c71fa143804aab From 81bea04db07b30ccfd1a9484b45c28e088f4a4cd Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 11 Nov 2024 09:45:26 +0300 Subject: [PATCH 08/39] Moved out peer bubble widget to separated file. --- Telegram/CMakeLists.txt | 2 + .../earn/info_channel_earn_list.cpp | 53 +------------- .../SourceFiles/ui/widgets/peer_bubble.cpp | 72 +++++++++++++++++++ Telegram/SourceFiles/ui/widgets/peer_bubble.h | 26 +++++++ 4 files changed, 103 insertions(+), 50 deletions(-) create mode 100644 Telegram/SourceFiles/ui/widgets/peer_bubble.cpp create mode 100644 Telegram/SourceFiles/ui/widgets/peer_bubble.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index f05dcb98e..bb41134c1 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1551,6 +1551,8 @@ PRIVATE ui/widgets/label_with_custom_emoji.h ui/widgets/chat_filters_tabs_strip.cpp ui/widgets/chat_filters_tabs_strip.h + ui/widgets/peer_bubble.cpp + ui/widgets/peer_bubble.h ui/countryinput.cpp ui/countryinput.h ui/dynamic_thumbnails.cpp diff --git a/Telegram/SourceFiles/info/channel_statistics/earn/info_channel_earn_list.cpp b/Telegram/SourceFiles/info/channel_statistics/earn/info_channel_earn_list.cpp index 3f98b5f23..3a2250f39 100644 --- a/Telegram/SourceFiles/info/channel_statistics/earn/info_channel_earn_list.cpp +++ b/Telegram/SourceFiles/info/channel_statistics/earn/info_channel_earn_list.cpp @@ -52,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/vertical_list.h" #include "ui/widgets/fields/input_field.h" #include "ui/widgets/label_with_custom_emoji.h" +#include "ui/widgets/peer_bubble.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/slider_natural_width.h" #include "ui/wrap/slide_wrap.h" @@ -1199,58 +1200,10 @@ void InnerWidget::fill() { AddRecipient(box, recipient); } if (isIn) { - const auto peerBubble = box->addRow( + box->addRow( object_ptr>( box, - object_ptr(box)))->entity(); - peerBubble->setAttribute( - Qt::WA_TransparentForMouseEvents); - const auto left = Ui::CreateChild( - peerBubble, - peer, - st::uploadUserpicButton); - const auto right = Ui::CreateChild( - peerBubble, - Info::Profile::NameValue(peer), - st::channelEarnSemiboldLabel); - rpl::combine( - left->sizeValue(), - right->sizeValue() - ) | rpl::start_with_next([=]( - const QSize &leftSize, - const QSize &rightSize) { - const auto padding = QMargins( - st::chatGiveawayPeerPadding.left() * 2, - st::chatGiveawayPeerPadding.top(), - st::chatGiveawayPeerPadding.right(), - st::chatGiveawayPeerPadding.bottom()); - peerBubble->resize( - leftSize.width() - + rightSize.width() - + rect::m::sum::h(padding), - leftSize.height()); - left->moveToLeft(0, 0); - right->moveToRight( - padding.right(), - padding.top()); - const auto maxRightSize = box->width() - - rect::m::sum::h(st::boxRowPadding) - - rect::m::sum::h(padding) - - leftSize.width(); - if (rightSize.width() > maxRightSize) { - right->resizeToWidth(maxRightSize); - } - }, peerBubble->lifetime()); - peerBubble->paintRequest( - ) | rpl::start_with_next([=] { - auto p = QPainter(peerBubble); - auto hq = PainterHighQualityEnabler(p); - p.setPen(Qt::NoPen); - p.setBrush(st::windowBgOver); - const auto rect = peerBubble->rect(); - const auto radius = rect.height() / 2; - p.drawRoundedRect(rect, radius, radius); - }, peerBubble->lifetime()); + Ui::CreatePeerBubble(box, peer))); } const auto closeBox = [=] { box->closeBox(); }; { diff --git a/Telegram/SourceFiles/ui/widgets/peer_bubble.cpp b/Telegram/SourceFiles/ui/widgets/peer_bubble.cpp new file mode 100644 index 000000000..87ac897d3 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/peer_bubble.cpp @@ -0,0 +1,72 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/widgets/peer_bubble.h" + +#include "data/data_peer.h" +#include "info/profile/info_profile_values.h" +#include "ui/controls/userpic_button.h" +#include "ui/painter.h" +#include "ui/rect.h" +#include "ui/widgets/labels.h" +#include "styles/style_boxes.h" +#include "styles/style_channel_earn.h" +#include "styles/style_chat.h" +#include "styles/style_layers.h" + +namespace Ui { + +object_ptr CreatePeerBubble( + not_null parent, + not_null peer) { + auto owned = object_ptr(parent); + const auto peerBubble = owned.data(); + peerBubble->setAttribute(Qt::WA_TransparentForMouseEvents); + const auto left = Ui::CreateChild( + peerBubble, + peer, + st::uploadUserpicButton); + const auto right = Ui::CreateChild( + peerBubble, + Info::Profile::NameValue(peer), + st::channelEarnSemiboldLabel); + const auto padding = st::chatGiveawayPeerPadding + + QMargins(st::chatGiveawayPeerPadding.left(), 0, 0, 0); + rpl::combine( + left->sizeValue(), + right->sizeValue() + ) | rpl::start_with_next([=]( + const QSize &leftSize, + const QSize &rightSize) { + peerBubble->resize( + leftSize.width() + rightSize.width() + rect::m::sum::h(padding), + leftSize.height()); + left->moveToLeft(0, 0); + right->moveToRight(padding.right() + st::lineWidth, padding.top()); + const auto maxRightSize = parent->width() + - rect::m::sum::h(st::boxRowPadding) + - rect::m::sum::h(padding) + - leftSize.width(); + if ((rightSize.width() > maxRightSize) && (maxRightSize > 0)) { + right->resizeToWidth(maxRightSize); + } + }, peerBubble->lifetime()); + peerBubble->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(peerBubble); + auto hq = PainterHighQualityEnabler(p); + p.setPen(Qt::NoPen); + p.setBrush(st::windowBgOver); + const auto rect = peerBubble->rect(); + const auto radius = rect.height() / 2; + p.drawRoundedRect(rect, radius, radius); + }, peerBubble->lifetime()); + + return owned; +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/peer_bubble.h b/Telegram/SourceFiles/ui/widgets/peer_bubble.h new file mode 100644 index 000000000..9c0b657d9 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/peer_bubble.h @@ -0,0 +1,26 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +template +class object_ptr; + +class PeerData; + +namespace Ui { +class RpWidget; +class VerticalLayout; +} // namespace Ui + +namespace Ui { + +[[nodiscard]] object_ptr CreatePeerBubble( + not_null parent, + not_null peer); + +} // namespace Ui From 03d9fb41151057a006b79ccc85cb1ea3605c05b6 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 11 Nov 2024 02:23:33 +0300 Subject: [PATCH 09/39] Added api support of subscription period in credits invoices. --- Telegram/SourceFiles/payments/payments_form.cpp | 3 +++ Telegram/SourceFiles/payments/payments_form.h | 1 + 2 files changed, 4 insertions(+) diff --git a/Telegram/SourceFiles/payments/payments_form.cpp b/Telegram/SourceFiles/payments/payments_form.cpp index 7941404c4..382226840 100644 --- a/Telegram/SourceFiles/payments/payments_form.cpp +++ b/Telegram/SourceFiles/payments/payments_form.cpp @@ -456,6 +456,8 @@ void Form::requestForm() { const auto amount = tlPrices.empty() ? 0 : tlPrices.front().data().vamount().v; + const auto subscriptionPeriod + = data.vinvoice().data().vsubscription_period().value_or(0); if (currency != ::Ui::kCreditsCurrency || !amount) { using Type = Error::Type; _updates.fire(Error{ Type::Form, u"Bad Stars Form."_q }); @@ -467,6 +469,7 @@ void Form::requestForm() { .credits = amount, .currency = currency, .amount = amount, + .subscriptionPeriod = subscriptionPeriod, }; const auto formData = CreditsFormData{ .id = _id, diff --git a/Telegram/SourceFiles/payments/payments_form.h b/Telegram/SourceFiles/payments/payments_form.h index 9b0d38d4d..d93fce51f 100644 --- a/Telegram/SourceFiles/payments/payments_form.h +++ b/Telegram/SourceFiles/payments/payments_form.h @@ -170,6 +170,7 @@ struct InvoiceCredits { uint64 amount = 0; bool extended = false; PeerId giftPeerId = PeerId(0); + int subscriptionPeriod = 0; }; struct InvoiceStarGift { From 09643aef8239019f9c7bd1baf74391a63d36c40e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 11 Nov 2024 02:23:19 +0300 Subject: [PATCH 10/39] Added support of credits subscriptions to send credits box. --- Telegram/Resources/langs/lang.strings | 4 + .../SourceFiles/boxes/send_credits_box.cpp | 150 ++++++++++++++++-- Telegram/SourceFiles/dialogs/dialogs_entry.h | 2 +- Telegram/SourceFiles/ui/effects/credits.style | 2 + 4 files changed, 148 insertions(+), 10 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 846e92e63..d0988628c 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2445,6 +2445,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_credits_box_out_media#other" = "Do you want to unlock {media} in {chat} for **{count} Stars**?"; "lng_credits_box_out_media_user#one" = "Do you want to unlock {media} from {user} for **{count} Star**?"; "lng_credits_box_out_media_user#other" = "Do you want to unlock {media} from {user} for **{count} Stars**?"; +"lng_credits_box_out_subscription#one" = "Do you want to subscribe to **{title}** in **{bot}** for **{count}** star per month?"; +"lng_credits_box_out_subscription#other" = "Do you want to subscribe to **{title}** in **{bot}** for **{count}** stars per month?"; +"lng_credits_box_out_subscription_confirm#one" = "Subscribe for {emoji} {count} / month"; +"lng_credits_box_out_subscription_confirm#other" = "Subscribe for {emoji} {count} / month"; "lng_credits_box_out_photo" = "a photo"; "lng_credits_box_out_photos#one" = "{count} photo"; "lng_credits_box_out_photos#other" = "{count} photos"; diff --git a/Telegram/SourceFiles/boxes/send_credits_box.cpp b/Telegram/SourceFiles/boxes/send_credits_box.cpp index 3ac13da20..d00a95f1f 100644 --- a/Telegram/SourceFiles/boxes/send_credits_box.cpp +++ b/Telegram/SourceFiles/boxes/send_credits_box.cpp @@ -26,17 +26,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_credits_graphics.h" #include "ui/boxes/confirm_box.h" #include "ui/controls/userpic_button.h" +#include "ui/effects/credits_graphics.h" #include "ui/effects/premium_graphics.h" #include "ui/effects/premium_top_bar.h" // Ui::Premium::ColorizedSvg. #include "ui/image/image_prepare.h" #include "ui/layers/generic_box.h" +#include "ui/painter.h" #include "ui/rect.h" #include "ui/text/text_utilities.h" #include "ui/vertical_list.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/peer_bubble.h" #include "styles/style_boxes.h" +#include "styles/style_chat.h" #include "styles/style_credits.h" #include "styles/style_giveaway.h" +#include "styles/style_info.h" // inviteLinkSubscribeBoxTerms #include "styles/style_layers.h" #include "styles/style_premium.h" #include "styles/style_settings.h" @@ -92,6 +97,44 @@ struct PaidMediaData { }; } +void AddTerms( + not_null box, + not_null button, + const style::Box &stBox) { + const auto terms = Ui::CreateChild( + button->parentWidget(), + tr::lng_channel_invite_subscription_terms( + lt_link, + rpl::combine( + tr::lng_paid_react_agree_link(), + tr::lng_group_invite_subscription_about_url() + ) | rpl::map([](const QString &text, const QString &url) { + return Ui::Text::Link(text, url); + }), + Ui::Text::RichLangValue), + st::inviteLinkSubscribeBoxTerms); + const auto &buttonPadding = stBox.buttonPadding; + const auto style = box->lifetime().make_state(style::Box{ + .buttonPadding = buttonPadding + QMargins(0, 0, 0, terms->height()), + .buttonHeight = stBox.buttonHeight, + .button = stBox.button, + .margin = stBox.margin, + .title = stBox.title, + .bg = stBox.bg, + .titleAdditionalFg = stBox.titleAdditionalFg, + .shadowIgnoreTopSkip = stBox.shadowIgnoreTopSkip, + .shadowIgnoreBottomSkip = stBox.shadowIgnoreBottomSkip, + }); + button->geometryValue() | rpl::start_with_next([=](const QRect &rect) { + terms->resizeToWidth(box->width() + - rect::m::sum::h(st::boxRowPadding)); + terms->moveToLeft( + rect.x() + (rect.width() - terms->width()) / 2, + rect::bottom(rect) + buttonPadding.bottom() / 2); + }, terms->lifetime()); + box->setStyle(*style); +} + [[nodiscard]] rpl::producer SendCreditsConfirmText( not_null session, not_null form) { @@ -150,6 +193,16 @@ struct PaidMediaData { } const auto bot = session->data().user(form->botId); + if (form->invoice.subscriptionPeriod) { + return tr::lng_credits_box_out_subscription( + lt_count, + rpl::single(form->invoice.amount) | tr::to_count(), + lt_title, + rpl::single(TextWithEntities{ form->title }), + lt_bot, + rpl::single(TextWithEntities{ bot->name() }), + Ui::Text::RichLangValue); + } return tr::lng_credits_box_out_sure( lt_count, rpl::single(form->invoice.amount) | tr::to_count(), @@ -190,6 +243,57 @@ struct PaidMediaData { st::defaultUserpicButton); } +[[nodiscard]] not_null SendCreditsBadge( + not_null parent, + int credits) { + const auto widget = Ui::CreateChild(parent); + const auto &font = st::chatGiveawayBadgeFont; + const auto text = QString::number(credits); + const auto iconHeight = font->ascent - font->descent; + const auto iconWidth = iconHeight + st::lineWidth; + const auto width = font->width(text) + iconWidth + st::lineWidth; + const auto inner = QRect(0, 0, width, font->height); + const auto rect = inner + st::subscriptionCreditsBadgePadding; + const auto size = rect.size(); + const auto svg = widget->lifetime().make_state( + Ui::Premium::Svg()); + const auto half = st::chatGiveawayBadgeStroke / 2.; + const auto left = st::subscriptionCreditsBadgePadding.left(); + const auto smaller = QRectF(rect.translated(-rect.topLeft())) + - Margins(half); + const auto radius = smaller.height() / 2.; + widget->resize(size); + + widget->paintRequest() | rpl::start_with_next([=] { + auto p = QPainter(widget); + auto hq = PainterHighQualityEnabler(p); + p.setPen(QPen(st::premiumButtonFg, st::chatGiveawayBadgeStroke * 1.)); + p.setBrush(st::creditsBg3); + p.drawRoundedRect(smaller, radius, radius); + + p.translate(0, font->descent / 2); + + p.setPen(st::premiumButtonFg); + p.setBrush(st::premiumButtonFg); + svg->render( + &p, + QRect( + left, + half + (inner.height() - iconHeight) / 2, + iconHeight, + iconHeight)); + + p.setFont(font); + p.drawText( + left + iconWidth, + st::subscriptionCreditsBadgePadding.top() + font->ascent, + text); + + }, widget->lifetime()); + + return widget; +} + } // namespace void SendCreditsBox( @@ -203,7 +307,8 @@ void SendCreditsBox( rpl::variable confirmButtonBusy = false; }; const auto state = box->lifetime().make_state(); - box->setStyle(st::giveawayGiftCodeBox); + const auto &stBox = st::giveawayGiftCodeBox; + box->setStyle(stBox); box->setNoContentMargin(true); const auto session = form->invoice.session; @@ -245,14 +350,36 @@ void SendCreditsBox( content, SendCreditsThumbnail(content, session, form.get(), photoSize))); thumb->setAttribute(Qt::WA_TransparentForMouseEvents); + if (form->invoice.subscriptionPeriod) { + const auto badge = SendCreditsBadge(content, form->invoice.amount); + thumb->geometryValue() | rpl::start_with_next([=](const QRect &r) { + badge->moveToLeft( + r.x() + (r.width() - badge->width()) / 2, + rect::bottom(r) - badge->height() / 2); + }, badge->lifetime()); + Ui::AddSkip(content); + Ui::AddSkip(content); + } Ui::AddSkip(content); box->addRow(object_ptr>( box, object_ptr( box, - tr::lng_credits_box_out_title(), + form->invoice.subscriptionPeriod + ? rpl::single(form->title) + : tr::lng_credits_box_out_title(), st::settingsPremiumUserTitle))); + if (form->invoice.subscriptionPeriod && form->botId && form->photo) { + Ui::AddSkip(content); + Ui::AddSkip(content); + const auto bot = session->data().user(form->botId); + box->addRow( + object_ptr>( + box, + Ui::CreatePeerBubble(box, bot))); + Ui::AddSkip(content); + } Ui::AddSkip(content); box->addRow(object_ptr>( box, @@ -306,6 +433,9 @@ void SendCreditsBox( } }).send(); }); + if (form->invoice.subscriptionPeriod) { + AddTerms(box, button, stBox); + } { using namespace Info::Statistics; const auto loadingAnimation = InfiniteRadialAnimationWidget( @@ -317,12 +447,14 @@ void SendCreditsBox( SetButtonMarkedLabel( button, rpl::combine( - tr::lng_credits_box_out_confirm( - lt_count, - rpl::single(form->invoice.amount) | tr::to_count(), - lt_emoji, - rpl::single(CreditsEmojiSmall(session)), - Ui::Text::RichLangValue), + (form->invoice.subscriptionPeriod + ? tr::lng_credits_box_out_subscription_confirm + : tr::lng_credits_box_out_confirm)( + lt_count, + rpl::single(form->invoice.amount) | tr::to_count(), + lt_emoji, + rpl::single(CreditsEmojiSmall(session)), + Ui::Text::RichLangValue), state->confirmButtonBusy.value() ) | rpl::map([](TextWithEntities &&text, bool busy) { return busy ? TextWithEntities() : std::move(text); @@ -332,7 +464,7 @@ void SendCreditsBox( box->getDelegate()->style().button.textFg->c); const auto buttonWidth = st::boxWidth - - rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding); + - rect::m::sum::h(stBox.buttonPadding); button->widthValue() | rpl::filter([=] { return (button->widthNoMargins() != buttonWidth); }) | rpl::start_with_next([=] { diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index 56e84f48c..3a19eec68 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -295,7 +295,7 @@ auto Entry::unreadStateChangeNotifier(bool required) { _flags |= Flag::InUnreadChangeBlock; const auto notify = required && inChatList(); const auto wasState = notify ? chatListUnreadState() : UnreadState(); - return gsl::finally([=] { + return gsl::finally([=, this] { _flags &= ~Flag::InUnreadChangeBlock; if (notify) { Assert(inChatList()); diff --git a/Telegram/SourceFiles/ui/effects/credits.style b/Telegram/SourceFiles/ui/effects/credits.style index 734b2a2f0..1b82fe030 100644 --- a/Telegram/SourceFiles/ui/effects/credits.style +++ b/Telegram/SourceFiles/ui/effects/credits.style @@ -156,3 +156,5 @@ giftListAbout: FlatLabel(defaultFlatLabel) { giftListAboutMargin: margins(12px, 24px, 12px, 24px); giftBoxEmojiToggleTop: 7px; giftBoxLimitTop: 28px; + +subscriptionCreditsBadgePadding: margins(10px, 1px, 8px, 3px); From 1c64e905376bb9926d9ceb5226d2321695fa487c Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 12 Nov 2024 19:18:33 +0300 Subject: [PATCH 11/39] Split creation of button for pagination in statistics lists. --- .../info_statistics_list_controllers.cpp | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index 1a21f3fd5..658059f0b 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -68,6 +68,17 @@ void AddSubtitle( { 0, -subtitlePadding.top(), 0, -subtitlePadding.bottom() }); } +[[nodiscard]] object_ptr CreateShowMoreButton( + not_null parent, + rpl::producer title) { + auto owned = object_ptr( + parent, + std::move(title), + st::statisticsShowMoreButton); + Ui::AddToggleUpDownArrowToMoreButton(owned.data()); + return owned; +} + [[nodiscard]] QString FormatText( int value1, tr::phrase phrase1, int value2, tr::phrase phrase2, @@ -1256,9 +1267,10 @@ void AddCreditsHistoryList( object_ptr(container, &state->controller))); state->controller.setDelegate(&state->delegate); - const auto wrap = AddShowMoreButton( - container, - tr::lng_stories_show_more()); + const auto wrap = container->add( + object_ptr>( + container, + CreateShowMoreButton(container, tr::lng_stories_show_more()))); const auto showMore = [=] { if (!state->controller.skipRequest()) { @@ -1275,16 +1287,11 @@ void AddCreditsHistoryList( not_null*> AddShowMoreButton( not_null container, rpl::producer title) { - const auto wrap = container->add( + return container->add( object_ptr>( container, - object_ptr( - container, - std::move(title), - st::statisticsShowMoreButton)), + CreateShowMoreButton(container, std::move(title))), { 0, -st::settingsButton.padding.top(), 0, 0 }); - Ui::AddToggleUpDownArrowToMoreButton(wrap->entity()); - return wrap; } } // namespace Info::Statistics From 36fa455aad2a9f2e432ebc4b4cd843e01c7b94a1 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 12 Nov 2024 23:19:16 +0300 Subject: [PATCH 12/39] Added third line to entries of list for credits history. --- .../info_statistics_list_controllers.cpp | 80 ++++++++++++------- Telegram/SourceFiles/ui/effects/credits.style | 15 ++++ 2 files changed, 68 insertions(+), 27 deletions(-) diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index 658059f0b..67fe36ed5 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -772,6 +772,15 @@ public: bool selected, bool actionSelected) override; + void paintStatusText( + Painter &p, + const style::PeerListItem &st, + int x, + int y, + int available, + int outer, + bool selected) override; + private: void init(); @@ -785,6 +794,7 @@ private: QString _title; QString _name; + Ui::Text::String _description; Ui::Text::String _rightText; base::has_weak_ptr _guard; @@ -831,33 +841,31 @@ void CreditsRow::init() { const auto name = !isSpecial ? PeerListRow::generateName() : Ui::GenerateEntryName(_entry).text; - _name = (_entry.reaction || _entry.stargift || _entry.bareGiveawayMsgId) - ? Ui::GenerateEntryName(_entry).text - : _entry.title.isEmpty() - ? name - : _entry.title; - const auto joiner = QString(QChar(' ')) + QChar(8212) + QChar(' '); + _name = _entry.title.isEmpty() ? name : _entry.title; setSkipPeerBadge(true); - PeerListRow::setCustomStatus( - langDateTime(_entry.date) - + (_entry.floodSkip - ? (joiner + tr::lng_credits_box_history_entry_floodskip_about( - tr::now, - lt_count_decimal, - _entry.floodSkip)) - : _entry.refunded - ? (joiner + tr::lng_channel_earn_history_return(tr::now)) - : _entry.pending - ? (joiner + tr::lng_channel_earn_history_pending(tr::now)) - : _entry.failed - ? (joiner + tr::lng_channel_earn_history_failed(tr::now)) - : !_entry.subscriptionUntil.isNull() - ? (joiner - + tr::lng_credits_box_history_entry_subscription(tr::now)) - : QString()) - + ((_entry.gift && isSpecial) - ? (joiner + tr::lng_credits_box_history_entry_anonymous(tr::now)) - : ((_name == name) ? QString() : (joiner + name)))); + const auto description = _entry.floodSkip + ? tr::lng_credits_box_history_entry_floodskip_about( + tr::now, + lt_count_decimal, + _entry.floodSkip) + : _entry.refunded + ? tr::lng_channel_earn_history_return(tr::now) + : _entry.pending + ? tr::lng_channel_earn_history_pending(tr::now) + : _entry.failed + ? tr::lng_channel_earn_history_failed(tr::now) + : !_entry.subscriptionUntil.isNull() + ? tr::lng_credits_box_history_entry_subscription(tr::now) + : (_entry.peerType + == Data::CreditsHistoryEntry::PeerType::PremiumBot) + ? tr::lng_credits_box_history_entry_via_premium_bot(tr::now) + : (_entry.gift && isSpecial) + ? tr::lng_credits_box_history_entry_anonymous(tr::now) + : (_name == name) + ? Ui::GenerateEntryName(_entry).text + : name; + _description.setText(st::defaultTextStyle, description); + PeerListRow::setCustomStatus(langDateTime(_entry.date)); if (_subscription) { PeerListRow::setCustomStatus((_subscription.expired ? tr::lng_credits_subscription_status_none @@ -978,6 +986,24 @@ void CreditsRow::rightActionPaint( }); } +void CreditsRow::paintStatusText( + Painter &p, + const style::PeerListItem &st, + int x, + int y, + int available, + int outer, + bool selected) { + PeerListRow::paintStatusText(p, st, x, y, available, outer, selected); + p.setPen(st.nameFg); + _description.draw(p, { + .position = QPoint(x, y - _description.minHeight() - st::lineWidth), + .outerWidth = outer, + .availableWidth = available, + .elisionLines = 1, + }); +} + class CreditsController final : public PeerListController { public: explicit CreditsController(CreditsDescriptor d); @@ -1019,7 +1045,7 @@ CreditsController::CreditsController(CreditsDescriptor d) .session = _session, .customEmojiRepaint = [] {}, }) { - PeerListController::setStyleOverrides(&st::boostsListBox); + PeerListController::setStyleOverrides(&st::creditsHistoryEntriesList); } Main::Session &CreditsController::session() const { diff --git a/Telegram/SourceFiles/ui/effects/credits.style b/Telegram/SourceFiles/ui/effects/credits.style index 1b82fe030..84008e849 100644 --- a/Telegram/SourceFiles/ui/effects/credits.style +++ b/Telegram/SourceFiles/ui/effects/credits.style @@ -157,4 +157,19 @@ giftListAboutMargin: margins(12px, 24px, 12px, 24px); giftBoxEmojiToggleTop: 7px; giftBoxLimitTop: 28px; +creditsHistoryEntriesList: PeerList(defaultPeerList) { + padding: margins( + 0px, + 7px, + 0px, + 7px); + item: PeerListItem(defaultPeerListItem) { + height: 66px; + photoPosition: point(18px, 6px); + namePosition: point(70px, 6px); + statusPosition: point(70px, 43px); + photoSize: 42px; + } +} + subscriptionCreditsBadgePadding: margins(10px, 1px, 8px, 3px); From 3042fb7299b8c7b9f27ef670e4b749b3134695a5 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 12 Nov 2024 20:08:01 +0300 Subject: [PATCH 13/39] Added initial very limited implementation of peer list with widgets. --- Telegram/CMakeLists.txt | 2 + .../SourceFiles/boxes/peer_list_widgets.cpp | 389 ++++++++++++++++++ .../SourceFiles/boxes/peer_list_widgets.h | 110 +++++ 3 files changed, 501 insertions(+) create mode 100644 Telegram/SourceFiles/boxes/peer_list_widgets.cpp create mode 100644 Telegram/SourceFiles/boxes/peer_list_widgets.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index bb41134c1..f631e837b 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -294,6 +294,8 @@ PRIVATE boxes/peer_list_box.h boxes/peer_list_controllers.cpp boxes/peer_list_controllers.h + boxes/peer_list_widgets.cpp + boxes/peer_list_widgets.h boxes/peer_lists_box.cpp boxes/peer_lists_box.h boxes/passcode_box.cpp diff --git a/Telegram/SourceFiles/boxes/peer_list_widgets.cpp b/Telegram/SourceFiles/boxes/peer_list_widgets.cpp new file mode 100644 index 000000000..3a2b8ca37 --- /dev/null +++ b/Telegram/SourceFiles/boxes/peer_list_widgets.cpp @@ -0,0 +1,389 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "boxes/peer_list_widgets.h" + +#include "ui/painter.h" +#include "ui/widgets/buttons.h" +#include "ui/wrap/vertical_layout.h" +#include "styles/style_boxes.h" + +namespace { + +using State = std::unique_ptr; + +} // namespace + +PeerListWidgets::PeerListWidgets( + not_null parent, + not_null controller) +: Ui::RpWidget(parent) +, _controller(controller) +, _st(controller->computeListSt()) { + _content = base::make_unique_q(this); + parent->sizeValue() | rpl::start_with_next([this](const QSize &size) { + _content->resizeToWidth(size.width()); + resize(size.width(), _content->height()); + }, lifetime()); +} + +crl::time PeerListWidgets::paintRow( + Painter &p, + crl::time now, + bool selected, + not_null row) { + const auto &st = row->computeSt(_st.item); + + row->lazyInitialize(st); + const auto outerWidth = _content->width(); + const auto w = outerWidth; + + auto refreshStatusAt = row->refreshStatusTime(); + if (refreshStatusAt > 0 && now >= refreshStatusAt) { + row->refreshStatus(); + refreshStatusAt = row->refreshStatusTime(); + } + const auto refreshStatusIn = (refreshStatusAt > 0) + ? std::max(refreshStatusAt - now, crl::time(1)) + : 0; + + const auto peer = row->special() ? nullptr : row->peer().get(); + row->paintUserpic( + p, + st, + st.photoPosition.x(), + st.photoPosition.y(), + outerWidth); + + p.setPen(st::contactsNameFg); + + const auto skipRight = st.photoPosition.x(); + const auto rightActionSize = row->rightActionSize(); + const auto rightActionMargins = rightActionSize.isEmpty() + ? QMargins() + : row->rightActionMargins(); + const auto &name = row->name(); + const auto namePosition = st.namePosition; + const auto namex = namePosition.x(); + const auto namey = namePosition.y(); + auto namew = outerWidth - namex - skipRight; + if (!rightActionSize.isEmpty() + && (namey < rightActionMargins.top() + rightActionSize.height()) + && (namey + st.nameStyle.font->height + > rightActionMargins.top())) { + namew -= rightActionMargins.left() + + rightActionSize.width() + + rightActionMargins.right() + - skipRight; + } + const auto statusx = st.statusPosition.x(); + const auto statusy = st.statusPosition.y(); + auto statusw = outerWidth - statusx - skipRight; + if (!rightActionSize.isEmpty() + && (statusy < rightActionMargins.top() + rightActionSize.height()) + && (statusy + st::contactsStatusFont->height + > rightActionMargins.top())) { + statusw -= rightActionMargins.left() + + rightActionSize.width() + + rightActionMargins.right() + - skipRight; + } + namew -= row->paintNameIconGetWidth( + p, + [=] { updateRow(row); }, + now, + namex, + namey, + name.maxWidth(), + namew, + w, + selected); + auto nameCheckedRatio = row->disabled() ? 0. : row->checkedRatio(); + p.setPen(anim::pen(st.nameFg, st.nameFgChecked, nameCheckedRatio)); + name.drawLeftElided(p, namex, namey, namew, w); + + p.setFont(st::contactsStatusFont); + row->paintStatusText(p, st, statusx, statusy, statusw, w, selected); + + row->elementsPaint(p, outerWidth, selected, 0); + + return refreshStatusIn; +} + +void PeerListWidgets::appendRow(std::unique_ptr row) { + Expects(row != nullptr); + + if (_rowsById.find(row->id()) == _rowsById.cend()) { + const auto raw = row.get(); + const auto &st = raw->computeSt(_st.item); + raw->setAbsoluteIndex(_rows.size()); + _rows.push_back(std::move(row)); + + const auto widget = _content->add( + object_ptr::fromRaw( + Ui::CreateSimpleSettingsButton( + _content.get(), + st.button.ripple, + st.button.textBgOver))); + widget->resize(widget->width(), st.height); + widget->paintRequest() | rpl::start_with_next([=, this] { + auto p = Painter(widget); + const auto selected = widget->isOver() || widget->isDown(); + paintRow(p, crl::now(), selected, raw); + }, widget->lifetime()); + + widget->setClickedCallback([this, raw] { + _controller->rowClicked(raw); + }); + } +} + +PeerListRow *PeerListWidgets::findRow(PeerListRowId id) { + const auto it = _rowsById.find(id); + return (it == _rowsById.cend()) ? nullptr : it->second.get(); +} + +void PeerListWidgets::updateRow(not_null row) { + const auto it = ranges::find_if( + _rows, + [row](const auto &r) { return r.get() == row; }); + if (it != _rows.end()) { + const auto index = std::distance(_rows.begin(), it); + if (const auto widget = _content->widgetAt(index)) { + widget->update(); + } + } +} + +int PeerListWidgets::fullRowsCount() { + return _rows.size(); +} + +[[nodiscard]] not_null PeerListWidgets::rowAt(int index) { + Expects(index >= 0 && index < _rows.size()); + + return _rows[index].get(); +} + +void PeerListWidgets::refreshRows() { + _content->resizeToWidth(width()); + resize(width(), _content->height()); +} + +void PeerListWidgetsDelegate::setContent(PeerListWidgets *content) { + _content = content; +} + +void PeerListWidgetsDelegate::setUiShow( + std::shared_ptr uiShow) { + _uiShow = std::move(uiShow); +} + +void PeerListWidgetsDelegate::peerListSetHideEmpty(bool hide) { + Unexpected("...PeerListWidgetsDelegate::peerListSetHideEmpty"); +} + +void PeerListWidgetsDelegate::peerListAppendRow( + std::unique_ptr row) { + _content->appendRow(std::move(row)); +} + +void PeerListWidgetsDelegate::peerListAppendSearchRow( + std::unique_ptr row) { + Unexpected("...PeerListWidgetsDelegate::peerListAppendSearchRow"); +} + +void PeerListWidgetsDelegate::peerListAppendFoundRow( + not_null row) { + Unexpected("...PeerListWidgetsDelegate::peerListAppendFoundRow"); +} + +void PeerListWidgetsDelegate::peerListPrependRow( + std::unique_ptr row) { + Unexpected("...PeerListWidgetsDelegate::peerListPrependRow"); +} + +void PeerListWidgetsDelegate::peerListPrependRowFromSearchResult( + not_null row) { + Unexpected( + "...PeerListWidgetsDelegate::peerListPrependRowFromSearchResult"); +} + +PeerListRow* PeerListWidgetsDelegate::peerListFindRow(PeerListRowId id) { + return _content->findRow(id); +} + +auto PeerListWidgetsDelegate::peerListLastRowMousePosition() +-> std::optional { + Unexpected("...PeerListWidgetsDelegate::peerListLastRowMousePosition"); +} + +void PeerListWidgetsDelegate::peerListUpdateRow(not_null row) { + _content->updateRow(row); +} + +void PeerListWidgetsDelegate::peerListRemoveRow(not_null row) { + Unexpected("...PeerListWidgetsDelegate::peerListRemoveRow"); +} + +void PeerListWidgetsDelegate::peerListConvertRowToSearchResult( + not_null row) { + Unexpected( + "...PeerListWidgetsDelegate::peerListConvertRowToSearchResult"); +} + +void PeerListWidgetsDelegate::peerListSetRowChecked( + not_null row, + bool checked) { + Unexpected("...PeerListWidgetsDelegate::peerListSetRowChecked"); +} + +void PeerListWidgetsDelegate::peerListSetRowHidden( + not_null row, + bool hidden) { + Unexpected("...PeerListWidgetsDelegate::peerListSetRowHidden"); +} + +void PeerListWidgetsDelegate::peerListSetForeignRowChecked( + not_null row, + bool checked, + anim::type animated) { +} + +int PeerListWidgetsDelegate::peerListFullRowsCount() { + return _content->fullRowsCount(); +} + +not_null PeerListWidgetsDelegate::peerListRowAt(int index) { + return _content->rowAt(index); +} + +int PeerListWidgetsDelegate::peerListSearchRowsCount() { + Unexpected("...PeerListWidgetsDelegate::peerListSearchRowsCount"); +} + +not_null PeerListWidgetsDelegate::peerListSearchRowAt(int) { + Unexpected("...PeerListWidgetsDelegate::peerListSearchRowAt"); +} + +void PeerListWidgetsDelegate::peerListRefreshRows() { + _content->refreshRows(); +} + +void PeerListWidgetsDelegate::peerListSetDescription( + object_ptr) { + Unexpected("...PeerListWidgetsDelegate::peerListSetDescription"); +} + +void PeerListWidgetsDelegate::peerListSetSearchNoResults( + object_ptr) { + Unexpected("...PeerListWidgetsDelegate::peerListSetSearchNoResults"); +} + +void PeerListWidgetsDelegate::peerListSetAboveWidget( + object_ptr) { + Unexpected("...PeerListWidgetsDelegate::peerListSetAboveWidget"); +} + +void PeerListWidgetsDelegate::peerListSetAboveSearchWidget( + object_ptr) { + Unexpected("...PeerListWidgetsDelegate::peerListSetAboveSearchWidget"); +} + +void PeerListWidgetsDelegate::peerListSetBelowWidget( + object_ptr) { + Unexpected("...PeerListWidgetsDelegate::peerListSetBelowWidget"); +} + +void PeerListWidgetsDelegate::peerListSetSearchMode(PeerListSearchMode mode) { + Unexpected("...PeerListWidgetsDelegate::peerListSetSearchMode"); +} + +void PeerListWidgetsDelegate::peerListMouseLeftGeometry() { + Unexpected("...PeerListWidgetsDelegate::peerListMouseLeftGeometry"); +} + +void PeerListWidgetsDelegate::peerListSortRows( + Fn) { + Unexpected("...PeerListWidgetsDelegate::peerListSortRows"); +} + +int PeerListWidgetsDelegate::peerListPartitionRows( + Fn border) { + Unexpected("...PeerListWidgetsDelegate::peerListPartitionRows"); +} + +State PeerListWidgetsDelegate::peerListSaveState() const { + Unexpected("...PeerListWidgetsDelegate::peerListSaveState"); + return nullptr; +} + +void PeerListWidgetsDelegate::peerListRestoreState( + std::unique_ptr state) { + Unexpected("...PeerListWidgetsDelegate::peerListRestoreState"); +} + +void PeerListWidgetsDelegate::peerListShowRowMenu( + not_null row, + bool highlightRow, + Fn)> destroyed) { +} + +void PeerListWidgetsDelegate::peerListSelectSkip(int direction) { + // _content->selectSkip(direction); +} + +void PeerListWidgetsDelegate::peerListPressLeftToContextMenu(bool shown) { + Unexpected("...PeerListWidgetsDelegate::peerListPressLeftToContextMenu"); +} + +bool PeerListWidgetsDelegate::peerListTrackRowPressFromGlobal(QPoint) { + return false; +} + +std::shared_ptr PeerListWidgetsDelegate::peerListUiShow() { + Expects(_uiShow != nullptr); + return _uiShow; +} + +void PeerListWidgetsDelegate::peerListAddSelectedPeerInBunch( + not_null peer) { + Unexpected("...PeerListWidgetsDelegate::peerListAddSelectedPeerInBunch"); +} + +void PeerListWidgetsDelegate::peerListAddSelectedRowInBunch( + not_null row) { + Unexpected("...PeerListWidgetsDelegate::peerListAddSelectedRowInBunch"); +} + +void PeerListWidgetsDelegate::peerListFinishSelectedRowsBunch() { + Unexpected("...PeerListWidgetsDelegate::peerListFinishSelectedRowsBunch"); +} + +void PeerListWidgetsDelegate::peerListSetTitle(rpl::producer title) { + Unexpected("...PeerListWidgetsDelegate::peerListSetTitle"); +} + +void PeerListWidgetsDelegate::peerListSetAdditionalTitle( + rpl::producer title) { + Unexpected("...PeerListWidgetsDelegate::peerListSetAdditionalTitle"); +} + +bool PeerListWidgetsDelegate::peerListIsRowChecked( + not_null row) { + Unexpected("...PeerListWidgetsDelegate::peerListIsRowChecked"); + return false; +} + +void PeerListWidgetsDelegate::peerListScrollToTop() { + Unexpected("...PeerListWidgetsDelegate::peerListScrollToTop"); +} + +int PeerListWidgetsDelegate::peerListSelectedRowsCount() { + Unexpected("...PeerListWidgetsDelegate::peerListSelectedRowsCount"); + return 0; +} diff --git a/Telegram/SourceFiles/boxes/peer_list_widgets.h b/Telegram/SourceFiles/boxes/peer_list_widgets.h new file mode 100644 index 000000000..d139487bc --- /dev/null +++ b/Telegram/SourceFiles/boxes/peer_list_widgets.h @@ -0,0 +1,110 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "boxes/peer_list_box.h" + +namespace Ui { +class VerticalLayout; +} // namespace Ui + +class PeerListWidgets : public Ui::RpWidget { +public: + PeerListWidgets( + not_null parent, + not_null controller); + + crl::time paintRow( + Painter &p, + crl::time now, + bool selected, + not_null row); + void appendRow(std::unique_ptr row); + PeerListRow* findRow(PeerListRowId id); + void updateRow(not_null row); + int fullRowsCount(); + [[nodiscard]] not_null rowAt(int index); + void refreshRows(); + +private: + const not_null _controller; + const style::PeerList &_st; + base::unique_qptr _content; + + std::vector> _rows; + std::map> _rowsById; + std::map>> _rowsByPeer; +}; + +class PeerListWidgetsDelegate : public PeerListDelegate { +public: + void setContent(PeerListWidgets *content); + void setUiShow(std::shared_ptr uiShow); + + void peerListSetHideEmpty(bool hide) override; + void peerListAppendRow(std::unique_ptr row) override; + void peerListAppendSearchRow(std::unique_ptr row) override; + void peerListAppendFoundRow(not_null row) override; + void peerListPrependRow(std::unique_ptr row) override; + void peerListPrependRowFromSearchResult( + not_null row) override; + PeerListRow *peerListFindRow(PeerListRowId id) override; + std::optional peerListLastRowMousePosition() override; + void peerListUpdateRow(not_null row) override; + void peerListRemoveRow(not_null row) override; + void peerListConvertRowToSearchResult( + not_null row) override; + void peerListSetRowChecked( + not_null row, + bool checked) override; + void peerListSetRowHidden( + not_null row, + bool hidden) override; + void peerListSetForeignRowChecked( + not_null row, + bool checked, + anim::type animated) override; + int peerListFullRowsCount() override; + not_null peerListRowAt(int index) override; + int peerListSearchRowsCount() override; + not_null peerListSearchRowAt(int index) override; + void peerListRefreshRows() override; + void peerListSetDescription(object_ptr) override; + void peerListSetSearchNoResults(object_ptr) override; + void peerListSetAboveWidget(object_ptr) override; + void peerListSetAboveSearchWidget(object_ptr) override; + void peerListSetBelowWidget(object_ptr) override; + void peerListSetSearchMode(PeerListSearchMode mode) override; + void peerListMouseLeftGeometry() override; + void peerListSortRows( + Fn) override; + int peerListPartitionRows(Fn border) override; + std::unique_ptr peerListSaveState() const override; + void peerListRestoreState(std::unique_ptr state) override; + void peerListShowRowMenu( + not_null row, + bool highlightRow, + Fn)> destroyed = nullptr) override; + void peerListSelectSkip(int direction) override; + void peerListPressLeftToContextMenu(bool shown) override; + bool peerListTrackRowPressFromGlobal(QPoint globalPosition) override; + std::shared_ptr peerListUiShow() override; + void peerListAddSelectedPeerInBunch(not_null peer) override; + void peerListAddSelectedRowInBunch(not_null row) override; + void peerListFinishSelectedRowsBunch() override; + void peerListSetTitle(rpl::producer title) override; + void peerListSetAdditionalTitle(rpl::producer title) override; + bool peerListIsRowChecked(not_null row) override; + void peerListScrollToTop() override; + int peerListSelectedRowsCount() override; + +private: + PeerListWidgets *_content = nullptr; + std::shared_ptr _uiShow; + +}; From 479e369d2962ee3528bed02615896e9d00229a53 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 12 Nov 2024 23:26:31 +0300 Subject: [PATCH 14/39] Replaced list of credits subscriptions with peer list with widgets. --- .../info_statistics_list_controllers.cpp | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index 67fe36ed5..c01e7a9a2 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_credits.h" #include "api/api_statistics.h" #include "boxes/peer_list_controllers.h" +#include "boxes/peer_list_widgets.h" #include "chat_helpers/stickers_gift_box_pack.h" #include "core/ui_integration.h" // Core::MarkedTextContext. #include "data/data_channel.h" @@ -1274,29 +1275,36 @@ void AddCreditsHistoryList( not_null bot, bool in, bool out, - bool subscription) { + bool subs) { struct State final { - State( - CreditsDescriptor d, - std::shared_ptr show) - : delegate(std::move(show)) - , controller(std::move(d)) { + State(CreditsDescriptor d) : controller(std::move(d)) { } - PeerListContentDelegateShow delegate; + std::optional creditsDelegate; + std::optional subscriptionDelegate; CreditsController controller; }; const auto state = container->lifetime().make_state( - CreditsDescriptor{ firstSlice, callback, bot, in, out, subscription }, - show); - - state->delegate.setContent(container->add( - object_ptr(container, &state->controller))); - state->controller.setDelegate(&state->delegate); + CreditsDescriptor{ firstSlice, callback, bot, in, out, subs }); + if (subs) { + state->subscriptionDelegate.emplace(); + state->subscriptionDelegate->setUiShow(show); + state->subscriptionDelegate->setContent(container->add( + object_ptr(container, &state->controller))); + state->controller.setDelegate(&(*state->subscriptionDelegate)); + } else { + state->creditsDelegate.emplace(show); + state->creditsDelegate->setContent(container->add( + object_ptr(container, &state->controller))); + state->controller.setDelegate(&(*state->creditsDelegate)); + } const auto wrap = container->add( object_ptr>( container, - CreateShowMoreButton(container, tr::lng_stories_show_more()))); + CreateShowMoreButton(container, tr::lng_stories_show_more())), + subs + ? QMargins() + : QMargins(0, -st::settingsButton.padding.top(), 0, 0)); const auto showMore = [=] { if (!state->controller.skipRequest()) { From 17e54104a92b05da3c3893100bb1ce7ecae35ffd Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 13 Nov 2024 01:23:54 +0300 Subject: [PATCH 15/39] Added api support of subscription details. --- Telegram/SourceFiles/api/api_credits.cpp | 13 +++++++++++-- Telegram/SourceFiles/data/data_subscriptions.h | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/api/api_credits.cpp b/Telegram/SourceFiles/api/api_credits.cpp index 80e995219..79560458e 100644 --- a/Telegram/SourceFiles/api/api_credits.cpp +++ b/Telegram/SourceFiles/api/api_credits.cpp @@ -133,17 +133,26 @@ constexpr auto kTransactionsLimit = 100; } [[nodiscard]] Data::SubscriptionEntry SubscriptionFromTL( - const MTPStarsSubscription &tl) { + const MTPStarsSubscription &tl, + not_null peer) { return Data::SubscriptionEntry{ .id = qs(tl.data().vid()), .inviteHash = qs(tl.data().vchat_invite_hash().value_or_empty()), + .title = qs(tl.data().vtitle().value_or_empty()), + .slug = qs(tl.data().vinvoice_slug().value_or_empty()), .until = base::unixtime::parse(tl.data().vuntil_date().v), .subscription = Data::PeerSubscription{ .credits = tl.data().vpricing().data().vamount().v, .period = tl.data().vpricing().data().vperiod().v, }, .barePeerId = peerFromMTP(tl.data().vpeer()).value, + .photoId = (tl.data().vphoto() + ? peer->owner().photoFromWeb( + *tl.data().vphoto(), + ImageLocation())->id + : 0), .cancelled = tl.data().is_canceled(), + .cancelledByBot = tl.data().is_bot_canceled(), .expired = (base::unixtime::now() > tl.data().vuntil_date().v), .canRefulfill = tl.data().is_can_refulfill(), }; @@ -166,7 +175,7 @@ constexpr auto kTransactionsLimit = 100; if (const auto history = data.vsubscriptions()) { subscriptions.reserve(history->v.size()); for (const auto &tl : history->v) { - subscriptions.push_back(SubscriptionFromTL(tl)); + subscriptions.push_back(SubscriptionFromTL(tl, peer)); } } return Data::CreditsStatusSlice{ diff --git a/Telegram/SourceFiles/data/data_subscriptions.h b/Telegram/SourceFiles/data/data_subscriptions.h index 69af0d4d6..c0d4de5a2 100644 --- a/Telegram/SourceFiles/data/data_subscriptions.h +++ b/Telegram/SourceFiles/data/data_subscriptions.h @@ -18,6 +18,7 @@ struct PeerSubscription final { } }; +using PhotoId = uint64; struct SubscriptionEntry final { explicit operator bool() const { return !id.isEmpty(); @@ -25,10 +26,14 @@ struct SubscriptionEntry final { QString id; QString inviteHash; + QString title; + QString slug; QDateTime until; PeerSubscription subscription; uint64 barePeerId = 0; + PhotoId photoId = PhotoId(0); bool cancelled = false; + bool cancelledByBot = false; bool expired = false; bool canRefulfill = false; }; From ee29deee4750e41e2d47508d26acaea1a0a8162b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 13 Nov 2024 01:27:07 +0300 Subject: [PATCH 16/39] Improved display of detailed subscriptions in list of credits history. --- .../info_statistics_list_controllers.cpp | 51 +++++++++++++++++-- .../ui/effects/credits_graphics.cpp | 2 +- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index c01e7a9a2..00dffd05a 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -25,6 +25,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "main/session/session_show.h" #include "settings/settings_credits_graphics.h" // PaintSubscriptionRightLabelCallback +#include "ui/dynamic_image.h" +#include "ui/dynamic_thumbnails.h" #include "ui/effects/credits_graphics.h" #include "ui/effects/outline_segments.h" // Ui::UnreadStoryOutlineGradient. #include "ui/effects/toggle_arrow.h" @@ -798,6 +800,9 @@ private: Ui::Text::String _description; Ui::Text::String _rightText; + std::shared_ptr _descriptionThumbnail; + QImage _descriptionThumbnailCache; + base::has_weak_ptr _guard; }; @@ -842,13 +847,19 @@ void CreditsRow::init() { const auto name = !isSpecial ? PeerListRow::generateName() : Ui::GenerateEntryName(_entry).text; - _name = _entry.title.isEmpty() ? name : _entry.title; + _name = _entry.title.isEmpty() + ? name + : (!_entry.subscriptionUntil.isNull() && !isSpecial) + ? name + : _entry.title; setSkipPeerBadge(true); const auto description = _entry.floodSkip ? tr::lng_credits_box_history_entry_floodskip_about( tr::now, lt_count_decimal, _entry.floodSkip) + : (!_entry.subscriptionUntil.isNull() && !_entry.title.isEmpty()) + ? _entry.title : _entry.refunded ? tr::lng_channel_earn_history_return(tr::now) : _entry.pending @@ -876,6 +887,24 @@ void CreditsRow::init() { tr::now, lt_date, langDayOfMonthFull(_subscription.until.date()))); + _description.setText(st::defaultTextStyle, _subscription.title); + } + const auto descriptionPhotoId = (!_entry.subscriptionUntil.isNull()) + ? _entry.photoId + : _subscription.photoId; + if (descriptionPhotoId) { + _descriptionThumbnail = Ui::MakePhotoThumbnail( + _context.session->data().photo(descriptionPhotoId), + {}); + _descriptionThumbnail->subscribeToUpdates([this] { + const auto thumbnailSide = st::defaultTextStyle.font->height; + _descriptionThumbnailCache = Images::Round( + _descriptionThumbnail->image(thumbnailSide), + ImageRoundRadius::Large); + if (_context.customEmojiRepaint) { + _context.customEmojiRepaint(); + } + }); } auto &manager = _context.session->data().customEmojiManager(); if (_entry) { @@ -912,7 +941,11 @@ const Data::SubscriptionEntry &CreditsRow::subscription() const { } QString CreditsRow::generateName() { - return _entry.title.isEmpty() ? _name : _entry.title; + return (!_entry.title.isEmpty() && !_entry.subscriptionUntil.isNull()) + ? _name + : _entry.title.isEmpty() + ? _name + : _entry.title; } PaintRoundImageCallback CreditsRow::generatePaintUserpicCallback(bool force) { @@ -997,8 +1030,20 @@ void CreditsRow::paintStatusText( bool selected) { PeerListRow::paintStatusText(p, st, x, y, available, outer, selected); p.setPen(st.nameFg); + if (!_descriptionThumbnailCache.isNull()) { + const auto thumbnailSide = _descriptionThumbnailCache.width() + / style::DevicePixelRatio(); + const auto thumbnailSpace = st::lineWidth * 4 + thumbnailSide; + p.drawImage( + x, + y - thumbnailSide, + _descriptionThumbnailCache); + x += thumbnailSpace; + outer -= thumbnailSpace; + available -= thumbnailSpace; + } _description.draw(p, { - .position = QPoint(x, y - _description.minHeight() - st::lineWidth), + .position = QPoint(x, y - _description.minHeight()), .outerWidth = outer, .availableWidth = available, .elisionLines = 1, diff --git a/Telegram/SourceFiles/ui/effects/credits_graphics.cpp b/Telegram/SourceFiles/ui/effects/credits_graphics.cpp index 76d899c76..d79741299 100644 --- a/Telegram/SourceFiles/ui/effects/credits_graphics.cpp +++ b/Telegram/SourceFiles/ui/effects/credits_graphics.cpp @@ -533,7 +533,7 @@ Fn)> PaintPreviewCallback( extended, std::move(update)); }; - } else if (entry.photoId) { + } else if (entry.photoId && entry.subscriptionUntil.isNull()) { const auto photo = session->data().photo(entry.photoId); return [=](Fn update) { return GenerateCreditsPaintEntryCallback( From ccc0bf57a138be2a054996a0a5d33a16dbd7620f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 13 Nov 2024 01:27:48 +0300 Subject: [PATCH 17/39] Added support of items with different heights in list of subscriptions. --- .../statistics/info_statistics_list_controllers.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index 00dffd05a..c7b47ec04 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -784,6 +784,9 @@ public: int outer, bool selected) override; + const style::PeerListItem &computeSt( + const style::PeerListItem &st) const override; + private: void init(); @@ -1050,6 +1053,13 @@ void CreditsRow::paintStatusText( }); } +const style::PeerListItem &CreditsRow::computeSt( + const style::PeerListItem &st) const { + return (!_subscription || !_subscription.title.isEmpty()) + ? st + : st::boostsListBox.item; +} + class CreditsController final : public PeerListController { public: explicit CreditsController(CreditsDescriptor d); From fbce06cb26ffd587c1fc1b7881534663ccd0ae01 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 13 Nov 2024 10:33:43 +0300 Subject: [PATCH 18/39] Added support of subscription status when it is cancelled by bot. --- Telegram/Resources/langs/lang.strings | 2 + .../info_statistics_list_controllers.cpp | 60 ++++++++++++++----- .../settings/settings_credits_graphics.cpp | 8 ++- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index d0988628c..6d6e3ed67 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2520,12 +2520,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_credits_subscription_off_button" = "Renew Subscription"; "lng_credits_subscription_off_about" = "You have canceled your subscription."; +"lng_credits_subscription_off_by_bot_about" = "{bot} has canceled your subscription."; "lng_credits_subscription_status_on" = "renews on {date}"; "lng_credits_subscription_status_off" = "expires on {date}"; "lng_credits_subscription_status_none" = "expired on {date}"; "lng_credits_subscription_status_off_right" = "canceled"; "lng_credits_subscription_status_none_right" = "expired"; +"lng_credits_subscription_status_off_by_bot_right" = "canceled\nby bot"; "lng_credits_small_balance_title#one" = "{count} Star Needed"; "lng_credits_small_balance_title#other" = "{count} Stars Needed"; diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index c7b47ec04..3dbe08b96 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -955,16 +955,29 @@ PaintRoundImageCallback CreditsRow::generatePaintUserpicCallback(bool force) { return _paintUserpicCallback; } +[[nodiscard]] QString RightActionText(const Data::SubscriptionEntry &s) { + return s.cancelledByBot + ? tr::lng_credits_subscription_status_off_by_bot_right(tr::now) + : s.cancelled + ? tr::lng_credits_subscription_status_off_right(tr::now) + : s.expired + ? tr::lng_credits_subscription_status_none_right(tr::now) + : QString(); +} + QSize CreditsRow::rightActionSize() const { if (_rightLabel) { return _rightLabel->size; - } else if (_subscription.cancelled || _subscription.expired) { - const auto text = _subscription.cancelled - ? tr::lng_credits_subscription_status_off_right(tr::now) - : tr::lng_credits_subscription_status_none_right(tr::now); - return QSize( - st::contactsStatusFont->width(text) + st::boxRowPadding.right(), - _rowHeight); + } else if (const auto t = RightActionText(_subscription); !t.isEmpty()) { + const auto lines = t.split('\n'); + auto maxWidth = 0; + for (const auto &line : lines) { + const auto width = st::contactsStatusFont->width(line); + if (width > maxWidth) { + maxWidth = width; + } + } + return QSize(maxWidth + st::boxRowPadding.right(), _rowHeight); } else if (_subscription || _entry) { return QSize( _rightText.maxWidth() + st::boxRowPadding.right() / 2, @@ -994,18 +1007,31 @@ void CreditsRow::rightActionPaint( const auto rightSkip = st::boxRowPadding.right(); if (_rightLabel) { return _rightLabel->draw(p, x, y, _rowHeight); - } else if (_subscription.cancelled || _subscription.expired) { + } else if (const auto t = RightActionText(_subscription); !t.isEmpty()) { const auto &statusFont = st::contactsStatusFont; y += _rowHeight / 2; p.setFont(statusFont); p.setPen(st::attentionButtonFg); - p.drawTextRight( - rightSkip, - y - statusFont->height / 2, - outerWidth, - _subscription.expired - ? tr::lng_credits_subscription_status_none_right(tr::now) - : tr::lng_credits_subscription_status_off_right(tr::now)); + + const auto lines = t.split('\n'); + if (lines.size() > 1) { + const auto rect = QRect(x, 0, outerWidth - x, _rowHeight); + const auto lineHeight = statusFont->height; + const auto totalHeight = lines.size() * lineHeight; + auto startY = rect.top() + + (rect.height() - totalHeight) / 2 + + statusFont->ascent; + + for (const auto &line : lines) { + const auto lineWidth = statusFont->width(line); + const auto startX = rect.left() + + (rect.width() - lineWidth) / 2; + p.drawText(startX, startY, line); + startY += lineHeight; + } + return; + } + p.drawTextRight(rightSkip, y - statusFont->height / 2, outerWidth, t); return; } y += _rowHeight / 2; @@ -1143,7 +1169,9 @@ void CreditsController::applySlice(const Data::CreditsStatusSlice &slice) { .entry = i, .subscription = s, .context = _context, - .rowHeight = computeListSt().item.height, + .rowHeight = ((!s || !s.title.isEmpty()) + ? computeListSt().item + : st::boostsListBox.item).height, .updateCallback = [=](not_null row) { delegate()->peerListUpdateRow(row); }, diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index fc1b1d8e8..cb0ab6e89 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -1270,10 +1270,16 @@ void ReceiptCreditsBox( st::creditsBoxAboutDivider))); } if (s) { + const auto user = peer ? peer->asUser() : nullptr; + const auto bot = (user && !user->isSelf()) ? user : nullptr; Ui::AddSkip(content); auto label = object_ptr( box, - s.cancelled + (s.cancelledByBot && bot) + ? tr::lng_credits_subscription_off_by_bot_about( + lt_bot, + rpl::single(bot->name())) + : s.cancelled ? tr::lng_credits_subscription_off_about() : tr::lng_credits_subscription_on_about( lt_date, From f7aaece2f7136aa059de9dc550b22c0a338aafa9 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 14 Nov 2024 01:43:37 +0300 Subject: [PATCH 19/39] Extended width of ReceiptCreditsBox. --- Telegram/SourceFiles/settings/settings_credits_graphics.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index cb0ab6e89..298ca6925 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -820,6 +820,7 @@ void ReceiptCreditsBox( const auto nonConvertible = (gotStarGift && !e.starsConverted); box->setStyle(st::giveawayGiftCodeBox); + box->setWidth(st::boxWideWidth); box->setNoContentMargin(true); const auto content = box->verticalLayout(); @@ -1303,7 +1304,7 @@ void ReceiptCreditsBox( Ui::Premium::MiniStars::Type::BiStars); stars->setColorOverride(Ui::Premium::CreditsIconGradientStops()); widget->resize( - st::boxWidth - stUser.photoSize, + st::boxWideWidth - stUser.photoSize, stUser.photoSize * 2); content->sizeValue( ) | rpl::start_with_next([=](const QSize &size) { @@ -1430,7 +1431,7 @@ void ReceiptCreditsBox( AddChildToWidgetCenter(button, loadingAnimation); loadingAnimation->showOn(state->confirmButtonBusy.value()); } - const auto buttonWidth = st::boxWidth + const auto buttonWidth = st::boxWideWidth - rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding); button->widthValue() | rpl::filter([=] { From 75183612662c38f6375be2ee5919ccbe2a3ce901 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 13 Nov 2024 23:59:49 +0300 Subject: [PATCH 20/39] Improved style of ReceiptCreditsBox for subscription with detailed info. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/boxes/gift_premium_box.cpp | 17 ++++++++++++++++- .../settings/settings_credits_graphics.cpp | 16 ++++++++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 6d6e3ed67..b838c9367 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2509,6 +2509,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_credits_subscriber_subtitle" = "appx. {total} per month"; "lng_credits_subscription_row_to" = "Subscription"; +"lng_credits_subscription_row_to_bot" = "Bot"; "lng_credits_subscription_row_from" = "Subscribed"; "lng_credits_subscription_row_next_on" = "Renews"; diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index a3d385c72..482f6678a 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -1247,6 +1247,13 @@ void AddCreditsHistoryEntryTable( })); } } + if (!entry.subscriptionUntil.isNull() && !entry.title.isEmpty()) { + AddTableRow( + table, + tr::lng_gift_link_label_reason(), + tr::lng_credits_box_history_entry_subscription( + Ui::Text::WithEntities)); + } if (!entry.id.isEmpty()) { constexpr auto kOneLineCount = 22; const auto oneLine = entry.id.size() <= kOneLineCount; @@ -1326,9 +1333,17 @@ void AddSubscriptionEntryTable( const auto peerId = PeerId(s.barePeerId); AddTableRow( table, - tr::lng_credits_subscription_row_to(), + (!s.title.isEmpty() && peerIsUser(peerId)) + ? tr::lng_credits_subscription_row_to_bot() + : tr::lng_credits_subscription_row_to(), controller, peerId); + if (!s.title.isEmpty()) { + AddTableRow( + table, + tr::lng_credits_subscription_row_to(), + rpl::single(Ui::Text::WithEntities(s.title))); + } if (!s.until.isNull()) { if (s.subscription.period > 0) { const auto subscribed = s.until.addSecs(-s.subscription.period); diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 298ca6925..8d913bdac 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -852,6 +852,16 @@ void ReceiptCreditsBox( content, GenericEntryPhoto(content, callback, stUser.photoSize))); AddViewMediaHandler(thumb->entity(), controller, e); + } else if (s.photoId || (e.photoId && !e.subscriptionUntil.isNull())) { + const auto photoId = s.photoId ? s.photoId : e.photoId; + const auto callback = [=](Fn update) { + return Ui::GenerateCreditsPaintEntryCallback( + session->data().photo(photoId), + std::move(update)); + }; + content->add(object_ptr>( + content, + GenericEntryPhoto(content, callback, stUser.photoSize))); } else if (peer && !e.gift) { if (e.subscriptionUntil.isNull() && s.until.isNull()) { content->add(object_ptr>( @@ -951,11 +961,13 @@ void ReceiptCreditsBox( box, object_ptr( box, - rpl::single(!s.until.isNull() + rpl::single(!s.title.isEmpty() + ? s.title + : !s.until.isNull() ? tr::lng_credits_box_subscription_title(tr::now) : isPrize ? tr::lng_credits_box_history_entry_giveaway_name(tr::now) - : !e.subscriptionUntil.isNull() + : (!e.subscriptionUntil.isNull() && e.title.isEmpty()) ? tr::lng_credits_box_history_entry_subscription(tr::now) : !e.title.isEmpty() ? e.title From 213274e96c30453e51bf555637bc916d7e86fc58 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 14 Nov 2024 01:04:46 +0300 Subject: [PATCH 21/39] Replaced style of button to cancel subscription with hyper link. --- Telegram/SourceFiles/api/api_credits.cpp | 16 ++++++ Telegram/SourceFiles/api/api_credits.h | 7 +++ .../settings/settings_credits_graphics.cpp | 57 +++++++++++++------ 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/Telegram/SourceFiles/api/api_credits.cpp b/Telegram/SourceFiles/api/api_credits.cpp index 79560458e..175112c40 100644 --- a/Telegram/SourceFiles/api/api_credits.cpp +++ b/Telegram/SourceFiles/api/api_credits.cpp @@ -472,4 +472,20 @@ Data::CreditsGiveawayOptions CreditsGiveawayOptions::options() const { return _options; } +void EditCreditsSubscription( + not_null session, + const QString &id, + bool cancel, + Fn done, + Fn fail) { + using Flag = MTPpayments_ChangeStarsSubscription::Flag; + session->api().request( + MTPpayments_ChangeStarsSubscription( + MTP_flags(Flag::f_canceled), + MTP_inputPeerSelf(), + MTP_string(id), + MTP_bool(cancel) + )).done(done).fail([=](const MTP::Error &e) { fail(e.type()); }).send(); +} + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_credits.h b/Telegram/SourceFiles/api/api_credits.h index e178cb866..4f697cf09 100644 --- a/Telegram/SourceFiles/api/api_credits.h +++ b/Telegram/SourceFiles/api/api_credits.h @@ -109,4 +109,11 @@ private: [[nodiscard]] rpl::producer> PremiumPeerBot( not_null session); +void EditCreditsSubscription( + not_null session, + const QString &id, + bool cancel, + Fn done, + Fn fail); + } // namespace Api diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 8d913bdac..2e7587b42 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -1285,6 +1285,10 @@ void ReceiptCreditsBox( if (s) { const auto user = peer ? peer->asUser() : nullptr; const auto bot = (user && !user->isSelf()) ? user : nullptr; + const auto toCancel = !s.expired && !s.cancelled && !s.cancelledByBot; + if (toCancel) { + Ui::AddSkip(content); + } Ui::AddSkip(content); auto label = object_ptr( box, @@ -1292,13 +1296,37 @@ void ReceiptCreditsBox( ? tr::lng_credits_subscription_off_by_bot_about( lt_bot, rpl::single(bot->name())) + : toCancel + ? tr::lng_credits_subscription_on_button() : s.cancelled ? tr::lng_credits_subscription_off_about() : tr::lng_credits_subscription_on_about( lt_date, rpl::single(langDayOfMonthFull(s.until.date()))), st::creditsBoxAboutDivider); - if (s.cancelled) { + if (toCancel) { + label->setClickHandlerFilter([=]( + const auto &, + Qt::MouseButton button) { + if (button != Qt::LeftButton) { + return false; + } + const auto done = [=, weak = Ui::MakeWeak(box)] { + if (const auto strong = weak.data()) { + strong->closeBox(); + } + }; + const auto fail = [=, s = box->uiShow()](const QString &e) { + s->showToast(e); + }; + Api::EditCreditsSubscription(session, s.id, true, done, fail); + return true; + }); + label->setMarkedText( + Ui::Text::Link( + tr::lng_credits_subscription_on_button(tr::now), + u"internal:"_q)); + } else if (s.cancelled || s.cancelledByBot) { label->setTextColorOverride(st::menuIconAttentionColor->c); } box->addRow( @@ -1335,15 +1363,14 @@ void ReceiptCreditsBox( } const auto toRenew = (s.cancelled || s.expired) - && !s.inviteHash.isEmpty(); - const auto toCancel = !toRenew && s; + && (!s.inviteHash.isEmpty() + || (base::unixtime::serialize(s.until) > base::unixtime::now())) + && !s.cancelledByBot; auto confirmText = rpl::conditional( state->confirmButtonBusy.value(), rpl::single(QString()), (toRenew ? tr::lng_credits_subscription_off_button() - : toCancel - ? tr::lng_credits_subscription_on_button() : (canConvert || couldConvert || nonConvertible) ? (e.savedToProfile ? tr::lng_gift_display_on_page_hide() @@ -1398,23 +1425,18 @@ void ReceiptCreditsBox( } }); } else { - using Flag = MTPpayments_ChangeStarsSubscription::Flag; - session->api().request( - MTPpayments_ChangeStarsSubscription( - MTP_flags(Flag::f_canceled), - MTP_inputPeerSelf(), - MTP_string(s.id), - MTP_bool(toCancel) - )).done([=] { + const auto done = [=] { if (const auto strong = weak.data()) { strong->closeBox(); } - }).fail([=, show = box->uiShow()](const MTP::Error &error) { + }; + const auto fail = [=, show = box->uiShow()](const QString &e) { if (const auto strong = weak.data()) { state->confirmButtonBusy = false; } - show->showToast(error.type()); - }).send(); + show->showToast(e); + }; + Api::EditCreditsSubscription(session, s.id, false, done, fail); } }; @@ -1423,13 +1445,12 @@ void ReceiptCreditsBox( || state->convertButtonBusy.current()) { return; } - state->confirmButtonBusy = true; if (peer && (toRenew - || toCancel || canConvert || couldConvert || nonConvertible)) { + state->confirmButtonBusy = true; send(); } else { box->closeBox(); From 73936dca735f3b4f873d0f9b7fe3b6d13b536c4b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 14 Nov 2024 02:37:36 +0300 Subject: [PATCH 22/39] Added mini stars to subscriptions with details in ReceiptCreditsBox. --- .../settings/settings_credits_graphics.cpp | 82 +++++++++---------- 1 file changed, 38 insertions(+), 44 deletions(-) diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 2e7587b42..eb4567a82 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -346,6 +346,37 @@ void AddViewMediaHandler( }, thumb->lifetime()); } +void AddMiniStars( + not_null content, + not_null widget, + const style::UserpicButton &stUser, + int boxWidth, + float64 heightRatio) { + using ColoredMiniStars = Ui::Premium::ColoredMiniStars; + const auto stars = widget->lifetime().make_state( + widget, + false, + Ui::Premium::MiniStars::Type::BiStars); + stars->setColorOverride(Ui::Premium::CreditsIconGradientStops()); + widget->resize( + boxWidth - stUser.photoSize, + stUser.photoSize * heightRatio); + content->sizeValue( + ) | rpl::start_with_next([=](const QSize &size) { + widget->moveToLeft(stUser.photoSize / 2, 0); + const auto starsRect = Rect(widget->size()); + stars->setPosition(starsRect.topLeft()); + stars->setSize(starsRect.size()); + widget->lower(); + }, widget->lifetime()); + widget->paintRequest( + ) | rpl::start_with_next([=](const QRect &r) { + auto p = QPainter(widget); + p.fillRect(r, Qt::transparent); + stars->paint(p); + }, widget->lifetime()); +} + } // namespace SubscriptionRightLabel PaintSubscriptionRightLabelCallback( @@ -664,31 +695,12 @@ void BoostCreditsBox( { const auto &stUser = st::premiumGiftsUserpicButton; const auto widget = content->add(object_ptr(content)); - using ColoredMiniStars = Ui::Premium::ColoredMiniStars; - const auto stars = widget->lifetime().make_state( - widget, - false, - Ui::Premium::MiniStars::Type::BiStars); - stars->setColorOverride(Ui::Premium::CreditsIconGradientStops()); - widget->resize( - st::boxWidth - stUser.photoSize, - stUser.photoSize * 1.3); + AddMiniStars(content, widget, stUser, st::boxWidth, 1.3); const auto svg = std::make_shared( Ui::Premium::ColorizedSvg( Ui::Premium::CreditsIconGradientStops())); - content->sizeValue( - ) | rpl::start_with_next([=](const QSize &size) { - widget->moveToLeft(stUser.photoSize / 2, 0); - const auto starsRect = Rect(widget->size()); - stars->setPosition(starsRect.topLeft()); - stars->setSize(starsRect.size()); - widget->lower(); - }, widget->lifetime()); - widget->paintRequest( - ) | rpl::start_with_next([=](const QRect &r) { + widget->paintRequest() | rpl::start_with_next([=](const QRect &r) { auto p = QPainter(widget); - p.fillRect(r, Qt::transparent); - stars->paint(p); svg->render( &p, QRectF( @@ -853,6 +865,10 @@ void ReceiptCreditsBox( GenericEntryPhoto(content, callback, stUser.photoSize))); AddViewMediaHandler(thumb->entity(), controller, e); } else if (s.photoId || (e.photoId && !e.subscriptionUntil.isNull())) { + if (!(s.cancelled || s.expired || s.cancelledByBot)) { + const auto widget = Ui::CreateChild(content); + AddMiniStars(content, widget, stUser, st::boxWideWidth, 1.5); + } const auto photoId = s.photoId ? s.photoId : e.photoId; const auto callback = [=](Fn update) { return Ui::GenerateCreditsPaintEntryCallback( @@ -1337,29 +1353,7 @@ void ReceiptCreditsBox( if (e.peerType == Data::CreditsHistoryEntry::PeerType::PremiumBot) { const auto widget = Ui::CreateChild(content); - using ColoredMiniStars = Ui::Premium::ColoredMiniStars; - const auto stars = widget->lifetime().make_state( - widget, - false, - Ui::Premium::MiniStars::Type::BiStars); - stars->setColorOverride(Ui::Premium::CreditsIconGradientStops()); - widget->resize( - st::boxWideWidth - stUser.photoSize, - stUser.photoSize * 2); - content->sizeValue( - ) | rpl::start_with_next([=](const QSize &size) { - widget->moveToLeft(stUser.photoSize / 2, 0); - const auto starsRect = Rect(widget->size()); - stars->setPosition(starsRect.topLeft()); - stars->setSize(starsRect.size()); - widget->lower(); - }, widget->lifetime()); - widget->paintRequest( - ) | rpl::start_with_next([=](const QRect &r) { - auto p = QPainter(widget); - p.fillRect(r, Qt::transparent); - stars->paint(p); - }, widget->lifetime()); + AddMiniStars(content, widget, stUser, st::boxWideWidth, 2); } const auto toRenew = (s.cancelled || s.expired) From 27a5ba4681fc4974304fa88e7ff739c7ffefa145 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 14 Nov 2024 04:02:07 +0300 Subject: [PATCH 23/39] Added top close button to ReceiptCreditsBox. --- Telegram/SourceFiles/boxes/send_credits_box.cpp | 10 +++------- .../SourceFiles/settings/settings_credits_graphics.cpp | 10 ++++++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/boxes/send_credits_box.cpp b/Telegram/SourceFiles/boxes/send_credits_box.cpp index d00a95f1f..d0ddd819c 100644 --- a/Telegram/SourceFiles/boxes/send_credits_box.cpp +++ b/Telegram/SourceFiles/boxes/send_credits_box.cpp @@ -473,15 +473,11 @@ void SendCreditsBox( { const auto close = Ui::CreateChild( - box.get(), + content, st::boxTitleClose); - close->setClickedCallback([=] { - box->closeBox(); - }); - box->widthValue( - ) | rpl::start_with_next([=](int width) { + close->setClickedCallback([=] { box->closeBox(); }); + content->widthValue() | rpl::start_with_next([=](int) { close->moveToRight(0, 0); - close->raise(); }, close->lifetime()); } diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index eb4567a82..299f0437b 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -1434,6 +1434,16 @@ void ReceiptCreditsBox( } }; + if ((toRenew || canConvert || couldConvert || nonConvertible) && peer) { + const auto close = Ui::CreateChild( + content, + st::boxTitleClose); + close->setClickedCallback([=] { box->closeBox(); }); + content->widthValue() | rpl::start_with_next([=](int) { + close->moveToRight(0, 0); + }, content->lifetime()); + } + const auto button = box->addButton(std::move(confirmText), [=] { if (state->confirmButtonBusy.current() || state->convertButtonBusy.current()) { From 9824df5f2a00b1efe08c6a9e4b120b7d3ddb5887 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 14 Nov 2024 05:09:02 +0300 Subject: [PATCH 24/39] Added phrases for business subscriptions. --- Telegram/Resources/langs/lang.strings | 7 +++++-- Telegram/SourceFiles/boxes/gift_premium_box.cpp | 7 ++++++- Telegram/SourceFiles/boxes/send_credits_box.cpp | 6 ++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index b838c9367..66fc25351 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2445,8 +2445,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_credits_box_out_media#other" = "Do you want to unlock {media} in {chat} for **{count} Stars**?"; "lng_credits_box_out_media_user#one" = "Do you want to unlock {media} from {user} for **{count} Star**?"; "lng_credits_box_out_media_user#other" = "Do you want to unlock {media} from {user} for **{count} Stars**?"; -"lng_credits_box_out_subscription#one" = "Do you want to subscribe to **{title}** in **{bot}** for **{count}** star per month?"; -"lng_credits_box_out_subscription#other" = "Do you want to subscribe to **{title}** in **{bot}** for **{count}** stars per month?"; +"lng_credits_box_out_subscription_bot#one" = "Do you want to subscribe to **{title}** in **{recipient}** for **{count}** star per month?"; +"lng_credits_box_out_subscription_bot#other" = "Do you want to subscribe to **{title}** in **{recipient}** for **{count}** stars per month?"; +"lng_credits_box_out_subscription_business#one" = "Do you want to subscribe to **{title}** from **{recipient}** for **{count}** star per month?"; +"lng_credits_box_out_subscription_business#other" = "Do you want to subscribe to **{title}** from **{recipient}** for **{count}** stars per month?"; "lng_credits_box_out_subscription_confirm#one" = "Subscribe for {emoji} {count} / month"; "lng_credits_box_out_subscription_confirm#other" = "Subscribe for {emoji} {count} / month"; "lng_credits_box_out_photo" = "a photo"; @@ -2510,6 +2512,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_credits_subscription_row_to" = "Subscription"; "lng_credits_subscription_row_to_bot" = "Bot"; +"lng_credits_subscription_row_to_business" = "Business"; "lng_credits_subscription_row_from" = "Subscribed"; "lng_credits_subscription_row_next_on" = "Renews"; diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index 482f6678a..78c74ffeb 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -1331,10 +1331,15 @@ void AddSubscriptionEntryTable( st::giveawayGiftCodeTable), st::giveawayGiftCodeTableMargin); const auto peerId = PeerId(s.barePeerId); + const auto user = peerIsUser(peerId) + ? controller->session().data().peer(peerId)->asUser() + : nullptr; AddTableRow( table, - (!s.title.isEmpty() && peerIsUser(peerId)) + (!s.title.isEmpty() && user && user->botInfo) ? tr::lng_credits_subscription_row_to_bot() + : (!s.title.isEmpty() && user && !user->botInfo) + ? tr::lng_credits_subscription_row_to_business() : tr::lng_credits_subscription_row_to(), controller, peerId); diff --git a/Telegram/SourceFiles/boxes/send_credits_box.cpp b/Telegram/SourceFiles/boxes/send_credits_box.cpp index d0ddd819c..e42151d75 100644 --- a/Telegram/SourceFiles/boxes/send_credits_box.cpp +++ b/Telegram/SourceFiles/boxes/send_credits_box.cpp @@ -194,12 +194,14 @@ void AddTerms( const auto bot = session->data().user(form->botId); if (form->invoice.subscriptionPeriod) { - return tr::lng_credits_box_out_subscription( + return (bot->botInfo + ? tr::lng_credits_box_out_subscription_bot + : tr::lng_credits_box_out_subscription_business)( lt_count, rpl::single(form->invoice.amount) | tr::to_count(), lt_title, rpl::single(TextWithEntities{ form->title }), - lt_bot, + lt_recipient, rpl::single(TextWithEntities{ bot->name() }), Ui::Text::RichLangValue); } From 4608ffcab488cd69fba82ec32ceafd117b8e56df Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 14 Nov 2024 06:42:04 +0300 Subject: [PATCH 25/39] Added weak pointer for rebuilder of subscription list to Data::Session. --- Telegram/SourceFiles/data/data_session.cpp | 13 +++++++++++++ Telegram/SourceFiles/data/data_session.h | 8 ++++++++ 2 files changed, 21 insertions(+) diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index d8782132e..f5b5553ce 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1957,6 +1957,19 @@ rpl::producer<> Session::pinnedDialogsOrderUpdated() const { return _pinnedDialogsOrderUpdated.events(); } +Session::CreditsSubsRebuilderPtr Session::createCreditsSubsRebuilder() { + if (auto result = activeCreditsSubsRebuilder()) { + return result; + } + auto result = std::make_shared(); + _creditsSubsRebuilder = result; + return result; +} + +Session::CreditsSubsRebuilderPtr Session::activeCreditsSubsRebuilder() const { + return _creditsSubsRebuilder.lock(); +} + void Session::registerHeavyViewPart(not_null view) { _heavyViewParts.emplace(view); } diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index b9d048062..84e735c09 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -70,6 +70,7 @@ class Chatbots; class BusinessInfo; struct ReactionId; struct UnavailableReason; +struct CreditsStatusSlice; struct RepliesReadTillUpdate { FullMsgId id; @@ -337,6 +338,11 @@ public: void notifyPinnedDialogsOrderUpdated(); [[nodiscard]] rpl::producer<> pinnedDialogsOrderUpdated() const; + using CreditsSubsRebuilder = rpl::event_stream; + using CreditsSubsRebuilderPtr = std::shared_ptr; + [[nodiscard]] CreditsSubsRebuilderPtr createCreditsSubsRebuilder(); + [[nodiscard]] CreditsSubsRebuilderPtr activeCreditsSubsRebuilder() const; + void registerRestricted( not_null item, const QString &reason); @@ -1095,6 +1101,8 @@ private: MessageIdsList _mimeForwardIds; + std::weak_ptr _creditsSubsRebuilder; + using CredentialsWithGeneration = std::pair< const Passport::SavedCredentials, int>; From f795d56b2aac6302eb8810341416cfd53a597b4b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 14 Nov 2024 06:43:18 +0300 Subject: [PATCH 26/39] Handled subscriptions list changes in settings section for credits. --- Telegram/SourceFiles/api/api_chat_invite.cpp | 28 ++++++++++++--- .../SourceFiles/settings/settings_credits.cpp | 13 +++++++ .../settings/settings_credits_graphics.cpp | 34 ++++++++++++++----- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/Telegram/SourceFiles/api/api_chat_invite.cpp b/Telegram/SourceFiles/api/api_chat_invite.cpp index 0a53c61dd..134e3a005 100644 --- a/Telegram/SourceFiles/api/api_chat_invite.cpp +++ b/Telegram/SourceFiles/api/api_chat_invite.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_chat_invite.h" #include "apiwrap.h" +#include "api/api_credits.h" #include "boxes/premium_limits_box.h" #include "core/application.h" #include "data/components/credits.h" @@ -295,20 +296,39 @@ void ConfirmSubscriptionBox( const auto buttonWidth = state->saveButton ? state->saveButton->width() : 0; + const auto finish = [=] { + state->api = std::nullopt; + state->loading.force_assign(false); + if (const auto strong = weak.data()) { + strong->closeBox(); + } + }; state->api->request( MTPpayments_SendStarsForm( MTP_long(formId), MTP_inputInvoiceChatInviteSubscription(MTP_string(hash))) ).done([=](const MTPpayments_PaymentResult &result) { - state->api = std::nullopt; - state->loading.force_assign(false); result.match([&](const MTPDpayments_paymentResult &data) { session->api().applyUpdates(data.vupdates()); }, [](const MTPDpayments_paymentVerificationNeeded &data) { }); - if (weak) { - box->closeBox(); + const auto refill = session->data().activeCreditsSubsRebuilder(); + const auto strong = weak.data(); + if (!strong) { + return; } + if (!refill) { + return finish(); + } + const auto api + = strong->lifetime().make_state( + session->user(), + true, + true); + api->requestSubscriptions({}, [=](Data::CreditsStatusSlice d) { + refill->fire(std::move(d)); + finish(); + }); }).fail([=](const MTP::Error &error) { const auto id = error.type(); if (weak) { diff --git a/Telegram/SourceFiles/settings/settings_credits.cpp b/Telegram/SourceFiles/settings/settings_credits.cpp index b4cd7396d..f22b94a7c 100644 --- a/Telegram/SourceFiles/settings/settings_credits.cpp +++ b/Telegram/SourceFiles/settings/settings_credits.cpp @@ -191,6 +191,19 @@ void Credits::setupSubscriptions(not_null container) { fill(std::move(d)); }); } + { + using Rebuilder = Data::Session::CreditsSubsRebuilder; + using RebuilderPtr = std::shared_ptr; + const auto rebuilder = content->lifetime().make_state( + self->owner().createCreditsSubsRebuilder()); + rebuilder->get()->events( + ) | rpl::start_with_next([=](Data::CreditsStatusSlice slice) { + while (content->count()) { + delete content->widgetAt(0); + } + fill(std::move(slice)); + }, content->lifetime()); + } } void Credits::setupHistory(not_null container) { diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 299f0437b..0b701307b 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -802,6 +802,28 @@ void BoostCreditsBox( }, button->lifetime()); } +void ProcessReceivedSubscriptions( + QPointer weak, + not_null session) { + const auto rebuilder = session->data().activeCreditsSubsRebuilder(); + if (const auto strong = weak.data()) { + if (!rebuilder) { + return strong->closeBox(); + } + const auto api + = strong->lifetime().make_state( + session->user(), + true, + true); + api->requestSubscriptions({}, [=](Data::CreditsStatusSlice first) { + rebuilder->fire(std::move(first)); + if (const auto strong = weak.data()) { + strong->closeBox(); + } + }); + } +} + void ReceiptCreditsBox( not_null box, not_null controller, @@ -1328,9 +1350,7 @@ void ReceiptCreditsBox( return false; } const auto done = [=, weak = Ui::MakeWeak(box)] { - if (const auto strong = weak.data()) { - strong->closeBox(); - } + ProcessReceivedSubscriptions(weak, session); }; const auto fail = [=, s = box->uiShow()](const QString &e) { s->showToast(e); @@ -1414,15 +1434,11 @@ void ReceiptCreditsBox( } } else if (toRenew && s.expired) { Api::CheckChatInvite(controller, s.inviteHash, nullptr, [=] { - if (const auto strong = weak.data()) { - strong->closeBox(); - } + ProcessReceivedSubscriptions(weak, session); }); } else { const auto done = [=] { - if (const auto strong = weak.data()) { - strong->closeBox(); - } + ProcessReceivedSubscriptions(weak, session); }; const auto fail = [=, show = box->uiShow()](const QString &e) { if (const auto strong = weak.data()) { From d69905feae16ea280d53798a68edead2727e559f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 14 Nov 2024 08:42:15 +0300 Subject: [PATCH 27/39] Added ability to subscribe again via slug from subscriptions list. --- Telegram/Resources/langs/lang.strings | 1 + .../payments/payments_non_panel_process.cpp | 16 ++++---- .../settings/settings_credits_graphics.cpp | 37 +++++++++++++++---- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 66fc25351..b29aa7693 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2523,6 +2523,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_credits_subscription_on_about" = "If you cancel now, you will still be able to access your subscription until {date}."; "lng_credits_subscription_off_button" = "Renew Subscription"; +"lng_credits_subscription_off_rejoin_button" = "Subscribe again"; "lng_credits_subscription_off_about" = "You have canceled your subscription."; "lng_credits_subscription_off_by_bot_about" = "{bot} has canceled your subscription."; diff --git a/Telegram/SourceFiles/payments/payments_non_panel_process.cpp b/Telegram/SourceFiles/payments/payments_non_panel_process.cpp index 6f348df4f..0b9e292fd 100644 --- a/Telegram/SourceFiles/payments/payments_non_panel_process.cpp +++ b/Telegram/SourceFiles/payments/payments_non_panel_process.cpp @@ -94,14 +94,14 @@ void ProcessCreditsPayment( Ui::SendCreditsBox, form, [=] { - *unsuccessful = false; - if (const auto widget = fireworks.data()) { - Ui::StartFireworks(widget); - } - if (const auto onstack = maybeReturnToBot) { - onstack(CheckoutResult::Paid); - } - })); + *unsuccessful = false; + if (const auto widget = fireworks.data()) { + Ui::StartFireworks(widget); + } + if (const auto onstack = maybeReturnToBot) { + onstack(CheckoutResult::Paid); + } + })); box->boxClosing() | rpl::start_with_next([=] { crl::on_main([=] { if (*unsuccessful) { diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 0b701307b..4a190dc3a 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -47,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "payments/payments_checkout_process.h" #include "payments/payments_form.h" +#include "payments/payments_non_panel_process.h" #include "settings/settings_common_session.h" #include "settings/settings_credits.h" #include "statistics/widgets/chart_header_widget.h" @@ -1376,15 +1377,23 @@ void ReceiptCreditsBox( AddMiniStars(content, widget, stUser, st::boxWideWidth, 2); } + const auto rejoinByApi = base::unixtime::serialize(s.until) + > base::unixtime::now(); + const auto rejoinByInvite = !s.inviteHash.isEmpty(); + const auto rejoinBySlug = !s.slug.isEmpty(); const auto toRenew = (s.cancelled || s.expired) - && (!s.inviteHash.isEmpty() - || (base::unixtime::serialize(s.until) > base::unixtime::now())) + && (rejoinByApi || rejoinByInvite) + && !s.cancelledByBot; + const auto toRejoin = (s.cancelled || s.expired) + && rejoinBySlug && !s.cancelledByBot; auto confirmText = rpl::conditional( state->confirmButtonBusy.value(), rpl::single(QString()), (toRenew ? tr::lng_credits_subscription_off_button() + : toRejoin + ? tr::lng_credits_subscription_off_rejoin_button() : (canConvert || couldConvert || nonConvertible) ? (e.savedToProfile ? tr::lng_gift_display_on_page_hide() @@ -1432,6 +1441,19 @@ void ReceiptCreditsBox( save, done); } + } else if (toRejoin) { + if (const auto window = weakWindow.get()) { + const auto finish = [=](Payments::CheckoutResult&&) { + ProcessReceivedSubscriptions(weak, session); + }; + Payments::CheckoutProcess::Start( + &window->session(), + s.slug, + [](auto) {}, + Payments::ProcessNonPanelPaymentFormFactory( + window, + finish)); + } } else if (toRenew && s.expired) { Api::CheckChatInvite(controller, s.inviteHash, nullptr, [=] { ProcessReceivedSubscriptions(weak, session); @@ -1450,7 +1472,10 @@ void ReceiptCreditsBox( } }; - if ((toRenew || canConvert || couldConvert || nonConvertible) && peer) { + const auto willBusy = toRejoin + || (peer + && (toRenew || canConvert || couldConvert || nonConvertible)); + if (willBusy) { const auto close = Ui::CreateChild( content, st::boxTitleClose); @@ -1465,11 +1490,7 @@ void ReceiptCreditsBox( || state->convertButtonBusy.current()) { return; } - if (peer - && (toRenew - || canConvert - || couldConvert - || nonConvertible)) { + if (willBusy) { state->confirmButtonBusy = true; send(); } else { From 2ae4e15f87f4124c86ccf9a0423320a72685b817 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 21 Nov 2024 14:30:21 +0300 Subject: [PATCH 28/39] Fixed ability to copy transactions id from credits history entries. Regression was introduced in 77e7796b3f. --- .../SourceFiles/boxes/gift_premium_box.cpp | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index 78c74ffeb..542189659 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -1255,24 +1255,12 @@ void AddCreditsHistoryEntryTable( Ui::Text::WithEntities)); } if (!entry.id.isEmpty()) { - constexpr auto kOneLineCount = 22; - const auto oneLine = entry.id.size() <= kOneLineCount; - auto multiLine = QString(); - if (!oneLine) { - for (auto i = 0; i < entry.id.size(); ++i) { - multiLine.append(entry.id[i]); - if ((i + 1) % kOneLineCount == 0) { - multiLine.append('\n'); - } - } - } + constexpr auto kOneLineCount = 24; + const auto oneLine = entry.id.length() <= kOneLineCount; auto label = object_ptr( table, rpl::single( - Ui::Text::Wrapped( - { oneLine ? entry.id : std::move(multiLine) }, - EntityType::Code, - {})), + Ui::Text::Wrapped({ entry.id }, EntityType::Code, {})), oneLine ? st::giveawayGiftCodeValue : st::giveawayGiftCodeValueMultiline); From 0c21eba1f8adef78da688f6420d0ca3a89d5f0b6 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 23 Nov 2024 05:11:13 +0300 Subject: [PATCH 29/39] Fixed display of preview for sticker packs. --- .../history/view/media/history_view_web_page.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index c9980a817..90aa91503 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -630,10 +630,13 @@ QSize WebPage::countOptimalSize() { _durationWidth = st::msgDateFont->width(_duration); } if (!_openButton.isEmpty()) { - accumulate_max( - maxWidth, - rect::m::sum::h(st::historyPageButtonPadding) - + _openButton.maxWidth()); + const auto w = rect::m::sum::h(st::historyPageButtonPadding) + + _openButton.maxWidth(); + if (sponsored) { + accumulate_max(maxWidth, w); + } else { + maxWidth += w; + } } maxWidth += rect::m::sum::h(padding); minHeight += rect::m::sum::v(padding); From 71efd95136df8ca48247a6e558c5e677b9324438 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 23 Nov 2024 05:28:36 +0300 Subject: [PATCH 30/39] Slightly shifted selection marks along x. --- Telegram/SourceFiles/history/view/history_view_message.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index d67787ae6..b7d4e89d2 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -1627,7 +1627,8 @@ void Message::draw(Painter &p, const PaintContext &context) const { (right - (st::msgSelectionOffset * progress - st.size) / 2 - st::msgPadding.right() / 2 - - st.size), + - st.size + - st::historyScroll.deltax), rect::bottom(g) - st.size - st::msgSelectionBottomSkip); { p.setPen(QPen(st.border, st.width)); From ec83f4ae72544b10712880431c3d260958b8e830 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 23 Nov 2024 06:01:23 +0300 Subject: [PATCH 31/39] Removed unnecessary rebuild of chats filters strip when order is same. --- .../ui/widgets/chat_filters_tabs_slider.cpp | 20 +++++++++++++++++++ .../ui/widgets/chat_filters_tabs_slider.h | 2 ++ .../ui/widgets/chat_filters_tabs_strip.cpp | 19 ++++++++++-------- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp index ac6f6dcbd..b2286be5f 100644 --- a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp +++ b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.cpp @@ -57,6 +57,26 @@ ChatsFiltersTabs::ChatsFiltersTabs( Ui::DiscreteSlider::setSelectOnPress(false); } +bool ChatsFiltersTabs::setSectionsAndCheckChanged( + std::vector &§ions) { + const auto &was = sectionsRef(); + const auto changed = [&] { + if (was.size() != sections.size()) { + return true; + } + for (auto i = 0; i < sections.size(); i++) { + if (was[i].label.toString() != sections[i]) { + return true; + } + } + return false; + }(); + if (changed) { + Ui::DiscreteSlider::setSections(std::move(sections)); + } + return changed; +} + int ChatsFiltersTabs::centerOfSection(int section) const { const auto widths = countSectionsWidths(0); auto result = 0; diff --git a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h index c66818fbd..9bd4cf67e 100644 --- a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h +++ b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_slider.h @@ -27,6 +27,8 @@ public: not_null parent, const style::SettingsSlider &st); + bool setSectionsAndCheckChanged(std::vector &§ions); + [[nodiscard]] int centerOfSection(int section) const; void fitWidthToSections(); void setUnreadCount(int index, int unreadCount, bool muted); diff --git a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp index fe8710f30..2c4472e79 100644 --- a/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp +++ b/Telegram/SourceFiles/ui/widgets/chat_filters_tabs_strip.cpp @@ -293,15 +293,18 @@ not_null AddChatFiltersTabsStrip( if ((list.size() <= 1 && !slider->width()) || state->ignoreRefresh) { return; } + const auto sectionsChanged = slider->setSectionsAndCheckChanged( + ranges::views::all( + list + ) | ranges::views::transform([](const Data::ChatFilter &filter) { + return filter.title().isEmpty() + ? tr::lng_filters_all_short(tr::now) + : filter.title(); + }) | ranges::to_vector); + if (!sectionsChanged) { + return; + } state->rebuildLifetime.destroy(); - auto sections = ranges::views::all( - list - ) | ranges::views::transform([](const Data::ChatFilter &filter) { - return filter.title().isEmpty() - ? tr::lng_filters_all_short(tr::now) - : filter.title(); - }) | ranges::to_vector; - slider->setSections(std::move(sections)); slider->fitWidthToSections(); { const auto reorderAll = session->user()->isPremium(); From a5e927ea4f252e62257d54136229dc3211f475a5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 21 Nov 2024 22:09:40 +0400 Subject: [PATCH 32/39] Fix build for Windows. --- Telegram/SourceFiles/boxes/peer_list_widgets.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/peer_list_widgets.cpp b/Telegram/SourceFiles/boxes/peer_list_widgets.cpp index 3a2b8ca37..dfa729eb6 100644 --- a/Telegram/SourceFiles/boxes/peer_list_widgets.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_widgets.cpp @@ -51,7 +51,6 @@ crl::time PeerListWidgets::paintRow( ? std::max(refreshStatusAt - now, crl::time(1)) : 0; - const auto peer = row->special() ? nullptr : row->peer().get(); row->paintUserpic( p, st, From 53091389808b9b2b17c8583f97399d866359af8b Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 21 Nov 2024 22:09:54 +0400 Subject: [PATCH 33/39] Support display of messageActionPaymentSentMe. --- Telegram/Resources/langs/lang.strings | 2 ++ Telegram/SourceFiles/history/history_item.cpp | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index b29aa7693..43fa84602 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1862,6 +1862,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_payment_init_recurring_for" = "You successfully transferred {amount} to {user} for {invoice} and allowed future recurring payments"; "lng_action_payment_init_recurring" = "You successfully transferred {amount} to {user} and allowed future recurring payments"; "lng_action_payment_used_recurring" = "You were charged {amount} via recurring payment"; +"lng_action_payment_bot_done" = "Bot connected to this account received {amount}"; +"lng_action_payment_bot_recurring" = "Bot connected to this account received {amount} via recurring payment"; "lng_action_took_screenshot" = "{from} took a screenshot!"; "lng_action_you_took_screenshot" = "You took a screenshot!"; "lng_action_bot_allowed_from_domain" = "You allowed this bot to message you when you logged in on {domain}."; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index c8f21a423..2c566ab1a 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -4558,6 +4558,21 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { return preparePaymentSentText(); }; + auto preparePaymentSentMe = [&](const MTPDmessageActionPaymentSentMe &data) { + auto result = PreparedServiceText(); + result.text = (data.is_recurring_used() + ? tr::lng_action_payment_bot_recurring + : tr::lng_action_payment_bot_done)( + tr::now, + lt_amount, + AmountAndStarCurrency( + &_history->session(), + data.vtotal_amount().v, + qs(data.vcurrency())), + Ui::Text::WithEntities); + return result; + }; + auto prepareScreenshotTaken = [this](const MTPDmessageActionScreenshotTaken &) { auto result = PreparedServiceText(); if (out()) { @@ -5361,7 +5376,7 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { prepareSecureValuesSent, prepareContactSignUp, prepareProximityReached, - PrepareErrorText, + preparePaymentSentMe, PrepareErrorText, prepareGroupCall, prepareInviteToGroupCall, From 03b6e2df1737391881cbc19b7c69011babeee5fa Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 23 Nov 2024 09:37:26 +0400 Subject: [PATCH 34/39] Don't confirm each bot-url webapp open. --- .../inline_bots/bot_attach_web_view.cpp | 28 +++++++++++++++---- .../inline_bots/bot_attach_web_view.h | 12 ++++++-- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index 52fda97bf..c5bf5ce1c 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -946,7 +946,12 @@ void WebViewInstance::resolve() { requestSimple(); }); }, [&](WebViewSourceLinkApp data) { - resolveApp(data.appname, data.token, !_context.maySkipConfirmation); + resolveApp( + data.appname, + data.token, + (_context.maySkipConfirmation + ? ConfirmType::None + : ConfirmType::Always)); }, [&](WebViewSourceLinkBotProfile) { confirmOpen([=] { requestMain(); @@ -994,14 +999,14 @@ bool WebViewInstance::openAppFromBotMenuLink() { if (appname.isEmpty()) { return false; } - resolveApp(appname, params.value(u"startapp"_q), true); + resolveApp(appname, params.value(u"startapp"_q), ConfirmType::Once); return true; } void WebViewInstance::resolveApp( const QString &appname, const QString &startparam, - bool forceConfirmation) { + ConfirmType confirmType) { const auto already = _session->data().findBotApp(_bot->id, appname); _requestId = _session->api().request(MTPmessages_GetBotApp( MTP_inputBotAppShortName( @@ -1021,8 +1026,11 @@ void WebViewInstance::resolveApp( close(); return; } - const auto confirm = data.is_inactive() || forceConfirmation; + const auto confirm = data.is_inactive() + || (confirmType != ConfirmType::None); const auto writeAccess = result.data().is_request_write_access(); + const auto forceConfirmation = data.is_inactive() + || (confirmType == ConfirmType::Always); // Check if this app can be added to main menu. // On fail it'll still be opened. @@ -1035,7 +1043,7 @@ void WebViewInstance::resolveApp( } else if (confirm) { confirmAppOpen(writeAccess, [=](bool allowWrite) { requestApp(allowWrite); - }); + }, forceConfirmation); } else { requestApp(false); } @@ -1082,10 +1090,18 @@ void WebViewInstance::confirmOpen(Fn done) { void WebViewInstance::confirmAppOpen( bool writeAccess, - Fn done) { + Fn done, + bool forceConfirmation) { + if (!forceConfirmation + && (_bot->isVerified() + || _session->local().isBotTrustedOpenWebView(_bot->id))) { + done(writeAccess); + return; + } _parentShow->show(Box([=](not_null box) { const auto allowed = std::make_shared(); const auto callback = [=](Fn close) { + _session->local().markBotTrustedOpenWebView(_bot->id); done((*allowed) && (*allowed)->checked()); close(); }; diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h index febf74dfa..f0cb146a7 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h @@ -230,12 +230,20 @@ private: void requestWithMenuAdd(); void maybeChooseAndRequestButton(PeerTypes supported); + enum class ConfirmType : uchar { + Always, + Once, + None, + }; void resolveApp( const QString &appname, const QString &startparam, - bool forceConfirmation); + ConfirmType confirmType); void confirmOpen(Fn done); - void confirmAppOpen(bool writeAccess, Fn done); + void confirmAppOpen( + bool writeAccess, + Fn done, + bool forceConfirmation); struct ShowArgs { QString url; From edc6cfe2101ed52851ebfce8e6ed63bd56dbfdfe Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 23 Nov 2024 10:04:55 +0400 Subject: [PATCH 35/39] Replace non-started calls. --- Telegram/SourceFiles/calls/calls_instance.cpp | 3 ++- Telegram/SourceFiles/calls/calls_panel.cpp | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index 118532d16..7d5793727 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -669,7 +669,8 @@ bool Instance::inCall() const { return false; } const auto state = _currentCall->state(); - return (state != Call::State::Busy); + return (state != Call::State::Busy) + && (state != Call::State::WaitingUserConfirmation); } bool Instance::inGroupCall() const { diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index 6f0ab0f6a..74ddcb8f2 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -993,7 +993,12 @@ void Panel::paint(QRect clip) { bool Panel::handleClose() const { if (_call) { - window()->hide(); + if (_call->state() == Call::State::WaitingUserConfirmation + || _call->state() == Call::State::Busy) { + _call->hangup(); + } else { + window()->hide(); + } return true; } return false; @@ -1028,6 +1033,7 @@ void Panel::stateChanged(State state) { _startVideo = base::make_unique_q( widget(), st::callStartVideo); + _startVideo->show(); _startVideo->setText(tr::lng_call_start_video()); _startVideo->clicks() | rpl::map_to(true) | rpl::start_to_stream( _startOutgoingRequests, From 2c7089d47f1b5c557b6406586214f039c2856d65 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 23 Nov 2024 10:47:36 +0400 Subject: [PATCH 36/39] Ctrl+Reply+Click replies in another chat. --- Telegram/Resources/langs/lang.strings | 1 + .../history/history_inner_widget.cpp | 67 +++++++------------ .../history/history_inner_widget.h | 2 + .../SourceFiles/history/history_widget.cpp | 10 ++- .../history/view/history_view_list_widget.cpp | 2 +- 5 files changed, 36 insertions(+), 46 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 43fa84602..b9c61205a 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -290,6 +290,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_error_cant_add_admin_invite" = "You can't add this user as an admin because they are not a member of this group and you are not allowed to add them."; "lng_error_cant_add_admin_unban" = "Sorry, you can't add this user as an admin because they are in the Removed Users list and you can't unban them."; "lng_error_cant_ban_admin" = "You can't ban this user because they are an admin in this group and you are not allowed to demote them."; +"lng_error_cant_reply_other" = "This message can't be replied in another chat."; "lng_error_admin_limit" = "Sorry, you've reached the maximum number of admins for this group."; "lng_error_admin_limit_channel" = "Sorry, you've reached the maximum number of admins for this channel."; "lng_error_post_link_invalid" = "Unfortunately, you can't access this message. You aren't a member of the chat where it was posted."; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 2753b0dc0..a6b9f2669 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -124,15 +124,6 @@ int BinarySearchBlocksOrItems(const T &list, int edge) { return start; } -[[nodiscard]] bool CanSendReply(not_null item) { - const auto peer = item->history()->peer; - const auto topic = item->topic(); - return topic - ? Data::CanSendAnything(topic) - : (Data::CanSendAnything(peer) - && (!peer->isChannel() || peer->asChannel()->amIn())); -} - } // namespace // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html @@ -576,21 +567,13 @@ void HistoryInner::setupSwipeReply() { const auto replyToItemId = (selected.item ? selected.item : still)->fullId(); - if (canSendReply) { - _widget->replyToMessage({ - .messageId = replyToItemId, - .quote = selected.text, - .quoteOffset = selected.offset, - }); - if (!selected.text.empty()) { - _widget->clearSelected(); - } - } else { - HistoryView::Controls::ShowReplyToChatBox(show, { - .messageId = replyToItemId, - .quote = selected.text, - .quoteOffset = selected.offset, - }); + _widget->replyToMessage({ + .messageId = replyToItemId, + .quote = selected.text, + .quoteOffset = selected.offset, + }); + if (!selected.text.empty()) { + _widget->clearSelected(); } }; return false; @@ -2575,26 +2558,13 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto quoteOffset = selected.offset; text.replace('&', u"&&"_q); _menu->addAction(text, [=] { - const auto still = session->data().message(itemId); - const auto forceAnotherChat = base::IsCtrlPressed() - && still - && still->allowsForward(); - if (canSendReply && !forceAnotherChat) { - _widget->replyToMessage({ - .messageId = itemId, - .quote = quote, - .quoteOffset = quoteOffset, - }); - if (!quote.empty()) { - _widget->clearSelected(); - } - } else { - const auto show = controller->uiShow(); - HistoryView::Controls::ShowReplyToChatBox(show, { - .messageId = itemId, - .quote = quote, - .quoteOffset = quoteOffset, - }); + _widget->replyToMessage({ + .messageId = itemId, + .quote = quote, + .quoteOffset = quoteOffset, + }); + if (!quote.empty()) { + _widget->clearSelected(); } }, &st::menuIconReply); } @@ -4774,3 +4744,12 @@ auto HistoryInner::DelegateMixin() -> std::unique_ptr { return std::make_unique(); } + +bool CanSendReply(not_null item) { + const auto peer = item->history()->peer; + const auto topic = item->topic(); + return topic + ? Data::CanSendAnything(topic) + : (Data::CanSendAnything(peer) + && (!peer->isChannel() || peer->asChannel()->amIn())); +} diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index e3d671a97..356609a26 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -553,3 +553,5 @@ private: ClickHandlerPtr _scrollDateLink; }; + +[[nodiscard]] bool CanSendReply(not_null item); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index e9d097a27..f9f3445de 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -7884,7 +7884,15 @@ void HistoryWidget::clearFieldText( void HistoryWidget::replyToMessage(FullReplyTo id) { if (const auto item = session().data().message(id.messageId)) { - replyToMessage(item, id.quote, id.quoteOffset); + if (CanSendReply(item) && !base::IsCtrlPressed()) { + replyToMessage(item, id.quote, id.quoteOffset); + } else if (item->allowsForward()) { + const auto show = controller()->uiShow(); + HistoryView::Controls::ShowReplyToChatBox(show, id); + } else { + controller()->showToast( + tr::lng_error_cant_reply_other(tr::now)); + } } } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 1a67af4ec..35d0d8073 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1879,7 +1879,7 @@ not_null ListWidget::elementPathShiftGradient() { } void ListWidget::elementReplyTo(const FullReplyTo &to) { - replyToMessageRequestNotify(to); + replyToMessageRequestNotify(to, base::IsCtrlPressed()); } void ListWidget::elementStartInteraction(not_null view) { From 85267a921e2287166172128ab06e2ff4a10df383 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 23 Nov 2024 11:11:39 +0400 Subject: [PATCH 37/39] Version 5.8.3. - Ctrl+Click on Reply in groups to reply in another chat. - Fix freeze on forward messages box opening. - Improve phrases in bot subscriptions. - Several bugfixes. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 7 +++++++ 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 8d93b797d..1f5ac3f65 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="5.8.3.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 3d660e2e8..9ae851b64 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 5,8,2,0 - PRODUCTVERSION 5,8,2,0 + FILEVERSION 5,8,3,0 + PRODUCTVERSION 5,8,3,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "5.8.2.0" + VALUE "FileVersion", "5.8.3.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "5.8.2.0" + VALUE "ProductVersion", "5.8.3.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 8ccb08a81..93a1d4d8d 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 5,8,2,0 - PRODUCTVERSION 5,8,2,0 + FILEVERSION 5,8,3,0 + PRODUCTVERSION 5,8,3,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "5.8.2.0" + VALUE "FileVersion", "5.8.3.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "5.8.2.0" + VALUE "ProductVersion", "5.8.3.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 481a4f39b..e631de674 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 5008002; -constexpr auto AppVersionStr = "5.8.2"; +constexpr auto AppVersion = 5008003; +constexpr auto AppVersionStr = "5.8.3"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index ff4691843..66fe3884c 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 5008002 +AppVersion 5008003 AppVersionStrMajor 5.8 -AppVersionStrSmall 5.8.2 -AppVersionStr 5.8.2 +AppVersionStrSmall 5.8.3 +AppVersionStr 5.8.3 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 5.8.2 +AppVersionOriginal 5.8.3 diff --git a/changelog.txt b/changelog.txt index a6704ef0b..22c02f612 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +5.8.3 (23.11.24) + +- Ctrl+Click on Reply in groups to reply in another chat. +- Fix freeze on forward messages box opening. +- Improve phrases in bot subscriptions. +- Several bugfixes. + 5.8.2 (19.11.24) - Improve bottom label color in mini apps. From 23e22650f91c80f30c50b73249a67e045baeee29 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 23 Nov 2024 08:02:44 +0300 Subject: [PATCH 38/39] Added ability to choose filters for channel from its profile. --- Telegram/SourceFiles/window/window_peer_menu.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index b25df4709..25e2ebbc0 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -276,7 +276,7 @@ private: void addInfo(); void addStoryArchive(); void addNewWindow(); - void addToggleFolder(); + void addToggleFolder(bool onlyForChannels); void addToggleUnreadMark(); void addToggleArchive(); void addClearHistory(); @@ -615,12 +615,16 @@ void Filler::addStoryArchive() { }, &st::menuIconStoriesArchiveSection); } -void Filler::addToggleFolder() { +void Filler::addToggleFolder(bool onlyForChannels) { const auto controller = _controller; const auto history = _request.key.history(); if (_topic || !history || !history->owner().chatsFilters().has()) { return; } + if (onlyForChannels + && (!history->peer->isChannel() || !history->inChatList())) { + return; + } _addAction(PeerMenuCallback::Args{ .text = tr::lng_filters_menu_add(tr::now), .handler = nullptr, @@ -1404,7 +1408,7 @@ void Filler::fillContextMenuActions() { addToggleMuteSubmenu(false); addToggleUnreadMark(); addToggleTopicClosed(); - addToggleFolder(); + addToggleFolder(false); if (const auto user = _peer->asUser()) { if (!user->isContact()) { addBlockUser(); @@ -1452,6 +1456,7 @@ void Filler::fillProfileActions() { addToggleTopicClosed(); addViewDiscussion(); addExportChat(); + addToggleFolder(true); addBlockUser(); addReport(); addLeaveChat(); From 3e49b45418a54b41d500bf533e63577a35b845d5 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 23 Nov 2024 10:27:28 +0300 Subject: [PATCH 39/39] Added chats filters list to each exception row in edit chats filter box. --- .../boxes/filters/edit_filter_chats_list.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp index ff0226a87..ce1418618 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp @@ -7,7 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/filters/edit_filter_chats_list.h" +#include "data/data_chat_filters.h" #include "data/data_premium_limits.h" +#include "data/data_session.h" #include "history/history.h" #include "window/window_session_controller.h" #include "lang/lang_keys.h" @@ -125,7 +127,15 @@ Flag TypeRow::flag() const { } ExceptionRow::ExceptionRow(not_null history) : Row(history) { - if (peer()->isSelf()) { + auto filters = QStringList(); + for (const auto &filter : history->owner().chatsFilters().list()) { + if (filter.contains(history) && filter.id()) { + filters << filter.title(); + } + } + if (!filters.isEmpty()) { + setCustomStatus(filters.join(", ")); + } else if (peer()->isSelf()) { setCustomStatus(tr::lng_saved_forward_here(tr::now)); } }