Shortcuts edit / delete menu.

This commit is contained in:
John Preston 2024-02-28 14:55:28 +04:00
parent f086203d25
commit aad8e989d8
32 changed files with 391 additions and 237 deletions

View file

@ -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}";

View file

@ -496,6 +496,71 @@ Shortcut ShortcutMessages::lookupShortcut(BusinessShortcutId id) const {
return i->second;
}
void ShortcutMessages::editShortcut(
BusinessShortcutId id,
QString name,
Fn<void()> done,
Fn<void(QString)> 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)) {

View file

@ -78,6 +78,12 @@ public:
[[nodiscard]] rpl::producer<ShortcutIdChange> shortcutIdChanged() const;
[[nodiscard]] BusinessShortcutId emplaceShortcut(QString name);
[[nodiscard]] Shortcut lookupShortcut(BusinessShortcutId id) const;
void editShortcut(
BusinessShortcutId id,
QString name,
Fn<void()> done,
Fn<void(QString)> fail);
void removeShortcut(BusinessShortcutId shortcutId);
private:
using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;

View file

@ -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<void()> 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<QString> Widget::title() {
return tr::lng_downloads_section();
}

View file

@ -57,6 +57,8 @@ public:
rpl::producer<SelectedItems> selectedListValue() const override;
void selectionAction(SelectionAction action) override;
void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) override;
rpl::producer<QString> title() override;
private:

View file

@ -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<SelectedItems> ContentWidget::selectedListValue() const {
return rpl::single(SelectedItems(Storage::SharedMediaType::Photo));
}

View file

@ -28,6 +28,10 @@ template <typename Widget>
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<SelectedItems> selectedListValue() const;
virtual void selectionAction(SelectionAction action) {
}
virtual void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction);
[[nodiscard]] virtual rpl::producer<QString> title() = 0;
[[nodiscard]] virtual rpl::producer<QString> subtitle() {

View file

@ -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<void()> close) {
@ -413,11 +416,12 @@ void WrapWidget::checkBeforeClose(Fn<void()> 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<void()> 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<ContentWidget> content) {
}
void WrapWidget::finishShowContent() {
setupTopBarMenuToggle();
updateContentGeometry();
_content->setIsStackBottom(!hasStackHistory());
if (_topBar) {

View file

@ -172,6 +172,7 @@ private:
not_null<ContentMemento*> memento,
const Window::SectionShow &params);
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> _wrap;
std::unique_ptr<Controller> _controller;

View file

@ -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) {
memento->setScrollTop(scrollTopSave());
}

View file

@ -82,6 +82,7 @@ public:
rpl::producer<SelectedItems> selectedListValue() const override;
void selectionAction(SelectionAction action) override;
void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) override;
private:
void saveState(not_null<Memento*> memento);

View file

@ -38,13 +38,11 @@ public:
~AwayMessage();
[[nodiscard]] rpl::producer<QString> title() override;
[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
private:
void setupContent(not_null<Window::SessionController*> controller);
void save();
rpl::event_stream<Type> _showOther;
rpl::variable<Data::BusinessRecipients> _recipients;
rpl::variable<Data::AwaySchedule> _schedule;
rpl::variable<bool> _enabled;
@ -201,10 +199,6 @@ rpl::producer<QString> AwayMessage::title() {
return tr::lng_away_title();
}
rpl::producer<Type> AwayMessage::sectionShowOther() {
return _showOther.events();
}
void AwayMessage::setupContent(
not_null<Window::SessionController*> 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);

View file

@ -41,7 +41,6 @@ public:
~Greeting();
[[nodiscard]] rpl::producer<QString> title() override;
[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
const Ui::RoundRect *bottomSkipRounding() const {
return &_bottomSkipRounding;
@ -53,7 +52,6 @@ private:
Ui::RoundRect _bottomSkipRounding;
rpl::event_stream<Type> _showOther;
rpl::variable<Data::BusinessRecipients> _recipients;
rpl::variable<int> _noActivityDays;
rpl::variable<bool> _enabled;
@ -177,10 +175,6 @@ rpl::producer<QString> Greeting::title() {
return tr::lng_greeting_title();
}
rpl::producer<Type> Greeting::sectionShowOther() {
return _showOther.events();
}
void Greeting::setupContent(
not_null<Window::SessionController*> 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);

View file

@ -37,14 +37,11 @@ public:
~QuickReplies();
[[nodiscard]] rpl::producer<QString> title() override;
[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
private:
void setupContent(not_null<Window::SessionController*> controller);
void save();
rpl::event_stream<Type> _showOther;
};
QuickReplies::QuickReplies(
@ -64,10 +61,6 @@ rpl::producer<QString> QuickReplies::title() {
return tr::lng_replies_title();
}
rpl::producer<Type> QuickReplies::sectionShowOther() {
return _showOther.events();
}
void QuickReplies::setupContent(
not_null<Window::SessionController*> controller) {
using namespace rpl::mappers;
@ -94,41 +87,13 @@ void QuickReplies::setupContent(
const auto messages = &owner->shortcutMessages();
add->setClickedCallback([=] {
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
box->setTitle(tr::lng_replies_add_title());
box->addRow(object_ptr<Ui::FlatLabel>(
box,
tr::lng_replies_add_shortcut(),
st::settingsAddReplyLabel));
const auto field = box->addRow(object_ptr<Ui::InputField>(
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<void()> 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<Ui::GenericBox*> box,
QString name,
Fn<void(QString, Fn<void()>)> 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<Ui::FlatLabel>(
box,
(editing
? tr::lng_replies_edit_about()
: tr::lng_replies_add_shortcut()),
st::settingsAddReplyLabel));
const auto field = box->addRow(object_ptr<Ui::InputField>(
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

View file

@ -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<Ui::GenericBox*> box,
QString name,
Fn<void(QString, Fn<void()>)> submit);
} // namespace Settings

View file

@ -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<Info::SelectedItems> selectedListValue() override;
void selectionAction(Info::SelectionAction action) override;
void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) override;
bool paintOuter(
not_null<QWidget*> outer,
@ -268,9 +274,9 @@ private:
};
struct Factory : AbstractSectionFactory {
struct Factory final : AbstractSectionFactory {
explicit Factory(BusinessShortcutId shortcutId)
: shortcutId(shortcutId) {
: shortcutId(shortcutId) {
}
object_ptr<AbstractSection> 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<void()> 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<void()> 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<QWidget*> outer,
int maxVisibleHeight,

View file

@ -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<QString> title() override;
[[nodiscard]] void fillTopBarMenu(
const Ui::Menu::MenuCallback &addAction) override;
void setupContent();
protected:
@ -69,6 +77,20 @@ rpl::producer<QString> 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<std::vector<Type>> EmailConfirm::removeTypes() {
return rpl::single(std::vector<Type>{
CloudPasswordStartId(),

View file

@ -978,10 +978,6 @@ rpl::producer<QString> Advanced::title() {
return tr::lng_settings_advanced();
}
rpl::producer<Type> Advanced::sectionShowOther() {
return _showOther.events();
}
void Advanced::setupContent(not_null<Window::SessionController*> controller) {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
@ -1033,9 +1029,7 @@ void Advanced::setupContent(not_null<Window::SessionController*> controller) {
AddSkip(content);
AddDivider(content);
AddSkip(content);
SetupExport(controller, content, [=](Type type) {
_showOther.fire_copy(type);
});
SetupExport(controller, content, showOtherMethod());
Ui::ResizeFitChild(this, content);
}

View file

@ -54,13 +54,9 @@ public:
[[nodiscard]] rpl::producer<QString> title() override;
rpl::producer<Type> sectionShowOther() override;
private:
void setupContent(not_null<Window::SessionController*> controller);
rpl::event_stream<Type> _showOther;
};
} // namespace Settings

View file

@ -296,7 +296,6 @@ public:
void setStepDataReference(std::any &data) override;
[[nodiscard]] rpl::producer<> sectionShowBack() override final;
[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
private:
void setupContent();
@ -311,8 +310,6 @@ private:
Fn<void(bool)> _setPaused;
std::shared_ptr<Ui::RadiobuttonGroup> _radioGroup;
rpl::event_stream<Type> _showOther;
rpl::event_stream<> _showBack;
rpl::event_stream<> _showFinished;
rpl::variable<QString> _buttonText;
@ -341,10 +338,6 @@ rpl::producer<> Business::sectionShowBack() {
return _showBack.events();
}
rpl::producer<Type> Business::sectionShowOther() {
return _showOther.events();
}
void Business::setStepDataReference(std::any &data) {
using namespace Info::Settings;
const auto my = std::any_cast<SectionCustomTopBarData>(&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();

View file

@ -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<Window::SessionController*> controller)
: Section(parent) {
: Section(parent)
, _controller(controller) {
setupContent(controller);
}
@ -1740,6 +1742,14 @@ rpl::producer<QString> 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<Window::SessionController*> controller) {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);

View file

@ -44,9 +44,14 @@ public:
[[nodiscard]] rpl::producer<QString> title() override;
[[nodiscard]] void fillTopBarMenu(
const Ui::Menu::MenuCallback &addAction) override;
private:
void setupContent(not_null<Window::SessionController*> controller);
const not_null<Window::SessionController*> _controller;
};
} // namespace Settings

View file

@ -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<QWidget*> outer,

View file

@ -34,48 +34,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Settings {
void FillMenu(
not_null<Window::SessionController*> controller,
Type type,
Fn<void(Type)> 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

View file

@ -54,6 +54,7 @@ struct SectionFactory : AbstractSectionFactory {
static const auto result = std::make_shared<SectionFactory>();
return result;
}
};
template <typename SectionType>
@ -67,12 +68,24 @@ public:
[[nodiscard]] Type id() const final override {
return Id();
}
[[nodiscard]] rpl::producer<Type> sectionShowOther() final override {
return _showOtherRequests.events();
}
[[nodiscard]] void showOther(Type type) {
_showOtherRequests.fire_copy(type);
}
[[nodiscard]] Fn<void(Type)> showOtherMethod() {
return crl::guard(this, [=](Type type) {
showOther(type);
});
}
private:
rpl::event_stream<Type> _showOtherRequests;
};
void FillMenu(
not_null<Window::SessionController*> controller,
Type type,
Fn<void(Type)> showOther,
Ui::Menu::MenuCallback addAction);
bool HasMenu(Type type);
} // namespace Settings

View file

@ -383,7 +383,6 @@ public:
[[nodiscard]] rpl::producer<QString> title() override;
void showFinished() override;
[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
[[nodiscard]] rpl::producer<> sectionShowBack() override;
[[nodiscard]] rpl::producer<std::vector<Type>> removeFromStack() override;
@ -399,7 +398,6 @@ private:
rpl::variable<bool> _isBottomFillerShown;
rpl::event_stream<> _showFinished;
rpl::event_stream<Type> _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<Type> LocalPasscodeManage::sectionShowOther() {
return _showOther.events();
}
rpl::producer<> LocalPasscodeManage::sectionShowBack() {
return _showBack.events();
}

View file

@ -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<QString> 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<Window::SessionController*> 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<Window::SessionController*> controller) {
controller->session().data().cloudThemes().refresh();
}
rpl::producer<Type> Main::sectionShowOther() {
return _showOther.events();
}
} // namespace Settings

View file

@ -38,7 +38,8 @@ public:
[[nodiscard]] rpl::producer<QString> title() override;
rpl::producer<Type> 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<Window::SessionController*> controller);
const not_null<Window::SessionController*> _controller;
rpl::event_stream<Type> _showOther;
};

View file

@ -1281,17 +1281,11 @@ rpl::producer<QString> Notifications::title() {
return tr::lng_settings_section_notify();
}
rpl::producer<Type> Notifications::sectionShowOther() {
return _showOther.events();
}
void Notifications::setupContent(
not_null<Window::SessionController*> controller) {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
SetupNotifications(controller, content, [=](Type type) {
_showOther.fire_copy(type);
});
SetupNotifications(controller, content, showOtherMethod());
Ui::ResizeFitChild(this, content);
}

View file

@ -19,13 +19,9 @@ public:
[[nodiscard]] rpl::producer<QString> title() override;
rpl::producer<Type> sectionShowOther() override;
private:
void setupContent(not_null<Window::SessionController*> controller);
rpl::event_stream<Type> _showOther;
};
} // namespace Settings

View file

@ -968,10 +968,6 @@ rpl::producer<QString> PrivacySecurity::title() {
return tr::lng_settings_section_privacy();
}
rpl::producer<Type> PrivacySecurity::sectionShowOther() {
return _showOther.events();
}
void PrivacySecurity::setupContent(
not_null<Window::SessionController*> controller) {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(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());

View file

@ -47,13 +47,9 @@ public:
[[nodiscard]] rpl::producer<QString> title() override;
rpl::producer<Type> sectionShowOther() override;
private:
void setupContent(not_null<Window::SessionController*> controller);
rpl::event_stream<Type> _showOther;
};
} // namespace Settings