diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index dd5746e44..896fdc415 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2212,6 +2212,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_replies_edit_title" = "Edit Shortcut"; "lng_replies_edit_about" = "Edit the name for this shortcut."; "lng_replies_message_placeholder" = "Add a Quick Reply"; +"lng_replies_delete_sure" = "Are you sure you want to delete this quick reply with all its messages?"; +"lng_replies_error_occupied" = "This shortcut is already used."; "lng_greeting_title" = "Greeting Message"; "lng_greeting_about" = "Greet customers when they message you the first time or after a period of no activity."; @@ -3027,6 +3029,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_context_translate_selected" = "Translate Selected Text"; "lng_context_read_hidden" = "read"; "lng_context_read_show" = "show when"; +"lng_context_edit_shortcut" = "Edit Shortcut"; +"lng_context_delete_shortcut" = "Delete Quick Reply"; "lng_add_tag_about" = "Tag this message with an emoji for quick search."; "lng_subscribe_tag_about" = "Organize your Saved Messages with tags. {link}"; diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp index 8b59309bf..d9d75ec52 100644 --- a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp +++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp @@ -496,6 +496,71 @@ Shortcut ShortcutMessages::lookupShortcut(BusinessShortcutId id) const { return i->second; } +void ShortcutMessages::editShortcut( + BusinessShortcutId id, + QString name, + Fn done, + Fn fail) { + name = name.trimmed(); + if (name.isEmpty()) { + fail(QString()); + return; + } + const auto finish = [=] { + const auto i = _shortcuts.list.find(id); + if (i != end(_shortcuts.list)) { + i->second.name = name; + _shortcutsChanged.fire({}); + } + done(); + }; + for (const auto &[existingId, shortcut] : _shortcuts.list) { + if (shortcut.name == name) { + if (existingId == id) { + //done(); + //return; + break; + } else if (_data[existingId].items.empty() && !shortcut.count) { + removeShortcut(existingId); + break; + } else { + fail(u"SHORTCUT_OCCUPIED"_q); + return; + } + } + } + _session->api().request(MTPmessages_EditQuickReplyShortcut( + MTP_int(id), + MTP_string(name) + )).done(finish).fail([=](const MTP::Error &error) { + const auto type = error.type(); + if (type == u"SHORTCUT_ID_INVALID"_q) { + // Not on the server (yet). + finish(); + } else { + fail(type); + } + }).send(); +} + +void ShortcutMessages::removeShortcut(BusinessShortcutId shortcutId) { + auto i = _data.find(shortcutId); + while (i != end(_data)) { + if (i->second.items.empty()) { + _data.erase(i); + } else { + i->second.items.front()->destroy(); + } + i = _data.find(shortcutId); + } + _shortcuts.list.remove(shortcutId); + _shortcutIdChanges.fire({ shortcutId, 0 }); + + _session->api().request(MTPmessages_DeleteQuickReplyShortcut( + MTP_int(shortcutId) + )).send(); +} + void ShortcutMessages::cancelRequest(BusinessShortcutId shortcutId) { const auto j = _requests.find(shortcutId); if (j != end(_requests)) { diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.h b/Telegram/SourceFiles/data/business/data_shortcut_messages.h index 6b7343b57..57c1205f8 100644 --- a/Telegram/SourceFiles/data/business/data_shortcut_messages.h +++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.h @@ -78,6 +78,12 @@ public: [[nodiscard]] rpl::producer shortcutIdChanged() const; [[nodiscard]] BusinessShortcutId emplaceShortcut(QString name); [[nodiscard]] Shortcut lookupShortcut(BusinessShortcutId id) const; + void editShortcut( + BusinessShortcutId id, + QString name, + Fn done, + Fn fail); + void removeShortcut(BusinessShortcutId shortcutId); private: using OwnedItem = std::unique_ptr; diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp b/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp index 6efd528f6..9203981dc 100644 --- a/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp +++ b/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp @@ -10,13 +10,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/downloads/info_downloads_inner_widget.h" #include "info/info_controller.h" #include "info/info_memento.h" +#include "ui/boxes/confirm_box.h" #include "ui/search_field_controller.h" +#include "ui/widgets/menu/menu_add_action_callback.h" #include "ui/widgets/scroll_area.h" #include "data/data_download_manager.h" #include "data/data_user.h" #include "core/application.h" #include "lang/lang_keys.h" #include "styles/style_info.h" +#include "styles/style_layers.h" +#include "styles/style_menu_icons.h" namespace Info::Downloads { @@ -102,6 +106,31 @@ void Widget::selectionAction(SelectionAction action) { _inner->selectionAction(action); } +void Widget::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) { + const auto window = controller()->parentController(); + const auto deleteAll = [=] { + auto &manager = Core::App().downloadManager(); + const auto phrase = tr::lng_downloads_delete_sure_all(tr::now); + const auto added = manager.loadedHasNonCloudFile() + ? QString() + : tr::lng_downloads_delete_in_cloud(tr::now); + const auto deleteSure = [=, &manager](Fn close) { + Ui::PostponeCall(this, close); + manager.deleteAll(); + }; + window->show(Ui::MakeConfirmBox({ + .text = phrase + (added.isEmpty() ? QString() : "\n\n" + added), + .confirmed = deleteSure, + .confirmText = tr::lng_box_delete(tr::now), + .confirmStyle = &st::attentionBoxButton, + })); + }; + addAction( + tr::lng_context_delete_all_files(tr::now), + deleteAll, + &st::menuIconDelete); +} + rpl::producer Widget::title() { return tr::lng_downloads_section(); } diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_widget.h b/Telegram/SourceFiles/info/downloads/info_downloads_widget.h index 3da1a4f79..f79a31466 100644 --- a/Telegram/SourceFiles/info/downloads/info_downloads_widget.h +++ b/Telegram/SourceFiles/info/downloads/info_downloads_widget.h @@ -57,6 +57,8 @@ public: rpl::producer selectedListValue() const override; void selectionAction(SelectionAction action) override; + void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) override; + rpl::producer title() override; private: diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index ab5a0ef5e..adf4f22d2 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_forum_topic.h" #include "data/data_forum.h" #include "main/main_session.h" +#include "window/window_peer_menu.h" #include "styles/style_info.h" #include "styles/style_profile.h" #include "styles/style_layers.h" @@ -263,6 +264,24 @@ QRect ContentWidget::floatPlayerAvailableRect() const { return mapToGlobal(_scroll->geometry()); } +void ContentWidget::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) { + const auto peer = _controller->key().peer(); + const auto topic = _controller->key().topic(); + if (!peer && !topic) { + return; + } + + Window::FillDialogsEntryMenu( + _controller->parentController(), + Dialogs::EntryState{ + .key = (topic + ? Dialogs::Key{ topic } + : Dialogs::Key{ peer->owner().history(peer) }), + .section = Dialogs::EntryState::Section::Profile, + }, + addAction); +} + rpl::producer ContentWidget::selectedListValue() const { return rpl::single(SelectedItems(Storage::SharedMediaType::Photo)); } diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h index b8d1ebe4b..29d947c66 100644 --- a/Telegram/SourceFiles/info/info_content_widget.h +++ b/Telegram/SourceFiles/info/info_content_widget.h @@ -28,6 +28,10 @@ template class PaddingWrap; } // namespace Ui +namespace Ui::Menu { +struct MenuCallback; +} // namespace Ui::Menu + namespace Info::Settings { struct Tag; } // namespace Info::Settings @@ -95,6 +99,7 @@ public: virtual rpl::producer selectedListValue() const; virtual void selectionAction(SelectionAction action) { } + virtual void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction); [[nodiscard]] virtual rpl::producer title() = 0; [[nodiscard]] virtual rpl::producer subtitle() { diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index ddab6ef51..a2c6a781c 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_main.h" #include "settings/settings_premium.h" #include "ui/effects/ripple_animation.h" // MaskByDrawer. +#include "ui/widgets/menu/menu_add_action_callback.h" #include "ui/widgets/discrete_sliders.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" @@ -31,7 +32,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/shortcuts.h" #include "window/window_session_controller.h" #include "window/window_slide_animation.h" -#include "window/window_peer_menu.h" #include "boxes/peer_list_box.h" #include "ui/boxes/confirm_box.h" #include "main/main_session.h" @@ -364,16 +364,24 @@ void WrapWidget::createTopBar() { _controller->searchEnabledByContent(), _controller->takeSearchStartsFocused()); } + _topBar->lower(); + _topBar->resizeToWidth(width()); + _topBar->finishAnimating(); + _topBar->show(); +} + +void WrapWidget::setupTopBarMenuToggle() { + Expects(_content != nullptr); + + if (!_topBar) { + return; + } const auto section = _controller->section(); if (section.type() == Section::Type::Profile - && (wrapValue != Wrap::Side || hasStackHistory())) { + && (wrap() != Wrap::Side || hasStackHistory())) { addTopBarMenuButton(); addProfileCallsButton(); - } else if (section.type() == Section::Type::Settings - && (section.settingsType() - == ::Settings::CloudPasswordEmailConfirmId() - || section.settingsType() == ::Settings::Main::Id() - || section.settingsType() == ::Settings::Chat::Id())) { + } else if (section.type() == Section::Type::Settings) { addTopBarMenuButton(); } else if (section.type() == Section::Type::Downloads) { auto &manager = Core::App().downloadManager(); @@ -399,11 +407,6 @@ void WrapWidget::createTopBar() { } }, _topBar->lifetime()); } - - _topBar->lower(); - _topBar->resizeToWidth(width()); - _topBar->finishAnimating(); - _topBar->show(); } void WrapWidget::checkBeforeClose(Fn close) { @@ -413,11 +416,12 @@ void WrapWidget::checkBeforeClose(Fn close) { void WrapWidget::addTopBarMenuButton() { Expects(_topBar != nullptr); + Expects(_content != nullptr); { const auto guard = gsl::finally([&] { _topBarMenu = nullptr; }); showTopBarMenu(true); - if (_topBarMenu->empty()) { + if (!_topBarMenu) { return; } } @@ -486,65 +490,19 @@ void WrapWidget::showTopBarMenu(bool check) { } }); - const auto addAction = Ui::Menu::CreateAddActionCallback(_topBarMenu); - if (key().isDownloads()) { - addAction( - tr::lng_context_delete_all_files(tr::now), - [=] { deleteAllDownloads(); }, - &st::menuIconDelete); - } else if (const auto peer = key().peer()) { - const auto topic = key().topic(); - Window::FillDialogsEntryMenu( - _controller->parentController(), - Dialogs::EntryState{ - .key = (topic - ? Dialogs::Key{ topic } - : Dialogs::Key{ peer->owner().history(peer) }), - .section = Dialogs::EntryState::Section::Profile, - }, - addAction); - } else if (const auto self = key().settingsSelf()) { - const auto showOther = [=](::Settings::Type type) { - const auto controller = _controller.get(); - _topBarMenu = nullptr; - controller->showSettings(type); - }; - ::Settings::FillMenu( - _controller->parentController(), - _controller->section().settingsType(), - showOther, - addAction); - } else { + _content->fillTopBarMenu(Ui::Menu::CreateAddActionCallback(_topBarMenu)); + if (_topBarMenu->empty()) { _topBarMenu = nullptr; return; + } else if (check) { + return; } _topBarMenu->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight); - if (check) { - return; - } _topBarMenuToggle->setForceRippled(true); _topBarMenu->popup(_topBarMenuToggle->mapToGlobal( st::infoLayerTopBarMenuPosition)); } -void WrapWidget::deleteAllDownloads() { - auto &manager = Core::App().downloadManager(); - const auto phrase = tr::lng_downloads_delete_sure_all(tr::now); - const auto added = manager.loadedHasNonCloudFile() - ? QString() - : tr::lng_downloads_delete_in_cloud(tr::now); - const auto deleteSure = [=, &manager](Fn close) { - Ui::PostponeCall(this, close); - manager.deleteAll(); - }; - _controller->parentController()->show(Ui::MakeConfirmBox({ - .text = phrase + (added.isEmpty() ? QString() : "\n\n" + added), - .confirmed = deleteSure, - .confirmText = tr::lng_box_delete(tr::now), - .confirmStyle = &st::attentionBoxButton, - })); -} - bool WrapWidget::requireTopBarSearch() const { if (!_topBar || !_controller->searchFieldController()) { return false; @@ -619,6 +577,7 @@ void WrapWidget::showContent(object_ptr content) { } void WrapWidget::finishShowContent() { + setupTopBarMenuToggle(); updateContentGeometry(); _content->setIsStackBottom(!hasStackHistory()); if (_topBar) { diff --git a/Telegram/SourceFiles/info/info_wrap_widget.h b/Telegram/SourceFiles/info/info_wrap_widget.h index 0759c0729..db76d4b43 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.h +++ b/Telegram/SourceFiles/info/info_wrap_widget.h @@ -172,6 +172,7 @@ private: not_null memento, const Window::SectionShow ¶ms); void setupTop(); + void setupTopBarMenuToggle(); void createTopBar(); void highlightTopBar(); void setupShortcuts(); @@ -202,7 +203,6 @@ private: void addTopBarMenuButton(); void addProfileCallsButton(); void showTopBarMenu(bool check); - void deleteAllDownloads(); rpl::variable _wrap; std::unique_ptr _controller; diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp index 85878baf1..3524be661 100644 --- a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp +++ b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp @@ -256,6 +256,10 @@ void Widget::selectionAction(SelectionAction action) { _inner->selectionAction(action); } +void Widget::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) { + _inner->fillTopBarMenu(addAction); +} + void Widget::saveState(not_null memento) { memento->setScrollTop(scrollTopSave()); } diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.h b/Telegram/SourceFiles/info/settings/info_settings_widget.h index 7ab7968d5..09fca4419 100644 --- a/Telegram/SourceFiles/info/settings/info_settings_widget.h +++ b/Telegram/SourceFiles/info/settings/info_settings_widget.h @@ -82,6 +82,7 @@ public: rpl::producer selectedListValue() const override; void selectionAction(SelectionAction action) override; + void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) override; private: void saveState(not_null memento); diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp index 180145716..69ac74bee 100644 --- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp +++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp @@ -38,13 +38,11 @@ public: ~AwayMessage(); [[nodiscard]] rpl::producer title() override; - [[nodiscard]] rpl::producer sectionShowOther() override; private: void setupContent(not_null controller); void save(); - rpl::event_stream _showOther; rpl::variable _recipients; rpl::variable _schedule; rpl::variable _enabled; @@ -201,10 +199,6 @@ rpl::producer AwayMessage::title() { return tr::lng_away_title(); } -rpl::producer AwayMessage::sectionShowOther() { - return _showOther.events(); -} - void AwayMessage::setupContent( not_null controller) { using namespace Data; @@ -268,7 +262,7 @@ void AwayMessage::setupContent( create->setClickedCallback([=] { const auto owner = &controller->session().data(); const auto id = owner->shortcutMessages().emplaceShortcut("away"); - _showOther.fire(ShortcutMessagesId(id)); + showOther(ShortcutMessagesId(id)); }); Ui::AddSkip(createInner); Ui::AddDivider(createInner); diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp index 81580801c..96b2615c4 100644 --- a/Telegram/SourceFiles/settings/business/settings_greeting.cpp +++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp @@ -41,7 +41,6 @@ public: ~Greeting(); [[nodiscard]] rpl::producer title() override; - [[nodiscard]] rpl::producer sectionShowOther() override; const Ui::RoundRect *bottomSkipRounding() const { return &_bottomSkipRounding; @@ -53,7 +52,6 @@ private: Ui::RoundRect _bottomSkipRounding; - rpl::event_stream _showOther; rpl::variable _recipients; rpl::variable _noActivityDays; rpl::variable _enabled; @@ -177,10 +175,6 @@ rpl::producer Greeting::title() { return tr::lng_greeting_title(); } -rpl::producer Greeting::sectionShowOther() { - return _showOther.events(); -} - void Greeting::setupContent( not_null controller) { using namespace rpl::mappers; @@ -251,7 +245,7 @@ void Greeting::setupContent( create->setClickedCallback([=] { const auto owner = &controller->session().data(); const auto id = owner->shortcutMessages().emplaceShortcut("hello"); - _showOther.fire(ShortcutMessagesId(id)); + showOther(ShortcutMessagesId(id)); }); Ui::AddSkip(createInner); Ui::AddDivider(createInner); diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp index e13f822a2..431dcd4e1 100644 --- a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp +++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp @@ -37,14 +37,11 @@ public: ~QuickReplies(); [[nodiscard]] rpl::producer title() override; - [[nodiscard]] rpl::producer sectionShowOther() override; private: void setupContent(not_null controller); void save(); - rpl::event_stream _showOther; - }; QuickReplies::QuickReplies( @@ -64,10 +61,6 @@ rpl::producer QuickReplies::title() { return tr::lng_replies_title(); } -rpl::producer QuickReplies::sectionShowOther() { - return _showOther.events(); -} - void QuickReplies::setupContent( not_null controller) { using namespace rpl::mappers; @@ -94,41 +87,13 @@ void QuickReplies::setupContent( const auto messages = &owner->shortcutMessages(); add->setClickedCallback([=] { - controller->show(Box([=](not_null box) { - box->setTitle(tr::lng_replies_add_title()); - box->addRow(object_ptr( - box, - tr::lng_replies_add_shortcut(), - st::settingsAddReplyLabel)); - const auto field = box->addRow(object_ptr( - box, - st::settingsAddReplyField, - tr::lng_replies_add_placeholder(), - QString())); - box->setFocusCallback([=] { - field->setFocusFast(); - }); - - const auto submit = [=] { - const auto weak = Ui::MakeWeak(box); - const auto name = field->getLastText().trimmed(); - if (name.isEmpty()) { - field->showError(); - } else { - const auto id = messages->emplaceShortcut(name); - _showOther.fire(ShortcutMessagesId(id)); - } - if (const auto strong = weak.data()) { - strong->closeBox(); - } - }; - field->submits( - ) | rpl::start_with_next(submit, field->lifetime()); - box->addButton(tr::lng_settings_save(), submit); - box->addButton(tr::lng_cancel(), [=] { - box->closeBox(); - }); - })); + const auto submit = [=](QString name, Fn close) { + const auto id = messages->emplaceShortcut(name); + showOther(ShortcutMessagesId(id)); + close(); + }; + controller->show( + Box(EditShortcutNameBox, QString(), crl::guard(this, submit))); }); Ui::AddSkip(content); @@ -140,24 +105,33 @@ void QuickReplies::setupContent( rpl::single(rpl::empty) | rpl::then( messages->shortcutsChanged() ) | rpl::start_with_next([=] { - while (inner->count()) { - delete inner->widgetAt(0); - } + auto old = inner->count(); + const auto &shortcuts = messages->shortcuts(); auto i = 0; - for (const auto &shortcut : shortcuts.list) { - const auto name = shortcut.second.name; + for (const auto &[_, shortcut] : shortcuts.list) { + if (!shortcut.count) { + continue; + } + const auto name = shortcut.name; AddButtonWithLabel( inner, rpl::single('/' + name), tr::lng_forum_messages( lt_count, - rpl::single(1. * shortcut.second.count)), + rpl::single(1. * shortcut.count)), st::settingsButtonNoIcon )->setClickedCallback([=] { const auto id = messages->emplaceShortcut(name); - _showOther.fire(ShortcutMessagesId(id)); + showOther(ShortcutMessagesId(id)); }); + if (old) { + delete inner->widgetAt(0); + --old; + } + } + while (old--) { + delete inner->widgetAt(0); } }, content->lifetime()); @@ -173,4 +147,48 @@ Type QuickRepliesId() { return QuickReplies::Id(); } +void EditShortcutNameBox( + not_null box, + QString name, + Fn)> submit) { + name = name.trimmed(); + const auto editing = !name.isEmpty(); + box->setTitle(editing + ? tr::lng_replies_edit_title() + : tr::lng_replies_add_title()); + box->addRow(object_ptr( + box, + (editing + ? tr::lng_replies_edit_about() + : tr::lng_replies_add_shortcut()), + st::settingsAddReplyLabel)); + const auto field = box->addRow(object_ptr( + box, + st::settingsAddReplyField, + tr::lng_replies_add_placeholder(), + name)); + box->setFocusCallback([=] { + field->setFocusFast(); + }); + + const auto callback = [=] { + const auto name = field->getLastText().trimmed(); + if (name.isEmpty()) { + field->showError(); + } else { + submit(name, [weak = Ui::MakeWeak(box)] { + if (const auto strong = weak.data()) { + strong->closeBox(); + } + }); + } + }; + field->submits( + ) | rpl::start_with_next(callback, field->lifetime()); + box->addButton(tr::lng_settings_save(), callback); + box->addButton(tr::lng_cancel(), [=] { + box->closeBox(); + }); +} + } // namespace Settings diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.h b/Telegram/SourceFiles/settings/business/settings_quick_replies.h index 80cc2f129..4765c4f59 100644 --- a/Telegram/SourceFiles/settings/business/settings_quick_replies.h +++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.h @@ -9,8 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_type.h" +namespace Ui { +class GenericBox; +} // namespace Ui + namespace Settings { [[nodiscard]] Type QuickRepliesId(); +void EditShortcutNameBox( + not_null box, + QString name, + Fn)> submit); + } // namespace Settings diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp index e2186a172..0681b078a 100644 --- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp +++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp @@ -36,17 +36,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_numbers_animation.h" #include "main/main_session.h" #include "menu/menu_send.h" +#include "settings/business/settings_quick_replies.h" #include "settings/business/settings_recipients_helper.h" #include "storage/localimageloader.h" #include "storage/storage_account.h" #include "storage/storage_media_prepare.h" #include "storage/storage_shared_media.h" +#include "ui/boxes/confirm_box.h" #include "ui/chat/attach/attach_send_files_way.h" #include "ui/chat/chat_style.h" #include "ui/chat/chat_theme.h" #include "ui/controls/jump_down_button.h" #include "ui/text/format_values.h" #include "ui/text/text_utilities.h" +#include "ui/widgets/menu/menu_add_action_callback.h" #include "ui/widgets/scroll_area.h" #include "window/themes/window_theme.h" #include "window/section_widget.h" @@ -54,6 +57,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_boxes.h" #include "styles/style_chat_helpers.h" #include "styles/style_chat.h" +#include "styles/style_menu_icons.h" +#include "styles/style_layers.h" namespace Settings { namespace { @@ -84,6 +89,7 @@ public: rpl::producer selectedListValue() override; void selectionAction(Info::SelectionAction action) override; + void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) override; bool paintOuter( not_null outer, @@ -268,9 +274,9 @@ private: }; -struct Factory : AbstractSectionFactory { +struct Factory final : AbstractSectionFactory { explicit Factory(BusinessShortcutId shortcutId) - : shortcutId(shortcutId) { + : shortcutId(shortcutId) { } object_ptr create( @@ -426,6 +432,56 @@ void ShortcutMessages::selectionAction(Info::SelectionAction action) { Unexpected("Action in ShortcutMessages::selectionAction."); } +void ShortcutMessages::fillTopBarMenu( + const Ui::Menu::MenuCallback &addAction) { + const auto owner = &_controller->session().data(); + const auto messages = &owner->shortcutMessages(); + + addAction(tr::lng_context_edit_shortcut(tr::now), [=] { + const auto submit = [=](QString name, Fn close) { + const auto id = _shortcutId.current(); + const auto error = [=](QString text) { + if (!text.isEmpty()) { + _controller->showToast((text == u"SHORTCUT_OCCUPIED"_q) + ? tr::lng_replies_error_occupied(tr::now) + : text); + } + }; + messages->editShortcut(id, name, close, crl::guard(this, error)); + }; + const auto name = _shortcut.current(); + _controller->show( + Box(EditShortcutNameBox, name, crl::guard(this, submit))); + }, &st::menuIconEdit); + + const auto justDelete = crl::guard(this, [=] { + messages->removeShortcut(_shortcutId.current()); + }); + const auto confirmDeleteShortcut = [=] { + const auto slice = messages->list(_shortcutId.current()); + if (slice.fullCount == 0) { + justDelete(); + } else { + const auto confirmed = [=](Fn close) { + justDelete(); + close(); + }; + _controller->show(Ui::MakeConfirmBox({ + .text = { tr::lng_replies_delete_sure() }, + .confirmed = confirmed, + .confirmText = tr::lng_box_delete(), + .confirmStyle = &st::attentionBoxButton, + })); + } + }; + addAction({ + .text = tr::lng_context_delete_shortcut(tr::now), + .handler = crl::guard(this, confirmDeleteShortcut), + .icon = &st::menuIconDeleteAttention, + .isAttention = true, + }); +} + bool ShortcutMessages::paintOuter( not_null outer, int maxVisibleHeight, diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp index 60596c5bf..19bb96969 100644 --- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp +++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp @@ -7,10 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "settings/cloud_password/settings_cloud_password_email_confirm.h" +#include "apiwrap.h" #include "api/api_cloud_password.h" #include "base/unixtime.h" #include "core/core_cloud_password.h" #include "lang/lang_keys.h" +#include "main/main_session.h" #include "settings/cloud_password/settings_cloud_password_common.h" #include "settings/cloud_password/settings_cloud_password_email.h" #include "settings/cloud_password/settings_cloud_password_hint.h" @@ -20,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/vertical_list.h" #include "ui/boxes/confirm_box.h" #include "ui/text/format_values.h" +#include "ui/widgets/menu/menu_add_action_callback.h" #include "ui/widgets/buttons.h" #include "ui/widgets/sent_code_field.h" #include "ui/wrap/padding_wrap.h" @@ -27,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "styles/style_boxes.h" #include "styles/style_layers.h" +#include "styles/style_menu_icons.h" #include "styles/style_settings.h" /* @@ -55,6 +59,10 @@ public: using TypedAbstractStep::TypedAbstractStep; [[nodiscard]] rpl::producer title() override; + + [[nodiscard]] void fillTopBarMenu( + const Ui::Menu::MenuCallback &addAction) override; + void setupContent(); protected: @@ -69,6 +77,20 @@ rpl::producer EmailConfirm::title() { return tr::lng_settings_cloud_password_email_title(); } +void EmailConfirm::fillTopBarMenu( + const Ui::Menu::MenuCallback &addAction) { + const auto api = &controller()->session().api(); + if (const auto state = api->cloudPassword().stateCurrent()) { + if (state->unconfirmedPattern.isEmpty()) { + return; + } + } + addAction( + tr::lng_settings_password_abort(tr::now), + [=] { api->cloudPassword().clearUnconfirmedPassword(); }, + &st::menuIconCancel); +} + rpl::producer> EmailConfirm::removeTypes() { return rpl::single(std::vector{ CloudPasswordStartId(), diff --git a/Telegram/SourceFiles/settings/settings_advanced.cpp b/Telegram/SourceFiles/settings/settings_advanced.cpp index d23b2a6d6..b833383f5 100644 --- a/Telegram/SourceFiles/settings/settings_advanced.cpp +++ b/Telegram/SourceFiles/settings/settings_advanced.cpp @@ -978,10 +978,6 @@ rpl::producer Advanced::title() { return tr::lng_settings_advanced(); } -rpl::producer Advanced::sectionShowOther() { - return _showOther.events(); -} - void Advanced::setupContent(not_null controller) { const auto content = Ui::CreateChild(this); @@ -1033,9 +1029,7 @@ void Advanced::setupContent(not_null controller) { AddSkip(content); AddDivider(content); AddSkip(content); - SetupExport(controller, content, [=](Type type) { - _showOther.fire_copy(type); - }); + SetupExport(controller, content, showOtherMethod()); Ui::ResizeFitChild(this, content); } diff --git a/Telegram/SourceFiles/settings/settings_advanced.h b/Telegram/SourceFiles/settings/settings_advanced.h index fce804253..1d46797b4 100644 --- a/Telegram/SourceFiles/settings/settings_advanced.h +++ b/Telegram/SourceFiles/settings/settings_advanced.h @@ -54,13 +54,9 @@ public: [[nodiscard]] rpl::producer title() override; - rpl::producer sectionShowOther() override; - private: void setupContent(not_null controller); - rpl::event_stream _showOther; - }; } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp index e3aa43129..0881925b5 100644 --- a/Telegram/SourceFiles/settings/settings_business.cpp +++ b/Telegram/SourceFiles/settings/settings_business.cpp @@ -296,7 +296,6 @@ public: void setStepDataReference(std::any &data) override; [[nodiscard]] rpl::producer<> sectionShowBack() override final; - [[nodiscard]] rpl::producer sectionShowOther() override; private: void setupContent(); @@ -311,8 +310,6 @@ private: Fn _setPaused; std::shared_ptr _radioGroup; - rpl::event_stream _showOther; - rpl::event_stream<> _showBack; rpl::event_stream<> _showFinished; rpl::variable _buttonText; @@ -341,10 +338,6 @@ rpl::producer<> Business::sectionShowBack() { return _showBack.events(); } -rpl::producer Business::sectionShowOther() { - return _showOther.events(); -} - void Business::setStepDataReference(std::any &data) { using namespace Info::Settings; const auto my = std::any_cast(&data); @@ -365,7 +358,7 @@ void Business::setupContent() { Ui::AddSkip(content, st::settingsFromFileTop); AddBusinessSummary(content, _controller, [=](BusinessFeature feature) { - _showOther.fire([&] { + showOther([&] { switch (feature) { case BusinessFeature::AwayMessages: return AwayMessageId(); case BusinessFeature::OpeningHours: return WorkingHoursId(); diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 3da8d85db..d4f1e8b7e 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/painter.h" #include "ui/vertical_list.h" #include "ui/ui_utility.h" +#include "ui/widgets/menu/menu_add_action_callback.h" #include "history/view/history_view_quick_action.h" #include "lang/lang_keys.h" #include "export/export_manager.h" @@ -1732,7 +1733,8 @@ void SetupSupport( } Chat::Chat(QWidget *parent, not_null controller) -: Section(parent) { +: Section(parent) +, _controller(controller) { setupContent(controller); } @@ -1740,6 +1742,14 @@ rpl::producer Chat::title() { return tr::lng_settings_section_chat_settings(); } +void Chat::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) { + const auto window = &_controller->window(); + addAction( + tr::lng_settings_bg_theme_create(tr::now), + [=] { window->show(Box(Window::Theme::CreateBox, window)); }, + &st::menuIconChangeColors); +} + void Chat::setupContent(not_null controller) { const auto content = Ui::CreateChild(this); diff --git a/Telegram/SourceFiles/settings/settings_chat.h b/Telegram/SourceFiles/settings/settings_chat.h index 91cd93101..d1256de96 100644 --- a/Telegram/SourceFiles/settings/settings_chat.h +++ b/Telegram/SourceFiles/settings/settings_chat.h @@ -44,9 +44,14 @@ public: [[nodiscard]] rpl::producer title() override; + [[nodiscard]] void fillTopBarMenu( + const Ui::Menu::MenuCallback &addAction) override; + private: void setupContent(not_null controller); + const not_null _controller; + }; } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h index e401ad419..47bf52774 100644 --- a/Telegram/SourceFiles/settings/settings_common.h +++ b/Telegram/SourceFiles/settings/settings_common.h @@ -102,6 +102,9 @@ public: } virtual void selectionAction(Info::SelectionAction action) { } + [[nodiscard]] virtual void fillTopBarMenu( + const Ui::Menu::MenuCallback &addAction) { + } virtual bool paintOuter( not_null outer, diff --git a/Telegram/SourceFiles/settings/settings_common_session.cpp b/Telegram/SourceFiles/settings/settings_common_session.cpp index 047c2c622..8fac1a399 100644 --- a/Telegram/SourceFiles/settings/settings_common_session.cpp +++ b/Telegram/SourceFiles/settings/settings_common_session.cpp @@ -34,48 +34,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Settings { -void FillMenu( - not_null controller, - Type type, - Fn showOther, - Ui::Menu::MenuCallback addAction) { - const auto window = &controller->window(); - if (type == Chat::Id()) { - addAction( - tr::lng_settings_bg_theme_create(tr::now), - [=] { window->show(Box(Window::Theme::CreateBox, window)); }, - &st::menuIconChangeColors); - } else if (type == CloudPasswordEmailConfirmId()) { - const auto api = &controller->session().api(); - if (const auto state = api->cloudPassword().stateCurrent()) { - if (state->unconfirmedPattern.isEmpty()) { - return; - } - } - addAction( - tr::lng_settings_password_abort(tr::now), - [=] { api->cloudPassword().clearUnconfirmedPassword(); }, - &st::menuIconCancel); - } else { - const auto &list = Core::App().domain().accounts(); - if (list.size() < Core::App().domain().maxAccounts()) { - addAction(tr::lng_menu_add_account(tr::now), [=] { - Core::App().domain().addActivated(MTP::Environment{}); - }, &st::menuIconAddAccount); - } - if (!controller->session().supportMode()) { - addAction( - tr::lng_settings_information(tr::now), - [=] { showOther(Information::Id()); }, - &st::menuIconInfo); - } - addAction({ - .text = tr::lng_settings_logout(tr::now), - .handler = [=] { window->showLogoutConfirmation(); }, - .icon = &st::menuIconLeaveAttention, - .isAttention = true, - }); - } +bool HasMenu(Type type) { + return (type == ::Settings::CloudPasswordEmailConfirmId()) + || (type == Main::Id()) + || (type == Chat::Id()); } } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_common_session.h b/Telegram/SourceFiles/settings/settings_common_session.h index 16a03b9e4..2ebd1319c 100644 --- a/Telegram/SourceFiles/settings/settings_common_session.h +++ b/Telegram/SourceFiles/settings/settings_common_session.h @@ -54,6 +54,7 @@ struct SectionFactory : AbstractSectionFactory { static const auto result = std::make_shared(); return result; } + }; template @@ -67,12 +68,24 @@ public: [[nodiscard]] Type id() const final override { return Id(); } + + [[nodiscard]] rpl::producer sectionShowOther() final override { + return _showOtherRequests.events(); + } + [[nodiscard]] void showOther(Type type) { + _showOtherRequests.fire_copy(type); + } + [[nodiscard]] Fn showOtherMethod() { + return crl::guard(this, [=](Type type) { + showOther(type); + }); + } + +private: + rpl::event_stream _showOtherRequests; + }; -void FillMenu( - not_null controller, - Type type, - Fn showOther, - Ui::Menu::MenuCallback addAction); +bool HasMenu(Type type); } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_local_passcode.cpp b/Telegram/SourceFiles/settings/settings_local_passcode.cpp index d06726cb4..9a766f16d 100644 --- a/Telegram/SourceFiles/settings/settings_local_passcode.cpp +++ b/Telegram/SourceFiles/settings/settings_local_passcode.cpp @@ -383,7 +383,6 @@ public: [[nodiscard]] rpl::producer title() override; void showFinished() override; - [[nodiscard]] rpl::producer sectionShowOther() override; [[nodiscard]] rpl::producer<> sectionShowBack() override; [[nodiscard]] rpl::producer> removeFromStack() override; @@ -399,7 +398,6 @@ private: rpl::variable _isBottomFillerShown; rpl::event_stream<> _showFinished; - rpl::event_stream _showOther; rpl::event_stream<> _showBack; }; @@ -445,7 +443,7 @@ void LocalPasscodeManage::setupContent() { st::settingsButton, { &st::menuIconLock } )->addClickHandler([=] { - _showOther.fire(LocalPasscodeChange::Id()); + showOther(LocalPasscodeChange::Id()); }); auto autolockLabel = state->autoLockBoxClosing.events_starting_with( @@ -542,10 +540,6 @@ void LocalPasscodeManage::showFinished() { _showFinished.fire({}); } -rpl::producer LocalPasscodeManage::sectionShowOther() { - return _showOther.events(); -} - rpl::producer<> LocalPasscodeManage::sectionShowBack() { return _showBack.events(); } diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp index 854c94b05..4dd57db8f 100644 --- a/Telegram/SourceFiles/settings/settings_main.cpp +++ b/Telegram/SourceFiles/settings/settings_main.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "settings/settings_main.h" +#include "core/application.h" #include "settings/settings_business.h" #include "settings/settings_codes.h" #include "settings/settings_chat.h" @@ -28,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/padding_wrap.h" +#include "ui/widgets/menu/menu_add_action_callback.h" #include "ui/widgets/labels.h" #include "ui/widgets/continuous_sliders.h" #include "ui/widgets/buttons.h" @@ -49,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "main/main_session_settings.h" #include "main/main_account.h" +#include "main/main_domain.h" #include "main/main_app_config.h" #include "apiwrap.h" #include "api/api_peer_photo.h" @@ -691,6 +694,28 @@ rpl::producer Main::title() { return tr::lng_menu_settings(); } +void Main::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) { + const auto &list = Core::App().domain().accounts(); + if (list.size() < Core::App().domain().maxAccounts()) { + addAction(tr::lng_menu_add_account(tr::now), [=] { + Core::App().domain().addActivated(MTP::Environment{}); + }, &st::menuIconAddAccount); + } + if (!_controller->session().supportMode()) { + addAction( + tr::lng_settings_information(tr::now), + [=] { showOther(Information::Id()); }, + &st::menuIconInfo); + } + const auto window = &_controller->window(); + addAction({ + .text = tr::lng_settings_logout(tr::now), + .handler = [=] { window->showLogoutConfirmation(); }, + .icon = &st::menuIconLeaveAttention, + .isAttention = true, + }); +} + void Main::keyPressEvent(QKeyEvent *e) { crl::on_main(this, [=, text = e->text()]{ CodesFeedString(_controller, text); @@ -706,18 +731,14 @@ void Main::setupContent(not_null controller) { controller, controller->session().user())); - SetupSections(controller, content, [=](Type type) { - _showOther.fire_copy(type); - }); + SetupSections(controller, content, showOtherMethod()); if (HasInterfaceScale()) { Ui::AddDivider(content); Ui::AddSkip(content); SetupInterfaceScale(&controller->window(), content); Ui::AddSkip(content); } - SetupPremium(controller, content, [=](Type type) { - _showOther.fire_copy(type); - }); + SetupPremium(controller, content, showOtherMethod()); SetupHelp(controller, content); Ui::ResizeFitChild(this, content); @@ -730,8 +751,4 @@ void Main::setupContent(not_null controller) { controller->session().data().cloudThemes().refresh(); } -rpl::producer Main::sectionShowOther() { - return _showOther.events(); -} - } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_main.h b/Telegram/SourceFiles/settings/settings_main.h index e3d26d02b..4356a49b3 100644 --- a/Telegram/SourceFiles/settings/settings_main.h +++ b/Telegram/SourceFiles/settings/settings_main.h @@ -38,7 +38,8 @@ public: [[nodiscard]] rpl::producer title() override; - rpl::producer sectionShowOther() override; + [[nodiscard]] void fillTopBarMenu( + const Ui::Menu::MenuCallback &addAction) override; protected: void keyPressEvent(QKeyEvent *e) override; @@ -47,7 +48,6 @@ private: void setupContent(not_null controller); const not_null _controller; - rpl::event_stream _showOther; }; diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index 4f54017c9..ddfb849f8 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -1281,17 +1281,11 @@ rpl::producer Notifications::title() { return tr::lng_settings_section_notify(); } -rpl::producer Notifications::sectionShowOther() { - return _showOther.events(); -} - void Notifications::setupContent( not_null controller) { const auto content = Ui::CreateChild(this); - SetupNotifications(controller, content, [=](Type type) { - _showOther.fire_copy(type); - }); + SetupNotifications(controller, content, showOtherMethod()); Ui::ResizeFitChild(this, content); } diff --git a/Telegram/SourceFiles/settings/settings_notifications.h b/Telegram/SourceFiles/settings/settings_notifications.h index 1911a48af..f6df4d2e5 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.h +++ b/Telegram/SourceFiles/settings/settings_notifications.h @@ -19,13 +19,9 @@ public: [[nodiscard]] rpl::producer title() override; - rpl::producer sectionShowOther() override; - private: void setupContent(not_null controller); - rpl::event_stream _showOther; - }; } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index ed4894275..6d16cac01 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -968,10 +968,6 @@ rpl::producer PrivacySecurity::title() { return tr::lng_settings_section_privacy(); } -rpl::producer PrivacySecurity::sectionShowOther() { - return _showOther.events(); -} - void PrivacySecurity::setupContent( not_null controller) { const auto content = Ui::CreateChild(this); @@ -982,9 +978,7 @@ void PrivacySecurity::setupContent( return rpl::duplicate(updateOnTick); }; - SetupSecurity(controller, content, trigger(), [=](Type type) { - _showOther.fire_copy(type); - }); + SetupSecurity(controller, content, trigger(), showOtherMethod()); SetupPrivacy(controller, content, trigger()); #if !defined OS_MAC_STORE && !defined OS_WIN_STORE SetupSensitiveContent(controller, content, trigger()); diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.h b/Telegram/SourceFiles/settings/settings_privacy_security.h index 4fedfce81..01922b01e 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.h +++ b/Telegram/SourceFiles/settings/settings_privacy_security.h @@ -47,13 +47,9 @@ public: [[nodiscard]] rpl::producer title() override; - rpl::producer sectionShowOther() override; - private: void setupContent(not_null controller); - rpl::event_stream _showOther; - }; } // namespace Settings