/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "window/window_peer_menu.h" #include "lang/lang_keys.h" #include "boxes/confirm_box.h" #include "boxes/mute_settings_box.h" #include "boxes/add_contact_box.h" #include "boxes/report_box.h" #include "boxes/create_poll_box.h" #include "boxes/peers/add_participants_box.h" #include "boxes/peers/edit_contact_box.h" #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" #include "ui/widgets/labels.h" #include "ui/widgets/checkbox.h" #include "ui/layers/generic_box.h" #include "main/main_session.h" #include "apiwrap.h" #include "mainwidget.h" #include "mainwindow.h" #include "observer_peer.h" #include "api/api_common.h" #include "api/api_chat_filters.h" #include "history/history.h" #include "history/history_item.h" #include "history/history_message.h" // GetErrorTextForSending. #include "window/window_session_controller.h" #include "window/window_controller.h" #include "support/support_helper.h" #include "info/info_memento.h" #include "info/info_controller.h" //#include "info/feed/info_feed_channels_controllers.h" // #feed #include "info/profile/info_profile_values.h" #include "data/data_session.h" #include "data/data_folder.h" #include "data/data_poll.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_drafts.h" #include "data/data_user.h" #include "data/data_scheduled_messages.h" #include "data/data_histories.h" #include "data/data_chat_filters.h" #include "dialogs/dialogs_key.h" #include "boxes/peers/edit_peer_info_box.h" #include "facades.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_window.h" // st::windowMinWidth #include "styles/style_history.h" // st::historyErrorToast #include namespace Window { namespace { constexpr auto kArchivedToastDuration = crl::time(5000); class Filler { public: Filler( not_null controller, not_null peer, FilterId filterId, const PeerMenuCallback &addAction, PeerMenuSource source); void fill(); private: [[nodiscard]] bool showInfo(); [[nodiscard]] bool showHidePromotion(); [[nodiscard]] bool showToggleArchived(); [[nodiscard]] bool showTogglePin(); void addHidePromotion(); void addTogglePin(); void addInfo(); //void addSearch(); void addToggleUnreadMark(); void addToggleArchive(); void addUserActions(not_null user); void addBlockUser(not_null user); void addChatActions(not_null chat); void addChannelActions(not_null channel); not_null _controller; not_null _peer; FilterId _filterId = 0; const PeerMenuCallback &_addAction; PeerMenuSource _source; }; class FolderFiller { public: FolderFiller( not_null controller, not_null folder, const PeerMenuCallback &addAction, PeerMenuSource source); void fill(); private: void addTogglesForArchive(); //bool showInfo(); //void addTogglePin(); //void addInfo(); //void addSearch(); //void addNotifications(); //void addUngroup(); not_null _controller; not_null _folder; const PeerMenuCallback &_addAction; PeerMenuSource _source; }; History *FindWastedPin(not_null data, Data::Folder *folder) { const auto &order = data->pinnedChatsOrder(folder, FilterId()); for (const auto &pinned : order) { if (const auto history = pinned.history()) { if (history->peer->isChat() && history->peer->asChat()->isDeactivated() && !history->inChatList()) { return history; } } } return nullptr; } void AddChatMembers( not_null navigation, not_null chat) { AddParticipantsBoxController::Start(navigation, chat); } bool PinnedLimitReached(Dialogs::Key key, FilterId filterId) { Expects(key.entry()->folderKnown()); const auto entry = key.entry(); const auto owner = &entry->owner(); const auto folder = entry->folder(); const auto pinnedCount = owner->pinnedChatsCount(folder, filterId); const auto pinnedMax = owner->pinnedChatsLimit(folder, filterId); if (pinnedCount < pinnedMax) { return false; } // Some old chat, that was converted, maybe is still pinned. const auto wasted = filterId ? nullptr : FindWastedPin(owner, folder); if (wasted) { owner->setChatPinned(wasted, FilterId(), false); owner->setChatPinned(key, FilterId(), true); entry->session().api().savePinnedOrder(folder); } else { const auto errorText = filterId ? tr::lng_filters_error_pinned_max(tr::now) : tr::lng_error_pinned_max( tr::now, lt_count, pinnedMax); Ui::show(Box(errorText)); } return true; } void TogglePinnedDialog(Dialogs::Key key) { if (!key.entry()->folderKnown()) { return; } const auto owner = &key.entry()->owner(); const auto isPinned = !key.entry()->isPinnedDialog(0); if (isPinned && PinnedLimitReached(key, 0)) { return; } owner->setChatPinned(key, FilterId(), isPinned); const auto flags = isPinned ? MTPmessages_ToggleDialogPin::Flag::f_pinned : MTPmessages_ToggleDialogPin::Flag(0); if (const auto history = key.history()) { history->session().api().request(MTPmessages_ToggleDialogPin( MTP_flags(flags), MTP_inputDialogPeer(key.history()->peer->input) )).done([=](const MTPBool &result) { owner->notifyPinnedDialogsOrderUpdated(); }).send(); } else if (const auto folder = key.folder()) { folder->session().api().request(MTPmessages_ToggleDialogPin( MTP_flags(flags), MTP_inputDialogPeerFolder(MTP_int(folder->id())) )).send(); } if (isPinned) { if (const auto main = App::main()) { main->dialogsToUp(); } } } void TogglePinnedDialog(Dialogs::Key key, FilterId filterId) { if (!filterId) { return TogglePinnedDialog(key); } const auto owner = &key.entry()->owner(); // This can happen when you remove this filter from another client. if (!ranges::contains( (&owner->session())->data().chatsFilters().list(), filterId, &Data::ChatFilter::id)) { Ui::Toast::Show(tr::lng_cant_do_this(tr::now)); return; } const auto isPinned = !key.entry()->isPinnedDialog(filterId); if (isPinned && PinnedLimitReached(key, filterId)) { return; } owner->setChatPinned(key, filterId, isPinned); Api::SaveNewFilterPinned(&owner->session(), filterId); if (isPinned) { if (const auto main = App::main()) { main->dialogsToUp(); } } } Filler::Filler( not_null controller, not_null peer, FilterId filterId, const PeerMenuCallback &addAction, PeerMenuSource source) : _controller(controller) , _peer(peer) , _filterId(filterId) , _addAction(addAction) , _source(source) { } bool Filler::showInfo() { if (_source == PeerMenuSource::Profile || _peer->isSelf()) { return false; } else if (_controller->activeChatCurrent().peer() != _peer) { return true; } else if (!Adaptive::ThreeColumn()) { return true; } else if ( !_peer->session().settings().thirdSectionInfoEnabled() && !_peer->session().settings().tabbedReplacedWithInfo()) { return true; } return false; } bool Filler::showHidePromotion() { if (_source != PeerMenuSource::ChatsList) { return false; } const auto history = _peer->owner().historyLoaded(_peer); return history && history->useTopPromotion() && !history->topPromotionType().isEmpty(); } bool Filler::showToggleArchived() { if (_source != PeerMenuSource::ChatsList) { return false; } const auto history = _peer->owner().historyLoaded(_peer); if (history && history->useTopPromotion()) { return false; } else if (!_peer->isNotificationsUser() && !_peer->isSelf()) { return true; } return history && (history->folder() != nullptr); } bool Filler::showTogglePin() { if (_source != PeerMenuSource::ChatsList) { return false; } const auto history = _peer->owner().historyLoaded(_peer); return history && !history->fixedOnTopIndex(); } void Filler::addHidePromotion() { const auto history = _peer->owner().history(_peer); _addAction(tr::lng_context_hide_psa(tr::now), [=] { history->cacheTopPromotion(false, QString(), QString()); history->session().api().request(MTPhelp_HidePromoData( history->peer->input )).send(); }); } void Filler::addTogglePin() { const auto filterId = _filterId; const auto peer = _peer; auto isPinned = false; if (const auto history = peer->owner().historyLoaded(peer)) { isPinned = history->isPinnedDialog(filterId); } const auto pinText = [](bool isPinned) { return isPinned ? tr::lng_context_unpin_from_top(tr::now) : tr::lng_context_pin_to_top(tr::now); }; const auto pinToggle = [=] { TogglePinnedDialog(peer->owner().history(peer), filterId); }; const auto pinAction = _addAction(pinText(isPinned), pinToggle); const auto lifetime = Ui::CreateChild(pinAction); Notify::PeerUpdateViewer( peer, Notify::PeerUpdate::Flag::ChatPinnedChanged ) | rpl::start_with_next([=] { const auto history = peer->owner().history(peer); const auto isPinned = history->isPinnedDialog(filterId); pinAction->setText(pinText(isPinned)); }, *lifetime); } void Filler::addInfo() { const auto controller = _controller; const auto peer = _peer; const auto text = (peer->isChat() || peer->isMegagroup()) ? tr::lng_context_view_group(tr::now) : (peer->isUser() ? tr::lng_context_view_profile(tr::now) : tr::lng_context_view_channel(tr::now)); _addAction(text, [=] { controller->showPeerInfo(peer); }); } //void Filler::addSearch() { // _addAction(tr::lng_profile_search_messages(tr::now), [peer = _peer] { // App::main()->searchInChat(peer->owner().history(peer)); // }); //} void Filler::addToggleUnreadMark() { const auto peer = _peer; const auto isUnread = [](not_null peer) { if (const auto history = peer->owner().historyLoaded(peer)) { return (history->chatListUnreadCount() > 0) || (history->chatListUnreadMark()); } return false; }; const auto label = [=](not_null peer) { return isUnread(peer) ? tr::lng_context_mark_read(tr::now) : tr::lng_context_mark_unread(tr::now); }; auto action = _addAction(label(peer), [=] { const auto markAsRead = isUnread(peer); const auto handle = [&](not_null history) { if (markAsRead) { peer->owner().histories().readInbox(history); } else { peer->owner().histories().changeDialogUnreadMark( history, !markAsRead); } }; const auto history = peer->owner().history(peer); handle(history); if (markAsRead) { if (const auto migrated = history->migrateSibling()) { handle(migrated); } } }); const auto lifetime = Ui::CreateChild(action); Notify::PeerUpdateViewer( _peer, Notify::PeerUpdate::Flag::UnreadViewChanged ) | rpl::start_with_next([=] { action->setText(label(peer)); }, *lifetime); } void Filler::addToggleArchive() { const auto peer = _peer; const auto isArchived = [=] { const auto history = peer->owner().historyLoaded(peer); return history && history->folder(); }; const auto toggle = [=] { ToggleHistoryArchived( peer->owner().history(peer), !isArchived()); }; const auto archiveAction = _addAction( (isArchived() ? tr::lng_archived_remove(tr::now) : tr::lng_archived_add(tr::now)), toggle); const auto lifetime = Ui::CreateChild(archiveAction); Notify::PeerUpdateViewer( peer, Notify::PeerUpdate::Flag::FolderChanged ) | rpl::start_with_next([=] { archiveAction->setText(isArchived() ? tr::lng_archived_remove(tr::now) : tr::lng_archived_add(tr::now)); }, *lifetime); } void Filler::addBlockUser(not_null user) { const auto window = &_controller->window(); const auto blockText = [](not_null user) { return user->isBlocked() ? ((user->isBot() && !user->isSupport()) ? tr::lng_profile_restart_bot(tr::now) : tr::lng_profile_unblock_user(tr::now)) : ((user->isBot() && !user->isSupport()) ? tr::lng_profile_block_bot(tr::now) : tr::lng_profile_block_user(tr::now)); }; const auto blockAction = _addAction(blockText(user), [=] { if (user->isBlocked()) { PeerMenuUnblockUserWithBotRestart(user); } else if (user->isBot()) { user->session().api().blockUser(user); } else { window->show(Box(PeerMenuBlockUserBox, window, user, false)); } }); const auto lifetime = Ui::CreateChild(blockAction); Notify::PeerUpdateViewer( _peer, Notify::PeerUpdate::Flag::UserIsBlocked ) | rpl::start_with_next([=] { blockAction->setText(blockText(user)); }, *lifetime); if (user->blockStatus() == UserData::BlockStatus::Unknown) { user->session().api().requestFullPeer(user); } } void Filler::addUserActions(not_null user) { const auto controller = _controller; const auto window = &_controller->window(); if (_source != PeerMenuSource::ChatsList) { if (user->session().supportMode()) { _addAction("Edit support info", [=] { user->session().supportHelper().editInfo(user); }); } if (!user->isContact() && !user->isSelf() && !user->isBot()) { _addAction( tr::lng_info_add_as_contact(tr::now), [=] { window->show(Box(EditContactBox, window, user)); }); } if (user->canShareThisContact()) { _addAction( tr::lng_info_share_contact(tr::now), [=] { PeerMenuShareContactBox(controller, user); }); } if (user->isContact() && !user->isSelf()) { _addAction( tr::lng_info_edit_contact(tr::now), [=] { window->show(Box(EditContactBox, window, user)); }); _addAction( tr::lng_info_delete_contact(tr::now), [=] { PeerMenuDeleteContact(user); }); } if (user->isBot() && !user->botInfo->cantJoinGroups) { using AddBotToGroup = AddBotToGroupBoxController; _addAction( tr::lng_profile_invite_to_group(tr::now), [=] { AddBotToGroup::Start(controller, user); }); } if (user->canSendPolls()) { _addAction( tr::lng_polls_create(tr::now), [=] { PeerMenuCreatePoll(user); }); } if (user->canExportChatHistory()) { _addAction( tr::lng_profile_export_chat(tr::now), [=] { PeerMenuExportChat(user); }); } } _addAction( tr::lng_profile_delete_conversation(tr::now), DeleteAndLeaveHandler(user)); _addAction( tr::lng_profile_clear_history(tr::now), ClearHistoryHandler(user)); if (!user->isInaccessible() && user != user->session().user() && _source != PeerMenuSource::ChatsList) { addBlockUser(user); } } void Filler::addChatActions(not_null chat) { if (_source != PeerMenuSource::ChatsList) { const auto controller = _controller; if (EditPeerInfoBox::Available(chat)) { const auto text = tr::lng_manage_group_title(tr::now); _addAction(text, [=] { controller->showEditPeerBox(chat); }); } if (chat->canAddMembers()) { _addAction( tr::lng_profile_add_participant(tr::now), [=] { AddChatMembers(controller, chat); }); } if (chat->canSendPolls()) { _addAction( tr::lng_polls_create(tr::now), [=] { PeerMenuCreatePoll(chat); }); } if (chat->canExportChatHistory()) { _addAction( tr::lng_profile_export_chat(tr::now), [=] { PeerMenuExportChat(chat); }); } } _addAction( tr::lng_profile_clear_and_exit(tr::now), DeleteAndLeaveHandler(_peer)); _addAction( tr::lng_profile_clear_history(tr::now), ClearHistoryHandler(_peer)); } void Filler::addChannelActions(not_null channel) { const auto isGroup = channel->isMegagroup(); const auto navigation = _controller; //if (!isGroup) { // #feed // const auto feed = channel->feed(); // const auto grouped = (feed != nullptr); // if (!grouped || feed->channels().size() > 1) { // _addAction( // #feed // (grouped ? tr::lng_feed_ungroup(tr::now) : tr::lng_feed_group(tr::now)), // [=] { ToggleChannelGrouping(channel, !grouped); }); // } //} if (_source != PeerMenuSource::ChatsList) { if (EditPeerInfoBox::Available(channel)) { const auto controller = _controller; const auto text = isGroup ? tr::lng_manage_group_title(tr::now) : tr::lng_manage_channel_title(tr::now); _addAction(text, [=] { controller->showEditPeerBox(channel); }); } if (channel->canAddMembers()) { _addAction( tr::lng_channel_add_members(tr::now), [=] { PeerMenuAddChannelMembers(navigation, channel); }); } if (channel->canSendPolls()) { _addAction( tr::lng_polls_create(tr::now), [=] { PeerMenuCreatePoll(channel); }); } if (channel->canExportChatHistory()) { _addAction( (isGroup ? tr::lng_profile_export_chat(tr::now) : tr::lng_profile_export_channel(tr::now)), [=] { PeerMenuExportChat(channel); }); } } if (channel->amIn()) { if (isGroup && !channel->isPublic()) { _addAction( tr::lng_profile_clear_history(tr::now), ClearHistoryHandler(channel)); } auto text = isGroup ? tr::lng_profile_leave_group(tr::now) : tr::lng_profile_leave_channel(tr::now); _addAction(text, DeleteAndLeaveHandler(channel)); } else { auto text = isGroup ? tr::lng_profile_join_group(tr::now) : tr::lng_profile_join_channel(tr::now); _addAction( text, [=] { channel->session().api().joinChannel(channel); }); } if (_source != PeerMenuSource::ChatsList) { const auto needReport = !channel->amCreator() && (!isGroup || channel->isPublic()); if (needReport) { _addAction(tr::lng_profile_report(tr::now), [channel] { Ui::show(Box(channel)); }); } } } void Filler::fill() { if (showHidePromotion()) { addHidePromotion(); } if (showToggleArchived()) { addToggleArchive(); } if (showTogglePin()) { addTogglePin(); } if (showInfo()) { addInfo(); } if (_source != PeerMenuSource::Profile && !_peer->isSelf()) { PeerMenuAddMuteAction(_peer, _addAction); } if (_source == PeerMenuSource::ChatsList) { //addSearch(); addToggleUnreadMark(); } if (const auto user = _peer->asUser()) { addUserActions(user); } else if (const auto chat = _peer->asChat()) { addChatActions(chat); } else if (const auto channel = _peer->asChannel()) { addChannelActions(channel); } } FolderFiller::FolderFiller( not_null controller, not_null folder, const PeerMenuCallback &addAction, PeerMenuSource source) : _controller(controller) , _folder(folder) , _addAction(addAction) , _source(source) { } void FolderFiller::fill() { if (_source == PeerMenuSource::ChatsList) { addTogglesForArchive(); } } void FolderFiller::addTogglesForArchive() { if (_folder->id() != Data::Folder::kId) { return; } const auto controller = _controller; const auto hidden = controller->session().settings().archiveCollapsed(); const auto text = hidden ? tr::lng_context_archive_expand(tr::now) : tr::lng_context_archive_collapse(tr::now); _addAction(text, [=] { controller->session().settings().setArchiveCollapsed(!hidden); controller->session().saveSettingsDelayed(); }); _addAction(tr::lng_context_archive_to_menu(tr::now), [=] { Ui::Toast::Show(Ui::Toast::Config{ .text = { tr::lng_context_archive_to_menu_info(tr::now) }, .st = &st::windowArchiveToast, .durationMs = kArchivedToastDuration, .multiline = true, }); controller->session().settings().setArchiveInMainMenu( !controller->session().settings().archiveInMainMenu()); controller->session().saveSettingsDelayed(); }); } // //void FolderFiller::addInfo() { // auto controller = _controller; // auto feed = _feed; // _addAction(tr::lng_context_view_feed_info(tr::now), [=] { // controller->showSection(Info::Memento( // feed, // Info::Section(Info::Section::Type::Profile))); // }); //} // //void FolderFiller::addNotifications() { // const auto feed = _feed; // _addAction(tr::lng_feed_notifications(tr::now), [=] { // Info::FeedProfile::NotificationsController::Start(feed); // }); //} // //void FolderFiller::addSearch() { // const auto feed = _feed; // _addAction(tr::lng_profile_search_messages(tr::now), [=] { // App::main()->searchInChat(feed); // }); //} // //void FolderFiller::addUngroup() { // const auto feed = _feed; // //_addAction(tr::lng_feed_ungroup_all(tr::now), [=] { // #feed // // PeerMenuUngroupFeed(feed); // //}); //} } // namespace void PeerMenuExportChat(not_null peer) { peer->owner().startExport(peer); } void PeerMenuDeleteContact(not_null user) { const auto text = tr::lng_sure_delete_contact( tr::now, lt_contact, user->name); const auto deleteSure = [=] { Ui::hideLayer(); user->session().api().request(MTPcontacts_DeleteContacts( MTP_vector(1, user->inputUser) )).done([=](const MTPUpdates &result) { user->session().api().applyUpdates(result); }).send(); }; Ui::show(Box( text, tr::lng_box_delete(tr::now), deleteSure)); } void PeerMenuShareContactBox( not_null navigation, not_null user) { const auto weak = std::make_shared>(); auto callback = [=](not_null peer) { if (!peer->canWrite()) { Ui::show(Box( tr::lng_forward_share_cant(tr::now)), Ui::LayerOption::KeepOther); return; } else if (peer->isSelf()) { auto action = Api::SendAction(peer->owner().history(peer)); action.clearDraft = false; user->session().api().shareContact(user, action); Ui::Toast::Show(tr::lng_share_done(tr::now)); if (auto strong = *weak) { strong->closeBox(); } return; } auto recipient = peer->isUser() ? peer->name : '\xAB' + peer->name + '\xBB'; Ui::show(Box( tr::lng_forward_share_contact(tr::now, lt_recipient, recipient), tr::lng_forward_send(tr::now), [peer, user] { const auto history = peer->owner().history(peer); Ui::showPeerHistory(history, ShowAtTheEndMsgId); auto action = Api::SendAction(history); action.clearDraft = false; user->session().api().shareContact(user, action); }), Ui::LayerOption::KeepOther); }; *weak = Ui::show(Box( std::make_unique( navigation, std::move(callback)), [](not_null box) { box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); })); } void PeerMenuCreatePoll( not_null peer, PollData::Flags chosen, PollData::Flags disabled) { if (peer->isChannel() && !peer->isMegagroup()) { chosen &= ~PollData::Flag::PublicVotes; disabled |= PollData::Flag::PublicVotes; } const auto box = Ui::show(Box( &peer->session(), chosen, disabled, Api::SendType::Normal)); const auto lock = box->lifetime().make_state(false); box->submitRequests( ) | rpl::start_with_next([=](const CreatePollBox::Result &result) { if (std::exchange(*lock, true)) { return; } auto action = Api::SendAction(peer->owner().history(peer)); action.clearDraft = false; action.options = result.options; if (const auto id = App::main()->currentReplyToIdFor(action.history)) { action.replyTo = id; } if (const auto localDraft = action.history->localDraft()) { action.clearDraft = localDraft->textWithTags.text.isEmpty(); } const auto api = &peer->session().api(); api->createPoll(result.poll, action, crl::guard(box, [=] { box->closeBox(); }), crl::guard(box, [=](const RPCError &error) { *lock = false; box->submitFailed(tr::lng_attach_failed(tr::now)); })); }, box->lifetime()); } void PeerMenuBlockUserBox( not_null box, not_null window, not_null user, bool suggestClearChat) { using Flag = MTPDpeerSettings::Flag; const auto settings = user->settings().value_or(Flag(0)); const auto name = user->shortName(); box->addRow(object_ptr( box, tr::lng_blocked_list_confirm_text( lt_name, rpl::single(Ui::Text::Bold(name)), Ui::Text::WithEntities), st::blockUserConfirmation)); box->addSkip(st::boxMediumSkip); const auto report = (settings & Flag::f_report_spam) ? box->addRow(object_ptr( box, tr::lng_report_spam(tr::now), true, st::defaultBoxCheckbox)) : nullptr; if (report) { box->addSkip(st::boxMediumSkip); } const auto clear = suggestClearChat ? box->addRow(object_ptr( box, tr::lng_blocked_list_confirm_clear(tr::now), true, st::defaultBoxCheckbox)) : nullptr; if (report || clear) { box->addSkip(st::boxLittleSkip); } box->setTitle(tr::lng_blocked_list_confirm_title( lt_name, rpl::single(name))); box->addButton(tr::lng_blocked_list_confirm_ok(), [=] { const auto reportChecked = report && report->checked(); const auto clearChecked = clear && clear->checked(); box->closeBox(); user->session().api().blockUser(user); if (reportChecked) { user->session().api().request(MTPmessages_ReportSpam( user->input )).send(); } if (clearChecked) { crl::on_main(&user->session(), [=] { user->session().api().deleteConversation(user, false); }); window->sessionController()->showBackFromStack(); } Ui::Toast::Show( tr::lng_new_contact_block_done(tr::now, lt_user, user->shortName())); }, st::attentionBoxButton); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } void PeerMenuUnblockUserWithBotRestart(not_null user) { user->session().api().unblockUser(user, [=] { if (user->isBot() && !user->isSupport()) { user->session().api().sendBotStart(user); } }); } QPointer ShowForwardMessagesBox( not_null navigation, MessageIdsList &&items, FnMut &&successCallback) { const auto weak = std::make_shared>(); auto callback = [ ids = std::move(items), callback = std::move(successCallback), weak ](not_null peer) mutable { if (peer->isSelf()) { auto items = peer->owner().idsToItems(ids); if (!items.empty()) { const auto api = &peer->session().api(); auto action = Api::SendAction(peer->owner().history(peer)); action.clearDraft = false; action.generateLocal = false; api->forwardMessages(std::move(items), action, [] { Ui::Toast::Show(tr::lng_share_done(tr::now)); }); } } else if (!App::main()->setForwardDraft(peer->id, std::move(ids))) { return; } if (const auto strong = *weak) { strong->closeBox(); } if (callback) { callback(); } }; auto initBox = [](not_null box) { box->addButton(tr::lng_cancel(), [box] { box->closeBox(); }); }; *weak = Ui::show(Box( std::make_unique( navigation, std::move(callback)), std::move(initBox)), Ui::LayerOption::KeepOther); return weak->data(); } QPointer ShowSendNowMessagesBox( not_null navigation, not_null history, MessageIdsList &&items, FnMut &&successCallback) { const auto session = &navigation->session(); const auto text = (items.size() > 1) ? tr::lng_scheduled_send_now_many(tr::now, lt_count, items.size()) : tr::lng_scheduled_send_now(tr::now); const auto error = GetErrorTextForSending( history->peer, session->data().idsToItems(items), TextWithTags()); if (!error.isEmpty()) { Ui::Toast::Show(Ui::Toast::Config{ .text = { error }, .st = &st::historyErrorToast, .multiline = true, }); return { nullptr }; } const auto box = std::make_shared>(); auto done = [ =, list = std::move(items), callback = std::move(successCallback) ]() mutable { if (*box) { (*box)->closeBox(); } auto ids = QVector(); for (const auto item : session->data().idsToItems(list)) { if (item->allowsSendNow()) { ids.push_back(MTP_int( session->data().scheduledMessages().lookupId(item))); } } session->api().request(MTPmessages_SendScheduledMessages( history->peer->input, MTP_vector(ids) )).done([=](const MTPUpdates &result) { session->api().applyUpdates(result); }).fail([=](const RPCError &error) { session->api().sendMessageFail(error, history->peer); }).send(); if (callback) { callback(); } }; *box = Ui::show( Box(text, tr::lng_send_button(tr::now), std::move(done)), Ui::LayerOption::KeepOther); return box->data(); } void PeerMenuAddChannelMembers( not_null navigation, not_null channel) { if (!channel->isMegagroup() && channel->membersCount() >= Global::ChatSizeMax()) { Ui::show( Box(channel), Ui::LayerOption::KeepOther); return; } const auto api = &channel->session().api(); api->requestChannelMembersForAdd(channel, [=]( const MTPchannels_ChannelParticipants &result) { api->parseChannelParticipants(channel, result, [&]( int availableCount, const QVector &list) { auto already = ( list ) | ranges::view::transform([](const MTPChannelParticipant &p) { return p.match([](const auto &data) { return data.vuser_id().v; }); }) | ranges::view::transform([&](UserId userId) { return channel->owner().userLoaded(userId); }) | ranges::view::filter([](UserData *user) { return (user != nullptr); }) | ranges::to_vector; AddParticipantsBoxController::Start( navigation, channel, { already.begin(), already.end() }); }); }); } void PeerMenuAddMuteAction( not_null peer, const PeerMenuCallback &addAction) { peer->owner().requestNotifySettings(peer); const auto muteText = [](bool isMuted) { return isMuted ? tr::lng_enable_notifications_from_tray(tr::now) : tr::lng_disable_notifications_from_tray(tr::now); }; const auto muteAction = addAction(QString("-"), [=] { if (!peer->owner().notifyIsMuted(peer)) { Ui::show(Box(peer)); } else { peer->owner().updateNotifySettings(peer, 0); } }); const auto lifetime = Ui::CreateChild(muteAction); Info::Profile::NotificationsEnabledValue( peer ) | rpl::start_with_next([=](bool enabled) { muteAction->setText(muteText(!enabled)); }, *lifetime); } // #feed //void PeerMenuUngroupFeed(not_null feed) { // Ui::show(Box( // tr::lng_feed_sure_ungroup_all(tr::now), // tr::lng_feed_ungroup_sure(tr::now), // [=] { Ui::hideLayer(); feed->session().api().ungroupAllFromFeed(feed); })); //} // void ToggleHistoryArchived(not_null history, bool archived) { const auto callback = [=] { Ui::Toast::Show(Ui::Toast::Config{ .text = { (archived ? tr::lng_archived_added(tr::now) : tr::lng_archived_removed(tr::now)) }, .st = &st::windowArchiveToast, .durationMs = (archived ? kArchivedToastDuration : Ui::Toast::kDefaultDuration), .multiline = true, }); }; history->session().api().toggleHistoryArchived( history, archived, callback); } Fn ClearHistoryHandler(not_null peer) { return [=] { Ui::show( Box(peer, true), Ui::LayerOption::KeepOther); }; } Fn DeleteAndLeaveHandler(not_null peer) { return [=] { Ui::show( Box(peer, false), Ui::LayerOption::KeepOther); }; } void FillPeerMenu( not_null controller, not_null peer, FilterId filterId, const PeerMenuCallback &callback, PeerMenuSource source) { Filler filler(controller, peer, filterId, callback, source); filler.fill(); } void FillFolderMenu( not_null controller, not_null folder, const PeerMenuCallback &callback, PeerMenuSource source) { FolderFiller filler(controller, folder, callback, source); filler.fill(); } } // namespace Window