diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 3ff6069205..10eaf2cf9f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4232,6 +4232,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_context_edit_msg" = "Edit"; "lng_context_add_factcheck" = "Add Fact Check"; "lng_context_edit_factcheck" = "Edit Fact Check"; +"lng_context_add_offer" = "Add Offer"; "lng_context_forward_msg" = "Forward"; "lng_context_send_now_msg" = "Send Now"; "lng_context_reschedule" = "Reschedule"; @@ -5363,6 +5364,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_restricted_send_polls_all" = "Sorry, sending polls is not allowed in this group."; "lng_restricted_send_public_polls" = "Sorry, polls with visible votes can't be forwarded to channels."; +"lng_restricted_send_todo_lists" = "Sorry, To-Do lists can't be forwarded to channels."; "lng_restricted_send_paid_media" = "Sorry, paid media can't be sent to this channel."; "lng_restricted_send_voice_messages" = "{user} doesn't accept voice messages."; diff --git a/Telegram/SourceFiles/api/api_suggest_post.cpp b/Telegram/SourceFiles/api/api_suggest_post.cpp index f3bba3c9ae..b28699d618 100644 --- a/Telegram/SourceFiles/api/api_suggest_post.cpp +++ b/Telegram/SourceFiles/api/api_suggest_post.cpp @@ -37,7 +37,7 @@ namespace Api { namespace { void SendApproval( - not_null controller, + std::shared_ptr show, not_null item, TimeId scheduleDate = 0) { using Flag = MTPmessages_ToggleSuggestedPostApproval::Flag; @@ -50,8 +50,7 @@ void SendApproval( } const auto id = item->fullId(); - const auto weak = base::make_weak(controller); - const auto session = &controller->session(); + const auto session = &show->session(); const auto finish = [=] { if (const auto item = session->data().message(id)) { const auto suggestion = item->Get(); @@ -71,15 +70,13 @@ void SendApproval( session->api().applyUpdates(result); finish(); }).fail([=](const MTP::Error &error) { - if (const auto window = weak.get()) { - window->showToast(error.type()); - } + show->showToast(error.type()); finish(); }).send(); } void SendDecline( - not_null controller, + std::shared_ptr show, not_null item, const QString &comment) { using Flag = MTPmessages_ToggleSuggestedPostApproval::Flag; @@ -92,8 +89,7 @@ void SendDecline( } const auto id = item->fullId(); - const auto weak = base::make_weak(controller); - const auto session = &controller->session(); + const auto session = &show->session(); const auto finish = [=] { if (const auto item = session->data().message(id)) { const auto suggestion = item->Get(); @@ -114,21 +110,19 @@ void SendDecline( session->api().applyUpdates(result); finish(); }).fail([=](const MTP::Error &error) { - if (const auto window = weak.get()) { - window->showToast(error.type()); - } + show->showToast(error.type()); finish(); }).send(); } void RequestApprovalDate( - not_null controller, + std::shared_ptr show, not_null item) { const auto id = item->fullId(); const auto weak = std::make_shared>(); const auto done = [=](TimeId result) { - if (const auto item = controller->session().data().message(id)) { - SendApproval(controller, item, result); + if (const auto item = show->session().data().message(id)) { + SendApproval(show, item, result); } if (const auto strong = weak->data()) { strong->closeBox(); @@ -136,19 +130,19 @@ void RequestApprovalDate( }; using namespace HistoryView; auto dateBox = Box(ChooseSuggestTimeBox, SuggestTimeBoxArgs{ - .session = &controller->session(), + .session = &show->session(), .done = done, .mode = SuggestMode::New, }); *weak = dateBox.data(); - controller->uiShow()->show(std::move(dateBox)); + show->show(std::move(dateBox)); } void RequestDeclineComment( - not_null controller, + std::shared_ptr show, not_null item) { const auto id = item->fullId(); - controller->uiShow()->show(Box([=](not_null box) { + show->show(Box([=](not_null box) { const auto callback = std::make_shared>(); Ui::ConfirmBox(box, { .text = tr::lng_suggest_decline_text( @@ -169,11 +163,11 @@ void RequestDeclineComment( reason->setFocusFast(); }); *callback = [=, weak = Ui::MakeWeak(box)] { - const auto item = controller->session().data().message(id); + const auto item = show->session().data().message(id); if (!item) { return; } - SendDecline(controller, item, reason->getLastText().trimmed()); + SendDecline(show, item, reason->getLastText().trimmed()); if (const auto strong = weak.data()) { strong->closeBox(); } @@ -191,24 +185,21 @@ struct SendSuggestState { SendPaymentHelper sendPayment; }; void SendSuggest( - not_null controller, + std::shared_ptr show, not_null item, std::shared_ptr state, Fn modify, Fn done = nullptr, int starsApproved = 0) { const auto suggestion = item->Get(); - if (!suggestion) { - return; - } const auto id = item->fullId(); const auto withPaymentApproved = [=](int stars) { - if (const auto item = controller->session().data().message(id)) { - SendSuggest(controller, item, state, modify, done, stars); + if (const auto item = show->session().data().message(id)) { + SendSuggest(show, item, state, modify, done, stars); } }; const auto checked = state->sendPayment.check( - controller->uiShow(), + show, item->history()->peer, 1, starsApproved, @@ -218,18 +209,23 @@ void SendSuggest( } const auto isForward = item->Get(); auto action = SendAction(item->history()); + action.options.suggest.exists = 1; - action.options.suggest.date = suggestion->date; - action.options.suggest.priceWhole = suggestion->price.whole(); - action.options.suggest.priceNano = suggestion->price.nano(); - action.options.suggest.ton = suggestion->price.ton() ? 1 : 0; + if (suggestion) { + action.options.suggest.date = suggestion->date; + action.options.suggest.priceWhole = suggestion->price.whole(); + action.options.suggest.priceNano = suggestion->price.nano(); + action.options.suggest.ton = suggestion->price.ton() ? 1 : 0; + } + modify(action.options.suggest); + action.options.starsApproved = starsApproved; action.replyTo.monoforumPeerId = item->history()->amMonoforumAdmin() ? item->sublistPeerId() : PeerId(); action.replyTo.messageId = item->fullId(); - modify(action.options.suggest); - controller->session().api().forwardMessages({ + show->session().api().sendAction(action); + show->session().api().forwardMessages({ .items = { item }, .options = (isForward ? Data::ForwardOptions::PreserveInfo @@ -241,7 +237,7 @@ void SendSuggest( } void SuggestApprovalDate( - not_null controller, + std::shared_ptr show, not_null item) { const auto suggestion = item->Get(); if (!suggestion) { @@ -251,7 +247,7 @@ void SuggestApprovalDate( const auto state = std::make_shared(); const auto weak = std::make_shared>(); const auto done = [=](TimeId result) { - const auto item = controller->session().data().message(id); + const auto item = show->session().data().message(id); if (!item) { return; } @@ -261,7 +257,7 @@ void SuggestApprovalDate( } }; SendSuggest( - controller, + show, item, state, [=](SuggestPostOptions &options) { options.date = result; }, @@ -270,27 +266,25 @@ void SuggestApprovalDate( using namespace HistoryView; const auto admin = item->history()->amMonoforumAdmin(); auto dateBox = Box(ChooseSuggestTimeBox, SuggestTimeBoxArgs{ - .session = &controller->session(), + .session = &show->session(), .done = done, .value = suggestion->date, .mode = (admin ? SuggestMode::ChangeAdmin : SuggestMode::ChangeUser), }); *weak = dateBox.data(); - controller->uiShow()->show(std::move(dateBox)); + show->show(std::move(dateBox)); } -void SuggestApprovalPrice( - not_null controller, - not_null item) { - const auto suggestion = item->Get(); - if (!suggestion) { - return; - } +void SuggestOfferForMessage( + std::shared_ptr show, + not_null item, + SuggestPostOptions values, + HistoryView::SuggestMode mode) { const auto id = item->fullId(); const auto state = std::make_shared(); const auto weak = std::make_shared>(); const auto done = [=](SuggestPostOptions result) { - const auto item = controller->session().data().message(id); + const auto item = show->session().data().message(id); if (!item) { return; } @@ -300,28 +294,39 @@ void SuggestApprovalPrice( } }; SendSuggest( - controller, + show, item, state, [=](SuggestPostOptions &options) { options = result; }, close); }; using namespace HistoryView; - const auto admin = item->history()->amMonoforumAdmin(); - auto dateBox = Box(ChooseSuggestPriceBox, SuggestPriceBoxArgs{ - .session = &controller->session(), + auto priceBox = Box(ChooseSuggestPriceBox, SuggestPriceBoxArgs{ + .session = &show->session(), .done = done, - .value = { - .exists = uint32(1), - .priceWhole = uint32(suggestion->price.whole()), - .priceNano = uint32(suggestion->price.nano()), - .ton = uint32(suggestion->price.ton() ? 1 : 0), - .date = suggestion->date, - }, - .mode = (admin ? SuggestMode::ChangeAdmin : SuggestMode::ChangeUser), + .value = values, + .mode = mode, }); - *weak = dateBox.data(); - controller->uiShow()->show(std::move(dateBox)); + *weak = priceBox.data(); + show->show(std::move(priceBox)); +} + +void SuggestApprovalPrice( + std::shared_ptr show, + not_null item) { + const auto suggestion = item->Get(); + if (!suggestion) { + return; + } + const auto admin = item->history()->amMonoforumAdmin(); + using namespace HistoryView; + SuggestOfferForMessage(show, item, { + .exists = uint32(1), + .priceWhole = uint32(suggestion->price.whole()), + .priceNano = uint32(suggestion->price.nano()), + .ton = uint32(suggestion->price.ton() ? 1 : 0), + .date = suggestion->date, + }, admin ? SuggestMode::ChangeAdmin : SuggestMode::ChangeUser); } } // namespace @@ -340,13 +345,14 @@ std::shared_ptr AcceptClickHandler( if (!item) { return; } + const auto show = controller->uiShow(); const auto suggestion = item->Get(); if (!suggestion) { return; } else if (!suggestion->date) { - RequestApprovalDate(controller, item); + RequestApprovalDate(show, item); } else { - SendApproval(controller, item); + SendApproval(show, item); } }); } @@ -360,7 +366,7 @@ std::shared_ptr DeclineClickHandler( if (!controller) { return; } - RequestDeclineComment(controller, item); + RequestDeclineComment(controller->uiShow(), item); }); } @@ -426,16 +432,27 @@ std::shared_ptr SuggestChangesClickHandler( } menu->addAction(tr::lng_suggest_menu_edit_price(tr::now), [=] { if (const auto item = session->data().message(id)) { - SuggestApprovalPrice(window, item); + SuggestApprovalPrice(window->uiShow(), item); } }, &st::menuIconTagSell); menu->addAction(tr::lng_suggest_menu_edit_time(tr::now), [=] { if (const auto item = session->data().message(id)) { - SuggestApprovalDate(window, item); + SuggestApprovalDate(window->uiShow(), item); } }, &st::menuIconSchedule); menu->popup(QCursor::pos()); }); } +void AddOfferToMessage( + std::shared_ptr show, + FullMsgId itemId) { + const auto session = &show->session(); + const auto item = session->data().message(itemId); + if (!item || !HistoryView::CanAddOfferToMessage(item)) { + return; + } + SuggestOfferForMessage(show, item, {}, HistoryView::SuggestMode::New); +} + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_suggest_post.h b/Telegram/SourceFiles/api/api_suggest_post.h index 0584ce7be7..582f0adca0 100644 --- a/Telegram/SourceFiles/api/api_suggest_post.h +++ b/Telegram/SourceFiles/api/api_suggest_post.h @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class ClickHandler; +namespace Main { +class SessionShow; +} // namespace Main + namespace Api { [[nodiscard]] std::shared_ptr AcceptClickHandler( @@ -18,4 +22,8 @@ namespace Api { [[nodiscard]] std::shared_ptr SuggestChangesClickHandler( not_null item); +void AddOfferToMessage( + std::shared_ptr show, + FullMsgId itemId); + } // namespace Api diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 0fe7e05bdf..c5e7a82f6c 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_helpers.h" #include "history/view/controls/history_view_forward_panel.h" #include "history/view/controls/history_view_draft_options.h" +#include "history/view/controls/history_view_suggest_options.h" #include "history/view/media/history_view_sticker.h" #include "history/view/media/history_view_web_page.h" #include "history/view/reactions/history_view_reactions.h" @@ -76,6 +77,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "apiwrap.h" #include "api/api_attached_stickers.h" +#include "api/api_suggest_post.h" #include "api/api_toggling_media.h" #include "api/api_who_reacted.h" #include "api/api_views.h" @@ -2410,9 +2412,6 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { highlightId); }, &st::menuIconViewReplies); } - _menu->addAction(u"Add Offer"_q, [=] { - - }, &st::menuIconDiscussion); const auto t = base::unixtime::now(); const auto editItem = (albumPartItem && albumPartItem->allowsEdit(t)) ? albumPartItem @@ -2812,6 +2811,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { forwardItem(itemId); }, &st::menuIconForward); } + if (HistoryView::CanAddOfferToMessage(item)) { + _menu->addAction(tr::lng_context_add_offer(tr::now), [=] { + Api::AddOfferToMessage(_controller->uiShow(), itemId); + }, &st::menuIconTagSell); + } if (item->canDelete()) { const auto callback = [=] { deleteItem(itemId); }; if (item->isUploading()) { @@ -3062,6 +3066,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { forwardAsGroup(itemId); }, &st::menuIconForward); } + if (HistoryView::CanAddOfferToMessage(item)) { + _menu->addAction(tr::lng_context_add_offer(tr::now), [=] { + Api::AddOfferToMessage(_controller->uiShow(), itemId); + }, &st::menuIconTagSell); + } if (canDelete) { const auto callback = [=] { deleteAsGroup(itemId); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index d3800408c7..8394682656 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -2695,11 +2695,26 @@ Data::SendError HistoryItem::errorTextForForward( } else if (requiresInline && !Data::CanSend(to, kInline)) { const auto forInline = Data::RestrictionError(peer, kInline); return forInline ? forInline : tr::lng_forward_cant(tr::now); - } else if (_media + } else if (const auto specific = errorTextForForwardIgnoreRights(to)) { + return specific; + } else if (!Data::CanSend(to, requiredRight, false)) { + return tr::lng_forward_cant(tr::now); + } + return {}; +} + +Data::SendError HistoryItem::errorTextForForwardIgnoreRights( + not_null to) const { + const auto peer = to->peer(); + if (_media && _media->poll() && _media->poll()->publicVotes() && peer->isBroadcast()) { return tr::lng_restricted_send_public_polls(tr::now); + } else if (_media + && _media->todolist() + && (peer->isBroadcast() || peer->isMonoforum())) { + return tr::lng_restricted_send_todo_lists(tr::now); } else if (_media && _media->invoice() && _media->invoice()->isPaidMedia @@ -2707,8 +2722,6 @@ Data::SendError HistoryItem::errorTextForForward( && peer->isFullLoaded() && !peer->asBroadcast()->canPostPaidMedia()) { return tr::lng_restricted_send_paid_media(tr::now); - } else if (!Data::CanSend(to, requiredRight, false)) { - return tr::lng_forward_cant(tr::now); } return {}; } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 7126716fd5..978624e85e 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -437,6 +437,8 @@ public: [[nodiscard]] bool requiresSendInlineRight() const; [[nodiscard]] Data::SendError errorTextForForward( not_null to) const; + [[nodiscard]] Data::SendError errorTextForForwardIgnoreRights( + not_null to) const; [[nodiscard]] const HistoryMessageTranslation *translation() const; [[nodiscard]] bool translationShowRequiresCheck(LanguageId to) const; bool translationShowRequiresRequest(LanguageId to); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 1347f00640..2dc081cde4 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2200,6 +2200,10 @@ void HistoryWidget::fastShowAtEnd(not_null history) { _pinnedClickedId = FullMsgId(); _minPinnedId = std::nullopt; if (_history->isReadyFor(_showAtMsgId)) { + _history->forgetScrollState(); + if (_migrated) { + _migrated->forgetScrollState(); + } historyLoaded(); } else { firstLoadMessages(); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp b/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp index 076c1f7717..805d5b5bb8 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp @@ -14,7 +14,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_media_types.h" #include "data/data_session.h" +#include "history/history.h" #include "history/history_item.h" +#include "history/history_item_components.h" #include "info/channel_statistics/earn/earn_icons.h" #include "lang/lang_keys.h" #include "main/main_app_config.h" @@ -384,6 +386,19 @@ bool CanEditSuggestedMessage(not_null item) { return !media || media->allowsEditCaption(); } +bool CanAddOfferToMessage(not_null item) { + const auto history = item->history(); + const auto broadcast = history->peer->monoforumBroadcast(); + return broadcast + && !history->amMonoforumAdmin() + && !item->Get() + && !item->groupId() + && item->isRegular() + && !item->isService() + && !item->errorTextForForwardIgnoreRights( + history->owner().history(broadcast)).has_value(); +} + SuggestOptions::SuggestOptions( std::shared_ptr show, not_null peer, diff --git a/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.h b/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.h index 55063b1f97..b16e3e6c0a 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.h @@ -56,6 +56,8 @@ void ChooseSuggestPriceBox( [[nodiscard]] bool CanEditSuggestedMessage(not_null item); +[[nodiscard]] bool CanAddOfferToMessage(not_null item); + class SuggestOptions final { public: SuggestOptions(