tdesktop/Telegram/SourceFiles/window/window_peer_menu.cpp

1091 lines
30 KiB
C++
Raw Normal View History

2017-11-06 18:03:20 +00:00
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
2017-11-06 18:03:20 +00:00
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
2017-11-06 18:03:20 +00:00
*/
#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"
2017-11-07 11:53:05 +00:00
#include "boxes/report_box.h"
#include "boxes/create_poll_box.h"
#include "boxes/peers/add_participants_box.h"
2019-06-12 14:13:49 +00:00
#include "boxes/peers/edit_contact_box.h"
#include "ui/toast/toast.h"
2019-06-12 13:26:04 +00:00
#include "ui/text/text_utilities.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/checkbox.h"
2019-09-18 11:19:05 +00:00
#include "ui/layers/generic_box.h"
2019-07-24 11:45:24 +00:00
#include "main/main_session.h"
2017-11-06 18:03:20 +00:00
#include "apiwrap.h"
#include "mainwidget.h"
#include "mainwindow.h"
2017-11-06 18:03:20 +00:00
#include "observer_peer.h"
2019-08-20 13:21:10 +00:00
#include "api/api_common.h"
#include "api/api_chat_filters.h"
#include "history/history.h"
#include "history/history_item.h"
2019-08-30 13:17:46 +00:00
#include "history/history_message.h" // GetErrorTextForSending.
#include "window/window_session_controller.h"
2019-06-12 13:26:04 +00:00
#include "window/window_controller.h"
#include "support/support_helper.h"
#include "info/info_memento.h"
#include "info/info_controller.h"
2019-04-15 11:54:03 +00:00
//#include "info/feed/info_feed_channels_controllers.h" // #feed
#include "info/profile/info_profile_values.h"
#include "data/data_session.h"
2019-04-15 11:54:03 +00:00
#include "data/data_folder.h"
2018-12-22 21:31:12 +00:00
#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"
2020-02-19 15:35:26 +00:00
#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"
2019-09-18 11:19:05 +00:00
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_window.h" // st::windowMinWidth
2017-11-06 18:03:20 +00:00
2019-09-04 07:19:15 +00:00
#include <QtWidgets/QAction>
2017-11-06 18:03:20 +00:00
namespace Window {
namespace {
constexpr auto kArchivedToastDuration = crl::time(5000);
2019-04-25 09:20:20 +00:00
2017-11-06 18:03:20 +00:00
class Filler {
public:
Filler(
not_null<SessionController*> controller,
2017-11-06 18:03:20 +00:00
not_null<PeerData*> peer,
2020-03-17 13:04:30 +00:00
FilterId filterId,
const PeerMenuCallback &addAction,
PeerMenuSource source);
2017-11-06 18:03:20 +00:00
void fill();
private:
bool showInfo();
bool showToggleArchived();
2019-05-01 13:10:11 +00:00
bool showTogglePin();
void addTogglePin();
2017-11-06 18:03:20 +00:00
void addInfo();
2019-04-25 08:36:31 +00:00
//void addSearch();
2018-06-26 18:03:45 +00:00
void addToggleUnreadMark();
void addToggleArchive();
2017-11-06 18:03:20 +00:00
void addUserActions(not_null<UserData*> user);
void addBlockUser(not_null<UserData*> user);
void addChatActions(not_null<ChatData*> chat);
void addChannelActions(not_null<ChannelData*> channel);
not_null<SessionController*> _controller;
2017-11-06 18:03:20 +00:00
not_null<PeerData*> _peer;
2020-03-17 13:04:30 +00:00
FilterId _filterId = 0;
const PeerMenuCallback &_addAction;
PeerMenuSource _source;
2017-11-06 18:03:20 +00:00
};
2019-04-15 11:54:03 +00:00
class FolderFiller {
public:
2019-04-15 11:54:03 +00:00
FolderFiller(
not_null<SessionController*> controller,
2019-04-15 11:54:03 +00:00
not_null<Data::Folder*> folder,
const PeerMenuCallback &addAction,
PeerMenuSource source);
void fill();
private:
void addTogglesForArchive();
2019-04-19 08:47:49 +00:00
//bool showInfo();
2019-05-01 13:10:11 +00:00
//void addTogglePin();
2019-04-19 08:47:49 +00:00
//void addInfo();
//void addSearch();
//void addNotifications();
//void addUngroup();
not_null<SessionController*> _controller;
not_null<Data::Folder*> _folder;
const PeerMenuCallback &_addAction;
PeerMenuSource _source;
};
2019-07-24 11:13:51 +00:00
History *FindWastedPin(not_null<Data::Session*> data, Data::Folder *folder) {
2020-03-17 13:04:30 +00:00
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()
2019-04-22 14:22:39 +00:00
&& !history->inChatList()) {
return history;
}
2017-11-06 18:03:20 +00:00
}
}
return nullptr;
}
2019-07-25 18:55:11 +00:00
void AddChatMembers(
not_null<Window::SessionNavigation*> navigation,
not_null<ChatData*> chat) {
AddParticipantsBoxController::Start(navigation, chat);
}
2020-03-17 13:04:30 +00:00
bool PinnedLimitReached(Dialogs::Key key, FilterId filterId) {
2019-04-19 08:47:49 +00:00
Expects(key.entry()->folderKnown());
2019-07-24 11:13:51 +00:00
const auto entry = key.entry();
const auto owner = &entry->owner();
const auto folder = entry->folder();
2020-03-17 13:04:30 +00:00
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.
2020-03-17 13:04:30 +00:00
const auto wasted = filterId ? nullptr : FindWastedPin(owner, folder);
if (wasted) {
owner->setChatPinned(wasted, FilterId(), false);
owner->setChatPinned(key, FilterId(), true);
2019-07-24 11:13:51 +00:00
entry->session().api().savePinnedOrder(folder);
} else {
2020-03-19 20:20:35 +00:00
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<InformBox>(errorText));
}
return true;
}
void TogglePinnedDialog(Dialogs::Key key) {
2019-04-19 08:47:49 +00:00
if (!key.entry()->folderKnown()) {
return;
}
2019-07-24 11:13:51 +00:00
const auto owner = &key.entry()->owner();
2020-03-17 13:04:30 +00:00
const auto isPinned = !key.entry()->isPinnedDialog(0);
if (isPinned && PinnedLimitReached(key, 0)) {
return;
}
2020-03-17 13:04:30 +00:00
owner->setChatPinned(key, FilterId(), isPinned);
2019-04-19 08:47:49 +00:00
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(
2018-03-06 17:07:42 +00:00
MTP_flags(flags),
2019-04-19 08:47:49 +00:00
MTP_inputDialogPeer(key.history()->peer->input)
)).done([=](const MTPBool &result) {
2019-07-24 11:13:51 +00:00
owner->notifyPinnedDialogsOrderUpdated();
}).send();
2019-04-19 08:47:49 +00:00
} else if (const auto folder = key.folder()) {
folder->session().api().request(MTPmessages_ToggleDialogPin(
MTP_flags(flags),
MTP_inputDialogPeerFolder(MTP_int(folder->id()))
)).send();
2018-03-06 17:07:42 +00:00
}
if (isPinned) {
if (const auto main = App::main()) {
main->dialogsToUp();
}
}
}
2020-03-17 13:04:30 +00:00
void TogglePinnedDialog(Dialogs::Key key, FilterId filterId) {
if (!filterId) {
return TogglePinnedDialog(key);
}
const auto owner = &key.entry()->owner();
const auto isPinned = !key.entry()->isPinnedDialog(filterId);
if (isPinned && PinnedLimitReached(key, filterId)) {
return;
}
owner->setChatPinned(key, filterId, isPinned);
Api::SaveNewFilterPinned(&owner->session(), filterId);
2020-03-17 13:04:30 +00:00
if (isPinned) {
if (const auto main = App::main()) {
main->dialogsToUp();
}
}
}
2017-11-06 18:03:20 +00:00
Filler::Filler(
not_null<SessionController*> controller,
2017-11-06 18:03:20 +00:00
not_null<PeerData*> peer,
2020-03-17 13:04:30 +00:00
FilterId filterId,
const PeerMenuCallback &addAction,
PeerMenuSource source)
2017-11-07 11:53:05 +00:00
: _controller(controller)
, _peer(peer)
2020-03-17 13:04:30 +00:00
, _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 (
2019-07-24 11:13:51 +00:00
!_peer->session().settings().thirdSectionInfoEnabled() &&
!_peer->session().settings().tabbedReplacedWithInfo()) {
return true;
}
return false;
2017-11-06 18:03:20 +00:00
}
bool Filler::showToggleArchived() {
if (_source != PeerMenuSource::ChatsList) {
return false;
}
const auto history = _peer->owner().historyLoaded(_peer);
if (history && history->useProxyPromotion()) {
return false;
} else if (!_peer->isNotificationsUser() && !_peer->isSelf()) {
return true;
}
return history && (history->folder() != nullptr);
}
2019-05-01 13:10:11 +00:00
bool Filler::showTogglePin() {
if (_source != PeerMenuSource::ChatsList) {
return false;
}
const auto history = _peer->owner().historyLoaded(_peer);
return history && !history->fixedOnTopIndex();
}
void Filler::addTogglePin() {
2020-03-17 13:04:30 +00:00
const auto filterId = _filterId;
const auto peer = _peer;
2017-11-06 18:03:20 +00:00
auto isPinned = false;
2020-03-17 13:04:30 +00:00
if (const auto history = peer->owner().historyLoaded(peer)) {
isPinned = history->isPinnedDialog(filterId);
2017-11-06 18:03:20 +00:00
}
2020-03-17 13:04:30 +00:00
const auto pinText = [](bool isPinned) {
2019-06-19 15:09:03 +00:00
return isPinned
? tr::lng_context_unpin_from_top(tr::now)
: tr::lng_context_pin_to_top(tr::now);
2017-11-06 18:03:20 +00:00
};
2020-03-17 13:04:30 +00:00
const auto pinToggle = [=] {
TogglePinnedDialog(peer->owner().history(peer), filterId);
2017-11-06 18:03:20 +00:00
};
2020-03-17 13:04:30 +00:00
const auto pinAction = _addAction(pinText(isPinned), pinToggle);
2017-11-06 18:03:20 +00:00
2018-11-21 10:09:46 +00:00
const auto lifetime = Ui::CreateChild<rpl::lifetime>(pinAction);
Notify::PeerUpdateViewer(
2017-11-06 18:03:20 +00:00
peer,
Notify::PeerUpdate::Flag::ChatPinnedChanged
2020-03-17 13:04:30 +00:00
) | rpl::start_with_next([=] {
const auto history = peer->owner().history(peer);
const auto isPinned = history->isPinnedDialog(filterId);
pinAction->setText(pinText(isPinned));
2018-11-21 10:09:46 +00:00
}, *lifetime);
2017-11-06 18:03:20 +00:00
}
void Filler::addInfo() {
2019-06-12 13:26:04 +00:00
const auto controller = _controller;
const auto peer = _peer;
2019-06-19 15:09:03 +00:00
const auto text = (peer->isChat() || peer->isMegagroup())
? tr::lng_context_view_group(tr::now)
2017-11-07 11:53:05 +00:00
: (peer->isUser()
2019-06-19 15:09:03 +00:00
? tr::lng_context_view_profile(tr::now)
: tr::lng_context_view_channel(tr::now));
_addAction(text, [=] {
2017-11-07 11:53:05 +00:00
controller->showPeerInfo(peer);
2017-11-06 18:03:20 +00:00
});
}
2019-04-25 08:36:31 +00:00
//void Filler::addSearch() {
2019-06-19 15:09:03 +00:00
// _addAction(tr::lng_profile_search_messages(tr::now), [peer = _peer] {
2019-04-25 08:36:31 +00:00
// App::main()->searchInChat(peer->owner().history(peer));
// });
//}
2017-11-06 18:03:20 +00:00
2018-06-26 18:03:45 +00:00
void Filler::addToggleUnreadMark() {
const auto peer = _peer;
const auto isUnread = [](not_null<PeerData*> peer) {
2019-01-18 12:27:37 +00:00
if (const auto history = peer->owner().historyLoaded(peer)) {
2018-06-26 18:03:45 +00:00
return (history->chatListUnreadCount() > 0)
|| (history->chatListUnreadMark());
}
return false;
};
const auto label = [=](not_null<PeerData*> peer) {
2019-06-19 15:09:03 +00:00
return isUnread(peer)
? tr::lng_context_mark_read(tr::now)
: tr::lng_context_mark_unread(tr::now);
2018-06-26 18:03:45 +00:00
};
auto action = _addAction(label(peer), [=] {
const auto markAsRead = isUnread(peer);
const auto handle = [&](not_null<History*> history) {
if (markAsRead) {
2020-02-21 07:58:50 +00:00
peer->owner().histories().readInbox(history);
2018-06-26 18:03:45 +00:00
} else {
2020-02-21 07:58:50 +00:00
peer->owner().histories().changeDialogUnreadMark(
2019-07-24 11:13:51 +00:00
history,
!markAsRead);
2018-06-26 18:03:45 +00:00
}
};
2019-01-18 12:27:37 +00:00
const auto history = peer->owner().history(peer);
2018-06-26 18:03:45 +00:00
handle(history);
if (markAsRead) {
if (const auto migrated = history->migrateSibling()) {
handle(migrated);
}
}
});
2018-11-21 10:09:46 +00:00
const auto lifetime = Ui::CreateChild<rpl::lifetime>(action);
Notify::PeerUpdateViewer(
2018-06-26 18:03:45 +00:00
_peer,
Notify::PeerUpdate::Flag::UnreadViewChanged
) | rpl::start_with_next([=] {
action->setText(label(peer));
2018-11-21 10:09:46 +00:00
}, *lifetime);
2018-06-26 18:03:45 +00:00
}
void Filler::addToggleArchive() {
const auto peer = _peer;
const auto archived = [&] {
const auto history = peer->owner().historyLoaded(peer);
return history && history->folder();
}();
const auto toggle = [=] {
ToggleHistoryArchived(
peer->owner().history(peer),
!archived);
};
_addAction(
2019-06-19 15:09:03 +00:00
(archived
? tr::lng_archived_remove(tr::now)
: tr::lng_archived_add(tr::now)),
toggle);
}
2017-11-06 18:03:20 +00:00
void Filler::addBlockUser(not_null<UserData*> user) {
2019-09-03 15:24:51 +00:00
const auto window = &_controller->window();
2019-06-12 13:26:04 +00:00
const auto blockText = [](not_null<UserData*> user) {
2019-06-19 15:09:03 +00:00
return user->isBlocked()
2019-03-12 10:36:33 +00:00
? ((user->isBot() && !user->isSupport())
2019-06-19 15:09:03 +00:00
? tr::lng_profile_restart_bot(tr::now)
: tr::lng_profile_unblock_user(tr::now))
2019-03-12 10:36:33 +00:00
: ((user->isBot() && !user->isSupport())
2019-06-19 15:09:03 +00:00
? tr::lng_profile_block_bot(tr::now)
: tr::lng_profile_block_user(tr::now));
2017-11-06 18:03:20 +00:00
};
2019-06-12 13:26:04 +00:00
const auto blockAction = _addAction(blockText(user), [=] {
2018-12-04 11:46:07 +00:00
if (user->isBlocked()) {
PeerMenuUnblockUserWithBotRestart(user);
2019-06-12 13:26:04 +00:00
} else if (user->isBot()) {
user->session().api().blockUser(user);
2017-11-06 18:03:20 +00:00
} else {
window->show(Box(PeerMenuBlockUserBox, window, user, false));
2017-11-06 18:03:20 +00:00
}
});
2018-11-21 10:09:46 +00:00
const auto lifetime = Ui::CreateChild<rpl::lifetime>(blockAction);
Notify::PeerUpdateViewer(
2017-11-06 18:03:20 +00:00
_peer,
Notify::PeerUpdate::Flag::UserIsBlocked
) | rpl::start_with_next([=] {
blockAction->setText(blockText(user));
2018-11-21 10:09:46 +00:00
}, *lifetime);
2017-11-06 18:03:20 +00:00
if (user->blockStatus() == UserData::BlockStatus::Unknown) {
2019-07-24 11:13:51 +00:00
user->session().api().requestFullPeer(user);
2017-11-06 18:03:20 +00:00
}
}
void Filler::addUserActions(not_null<UserData*> user) {
2019-07-25 18:55:11 +00:00
const auto controller = _controller;
2019-09-03 15:24:51 +00:00
const auto window = &_controller->window();
if (_source != PeerMenuSource::ChatsList) {
2019-07-24 11:13:51 +00:00
if (user->session().supportMode()) {
_addAction("Edit support info", [=] {
2019-07-24 11:13:51 +00:00
user->session().supportHelper().editInfo(user);
});
}
if (!user->isContact() && !user->isSelf() && !user->isBot()) {
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_info_add_as_contact(tr::now),
2019-06-12 14:13:49 +00:00
[=] { window->show(Box(EditContactBox, window, user)); });
}
if (user->canShareThisContact()) {
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_info_share_contact(tr::now),
2019-07-25 18:55:11 +00:00
[=] { PeerMenuShareContactBox(controller, user); });
}
if (user->isContact() && !user->isSelf()) {
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_info_edit_contact(tr::now),
2019-06-12 14:13:49 +00:00
[=] { window->show(Box(EditContactBox, window, user)); });
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_info_delete_contact(tr::now),
[=] { PeerMenuDeleteContact(user); });
}
if (user->isBot() && !user->botInfo->cantJoinGroups) {
2019-07-25 18:55:11 +00:00
using AddBotToGroup = AddBotToGroupBoxController;
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_profile_invite_to_group(tr::now),
2019-07-25 18:55:11 +00:00
[=] { AddBotToGroup::Start(controller, user); });
}
if (user->canSendPolls()) {
_addAction(
tr::lng_polls_create(tr::now),
[=] { PeerMenuCreatePoll(user); });
}
if (user->canExportChatHistory()) {
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_profile_export_chat(tr::now),
[=] { PeerMenuExportChat(user); });
}
2017-11-06 18:03:20 +00:00
}
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_profile_delete_conversation(tr::now),
2017-11-06 18:03:20 +00:00
DeleteAndLeaveHandler(user));
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_profile_clear_history(tr::now),
2017-11-06 18:03:20 +00:00
ClearHistoryHandler(user));
2019-04-25 08:36:31 +00:00
if (!user->isInaccessible()
2019-07-24 11:13:51 +00:00
&& user != user->session().user()
2019-04-25 08:36:31 +00:00
&& _source != PeerMenuSource::ChatsList) {
2017-11-06 18:03:20 +00:00
addBlockUser(user);
}
}
void Filler::addChatActions(not_null<ChatData*> chat) {
if (_source != PeerMenuSource::ChatsList) {
2019-06-12 14:13:49 +00:00
const auto controller = _controller;
if (EditPeerInfoBox::Available(chat)) {
2019-06-19 15:09:03 +00:00
const auto text = tr::lng_manage_group_title(tr::now);
_addAction(text, [=] {
2019-06-12 14:13:49 +00:00
controller->showEditPeerBox(chat);
});
}
if (chat->canAddMembers()) {
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_profile_add_participant(tr::now),
2019-07-25 18:55:11 +00:00
[=] { AddChatMembers(controller, chat); });
}
if (chat->canSendPolls()) {
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_polls_create(tr::now),
[=] { PeerMenuCreatePoll(chat); });
}
if (chat->canExportChatHistory()) {
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_profile_export_chat(tr::now),
[=] { PeerMenuExportChat(chat); });
}
}
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_profile_clear_and_exit(tr::now),
2017-11-06 18:03:20 +00:00
DeleteAndLeaveHandler(_peer));
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_profile_clear_history(tr::now),
2017-11-06 18:03:20 +00:00
ClearHistoryHandler(_peer));
}
void Filler::addChannelActions(not_null<ChannelData*> channel) {
2019-07-25 18:55:11 +00:00
const auto isGroup = channel->isMegagroup();
const auto navigation = _controller;
2019-04-15 11:54:03 +00:00
//if (!isGroup) { // #feed
// const auto feed = channel->feed();
// const auto grouped = (feed != nullptr);
// if (!grouped || feed->channels().size() > 1) {
// _addAction( // #feed
2019-06-19 15:09:03 +00:00
// (grouped ? tr::lng_feed_ungroup(tr::now) : tr::lng_feed_group(tr::now)),
2019-04-15 11:54:03 +00:00
// [=] { ToggleChannelGrouping(channel, !grouped); });
// }
//}
if (_source != PeerMenuSource::ChatsList) {
if (EditPeerInfoBox::Available(channel)) {
2019-06-12 14:13:49 +00:00
const auto controller = _controller;
2019-06-19 15:09:03 +00:00
const auto text = isGroup
? tr::lng_manage_group_title(tr::now)
: tr::lng_manage_channel_title(tr::now);
_addAction(text, [=] {
2019-06-12 14:13:49 +00:00
controller->showEditPeerBox(channel);
2017-11-08 16:45:30 +00:00
});
}
if (channel->canAddMembers()) {
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_channel_add_members(tr::now),
2019-07-25 18:55:11 +00:00
[=] { PeerMenuAddChannelMembers(navigation, channel); });
}
if (channel->canSendPolls()) {
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_polls_create(tr::now),
[=] { PeerMenuCreatePoll(channel); });
}
if (channel->canExportChatHistory()) {
_addAction(
2019-06-19 15:09:03 +00:00
(isGroup
? tr::lng_profile_export_chat(tr::now)
: tr::lng_profile_export_channel(tr::now)),
[=] { PeerMenuExportChat(channel); });
}
}
2017-11-07 11:53:05 +00:00
if (channel->amIn()) {
if (isGroup && !channel->isPublic()) {
_addAction(
2019-06-19 15:09:03 +00:00
tr::lng_profile_clear_history(tr::now),
ClearHistoryHandler(channel));
}
2019-06-19 15:09:03 +00:00
auto text = isGroup
? tr::lng_profile_leave_group(tr::now)
: tr::lng_profile_leave_channel(tr::now);
_addAction(text, DeleteAndLeaveHandler(channel));
} else {
2019-06-19 15:09:03 +00:00
auto text = isGroup
? tr::lng_profile_join_group(tr::now)
: tr::lng_profile_join_channel(tr::now);
_addAction(
text,
2019-07-24 11:13:51 +00:00
[=] { channel->session().api().joinChannel(channel); });
2017-11-06 18:03:20 +00:00
}
if (_source != PeerMenuSource::ChatsList) {
2019-06-21 12:27:46 +00:00
const auto needReport = !channel->amCreator()
&& (!isGroup || channel->isPublic());
2017-11-07 11:53:05 +00:00
if (needReport) {
2019-06-19 15:09:03 +00:00
_addAction(tr::lng_profile_report(tr::now), [channel] {
2017-11-07 11:53:05 +00:00
Ui::show(Box<ReportBox>(channel));
});
}
}
2017-11-06 18:03:20 +00:00
}
void Filler::fill() {
2019-05-01 13:10:11 +00:00
if (showToggleArchived()) {
addToggleArchive();
}
if (showTogglePin()) {
addTogglePin();
2017-11-06 18:03:20 +00:00
}
if (showInfo()) {
2017-11-06 18:03:20 +00:00
addInfo();
}
if (_source != PeerMenuSource::Profile && !_peer->isSelf()) {
PeerMenuAddMuteAction(_peer, _addAction);
}
if (_source == PeerMenuSource::ChatsList) {
2019-04-25 08:36:31 +00:00
//addSearch();
2018-06-26 18:03:45 +00:00
addToggleUnreadMark();
2017-11-06 18:03:20 +00:00
}
2018-02-13 16:11:00 +00:00
if (const auto user = _peer->asUser()) {
2017-11-06 18:03:20 +00:00
addUserActions(user);
2018-02-13 16:11:00 +00:00
} else if (const auto chat = _peer->asChat()) {
2017-11-06 18:03:20 +00:00
addChatActions(chat);
2018-02-13 16:11:00 +00:00
} else if (const auto channel = _peer->asChannel()) {
2017-11-06 18:03:20 +00:00
addChannelActions(channel);
}
}
2019-04-15 11:54:03 +00:00
FolderFiller::FolderFiller(
not_null<SessionController*> controller,
2019-04-15 11:54:03 +00:00
not_null<Data::Folder*> 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();
2019-06-19 15:09:03 +00:00
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();
});
2019-06-19 15:09:03 +00:00
_addAction(tr::lng_context_archive_to_menu(tr::now), [=] {
2019-07-17 10:37:42 +00:00
auto toast = Ui::Toast::Config();
2020-04-13 08:34:51 +00:00
toast.text = { tr::lng_context_archive_to_menu_info(tr::now) };
2019-07-17 10:37:42 +00:00
toast.minWidth = toast.maxWidth = st::boxWideWidth;
toast.multiline = true;
toast.durationMs = kArchivedToastDuration;
Ui::Toast::Show(toast);
controller->session().settings().setArchiveInMainMenu(
!controller->session().settings().archiveInMainMenu());
controller->session().saveSettingsDelayed();
});
}
2019-04-15 11:54:03 +00:00
//
//void FolderFiller::addInfo() {
// auto controller = _controller;
// auto feed = _feed;
2019-06-19 15:09:03 +00:00
// _addAction(tr::lng_context_view_feed_info(tr::now), [=] {
2019-04-15 11:54:03 +00:00
// controller->showSection(Info::Memento(
// feed,
// Info::Section(Info::Section::Type::Profile)));
// });
//}
//
//void FolderFiller::addNotifications() {
// const auto feed = _feed;
2019-06-19 15:09:03 +00:00
// _addAction(tr::lng_feed_notifications(tr::now), [=] {
2019-04-15 11:54:03 +00:00
// Info::FeedProfile::NotificationsController::Start(feed);
// });
//}
//
//void FolderFiller::addSearch() {
// const auto feed = _feed;
2019-06-19 15:09:03 +00:00
// _addAction(tr::lng_profile_search_messages(tr::now), [=] {
2019-04-15 11:54:03 +00:00
// App::main()->searchInChat(feed);
// });
//}
//
//void FolderFiller::addUngroup() {
// const auto feed = _feed;
2019-06-19 15:09:03 +00:00
// //_addAction(tr::lng_feed_ungroup_all(tr::now), [=] { // #feed
2019-04-15 11:54:03 +00:00
// // PeerMenuUngroupFeed(feed);
// //});
//}
2017-11-06 18:03:20 +00:00
} // namespace
2018-07-23 13:11:56 +00:00
void PeerMenuExportChat(not_null<PeerData*> peer) {
2019-07-24 11:13:51 +00:00
peer->owner().startExport(peer);
2018-07-23 13:11:56 +00:00
}
void PeerMenuDeleteContact(not_null<UserData*> user) {
const auto text = tr::lng_sure_delete_contact(
tr::now,
lt_contact,
user->name);
const auto deleteSure = [=] {
Ui::hideLayer();
2019-06-06 15:42:15 +00:00
user->session().api().request(MTPcontacts_DeleteContacts(
MTP_vector<MTPInputUser>(1, user->inputUser)
)).done([=](const MTPUpdates &result) {
user->session().api().applyUpdates(result);
}).send();
};
Ui::show(Box<ConfirmBox>(
text,
2019-06-19 15:09:03 +00:00
tr::lng_box_delete(tr::now),
deleteSure));
}
2019-07-25 18:55:11 +00:00
void PeerMenuShareContactBox(
not_null<Window::SessionNavigation*> navigation,
not_null<UserData*> user) {
const auto weak = std::make_shared<QPointer<PeerListBox>>();
auto callback = [=](not_null<PeerData*> peer) {
if (!peer->canWrite()) {
Ui::show(Box<InformBox>(
2019-06-19 15:09:03 +00:00
tr::lng_forward_share_cant(tr::now)),
2019-09-18 11:19:05 +00:00
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);
2019-06-19 15:09:03 +00:00
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<ConfirmBox>(
tr::lng_forward_share_contact(tr::now, lt_recipient, recipient),
2019-06-19 15:09:03 +00:00
tr::lng_forward_send(tr::now),
[peer, user] {
2019-01-18 12:27:37 +00:00
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);
2019-09-18 11:19:05 +00:00
}), Ui::LayerOption::KeepOther);
};
*weak = Ui::show(Box<PeerListBox>(
2019-07-25 18:55:11 +00:00
std::make_unique<ChooseRecipientBoxController>(
navigation,
std::move(callback)),
[](not_null<PeerListBox*> box) {
2019-07-25 18:55:11 +00:00
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
}));
}
void PeerMenuCreatePoll(
not_null<PeerData*> peer,
PollData::Flags chosen,
PollData::Flags disabled) {
if (peer->isChannel() && !peer->isMegagroup()) {
chosen &= ~PollData::Flag::PublicVotes;
disabled |= PollData::Flag::PublicVotes;
}
2019-08-20 13:21:10 +00:00
const auto box = Ui::show(Box<CreatePollBox>(
&peer->session(),
chosen,
disabled,
2019-08-20 13:21:10 +00:00
Api::SendType::Normal));
2018-12-23 16:47:00 +00:00
const auto lock = box->lifetime().make_state<bool>(false);
box->submitRequests(
2019-08-20 13:21:10 +00:00
) | rpl::start_with_next([=](const CreatePollBox::Result &result) {
2018-12-23 16:47:00 +00:00
if (std::exchange(*lock, true)) {
return;
}
auto action = Api::SendAction(peer->owner().history(peer));
action.clearDraft = false;
2019-08-20 13:21:10 +00:00
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();
}
2019-07-24 11:13:51 +00:00
const auto api = &peer->session().api();
2019-08-20 13:21:10 +00:00
api->createPoll(result.poll, action, crl::guard(box, [=] {
box->closeBox();
}), crl::guard(box, [=](const RPCError &error) {
2018-12-23 16:47:00 +00:00
*lock = false;
2019-06-19 15:09:03 +00:00
box->submitFailed(tr::lng_attach_failed(tr::now));
}));
}, box->lifetime());
}
2019-06-12 13:26:04 +00:00
void PeerMenuBlockUserBox(
2019-09-18 11:19:05 +00:00
not_null<Ui::GenericBox*> box,
2019-06-12 14:13:49 +00:00
not_null<Window::Controller*> window,
not_null<UserData*> user,
bool suggestClearChat) {
2019-06-12 13:26:04 +00:00
using Flag = MTPDpeerSettings::Flag;
const auto settings = user->settings().value_or(Flag(0));
const auto name = user->shortName();
box->addRow(object_ptr<Ui::FlatLabel>(
box,
tr::lng_blocked_list_confirm_text(
lt_name,
rpl::single(Ui::Text::Bold(name)),
Ui::Text::WithEntities),
2019-06-12 13:26:04 +00:00
st::blockUserConfirmation));
box->addSkip(st::boxMediumSkip);
const auto report = (settings & Flag::f_report_spam)
? box->addRow(object_ptr<Ui::Checkbox>(
box,
2019-06-19 15:09:03 +00:00
tr::lng_report_spam(tr::now),
2019-06-12 13:26:04 +00:00
true,
st::defaultBoxCheckbox))
: nullptr;
if (report) {
box->addSkip(st::boxMediumSkip);
}
const auto clear = suggestClearChat
? box->addRow(object_ptr<Ui::Checkbox>(
box,
tr::lng_blocked_list_confirm_clear(tr::now),
true,
st::defaultBoxCheckbox))
: nullptr;
2019-06-12 13:26:04 +00:00
if (report || clear) {
box->addSkip(st::boxLittleSkip);
}
2019-06-12 13:26:04 +00:00
box->setTitle(tr::lng_blocked_list_confirm_title(
lt_name,
rpl::single(name)));
2019-06-12 13:26:04 +00:00
box->addButton(tr::lng_blocked_list_confirm_ok(), [=] {
2019-06-12 13:26:04 +00:00
const auto reportChecked = report && report->checked();
const auto clearChecked = clear && clear->checked();
2019-06-12 13:26:04 +00:00
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()));
2019-06-12 13:26:04 +00:00
}, st::attentionBoxButton);
box->addButton(tr::lng_cancel(), [=] {
2019-06-12 13:26:04 +00:00
box->closeBox();
});
}
void PeerMenuUnblockUserWithBotRestart(not_null<UserData*> user) {
user->session().api().unblockUser(user, [=] {
if (user->isBot() && !user->isSupport()) {
user->session().api().sendBotStart(user);
}
});
}
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
2019-07-25 18:55:11 +00:00
not_null<Window::SessionNavigation*> navigation,
MessageIdsList &&items,
FnMut<void()> &&successCallback) {
const auto weak = std::make_shared<QPointer<PeerListBox>>();
auto callback = [
ids = std::move(items),
callback = std::move(successCallback),
weak
](not_null<PeerData*> peer) mutable {
if (peer->isSelf()) {
2019-07-24 11:13:51 +00:00
auto items = peer->owner().idsToItems(ids);
if (!items.empty()) {
2019-07-24 11:13:51 +00:00
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, [] {
2019-06-19 15:09:03 +00:00
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<PeerListBox*> box) {
box->addButton(tr::lng_cancel(), [box] {
box->closeBox();
});
};
*weak = Ui::show(Box<PeerListBox>(
2019-07-25 18:55:11 +00:00
std::make_unique<ChooseRecipientBoxController>(
navigation,
std::move(callback)),
2019-09-18 11:19:05 +00:00
std::move(initBox)), Ui::LayerOption::KeepOther);
return weak->data();
}
QPointer<Ui::RpWidget> ShowSendNowMessagesBox(
not_null<Window::SessionNavigation*> navigation,
not_null<History*> history,
MessageIdsList &&items,
FnMut<void()> &&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);
2019-08-30 13:17:46 +00:00
const auto error = GetErrorTextForSending(
history->peer,
session->data().idsToItems(items),
TextWithTags());
if (!error.isEmpty()) {
auto config = Ui::Toast::Config();
config.multiline = true;
config.minWidth = st::msgMinWidth;
2020-04-13 08:34:51 +00:00
config.text = { error };
2019-08-30 13:17:46 +00:00
Ui::Toast::Show(config);
return { nullptr };
}
2019-09-18 11:19:05 +00:00
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
auto done = [
=,
list = std::move(items),
callback = std::move(successCallback)
]() mutable {
if (*box) {
(*box)->closeBox();
}
auto ids = QVector<MTPint>();
2019-08-30 13:17:46 +00:00
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<MTPint>(ids)
)).done([=](const MTPUpdates &result) {
session->api().applyUpdates(result);
2019-08-30 13:17:46 +00:00
}).fail([=](const RPCError &error) {
session->api().sendMessageFail(error, history->peer);
}).send();
if (callback) {
callback();
}
};
*box = Ui::show(
Box<ConfirmBox>(text, tr::lng_send_button(tr::now), std::move(done)),
2019-09-18 11:19:05 +00:00
Ui::LayerOption::KeepOther);
return box->data();
}
2019-07-25 18:55:11 +00:00
void PeerMenuAddChannelMembers(
not_null<Window::SessionNavigation*> navigation,
not_null<ChannelData*> channel) {
if (!channel->isMegagroup()
&& channel->membersCount() >= Global::ChatSizeMax()) {
Ui::show(
Box<MaxInviteBox>(channel),
2019-09-18 11:19:05 +00:00
Ui::LayerOption::KeepOther);
return;
}
2019-07-24 11:13:51 +00:00
const auto api = &channel->session().api();
api->requestChannelMembersForAdd(channel, [=](
const MTPchannels_ChannelParticipants &result) {
api->parseChannelParticipants(channel, result, [&](
int availableCount,
2017-11-20 19:54:05 +00:00
const QVector<MTPChannelParticipant> &list) {
auto already = (
list
2019-01-10 11:15:13 +00:00
) | ranges::view::transform([](const MTPChannelParticipant &p) {
return p.match([](const auto &data) {
2019-07-05 13:38:38 +00:00
return data.vuser_id().v;
2019-01-10 11:15:13 +00:00
});
2019-07-24 11:13:51 +00:00
}) | ranges::view::transform([&](UserId userId) {
return channel->owner().userLoaded(userId);
2017-11-20 19:54:05 +00:00
}) | ranges::view::filter([](UserData *user) {
return (user != nullptr);
2017-11-21 11:59:18 +00:00
}) | ranges::to_vector;
2017-11-20 19:54:05 +00:00
AddParticipantsBoxController::Start(
2019-07-25 18:55:11 +00:00
navigation,
2017-11-20 19:54:05 +00:00
channel,
{ already.begin(), already.end() });
});
2019-07-24 11:13:51 +00:00
});
}
void PeerMenuAddMuteAction(
not_null<PeerData*> peer,
const PeerMenuCallback &addAction) {
2019-07-24 11:13:51 +00:00
peer->owner().requestNotifySettings(peer);
const auto muteText = [](bool isMuted) {
2019-06-19 15:09:03 +00:00
return isMuted
? tr::lng_enable_notifications_from_tray(tr::now)
: tr::lng_disable_notifications_from_tray(tr::now);
};
const auto muteAction = addAction(QString("-"), [=] {
2019-07-24 11:13:51 +00:00
if (!peer->owner().notifyIsMuted(peer)) {
Ui::show(Box<MuteSettingsBox>(peer));
} else {
2019-07-24 11:13:51 +00:00
peer->owner().updateNotifySettings(peer, 0);
}
});
2018-11-21 10:09:46 +00:00
const auto lifetime = Ui::CreateChild<rpl::lifetime>(muteAction);
Info::Profile::NotificationsEnabledValue(
peer
) | rpl::start_with_next([=](bool enabled) {
muteAction->setText(muteText(!enabled));
2018-11-21 10:09:46 +00:00
}, *lifetime);
}
2018-03-06 17:07:42 +00:00
// #feed
//void PeerMenuUngroupFeed(not_null<Data::Feed*> feed) {
// Ui::show(Box<ConfirmBox>(
2019-06-19 15:09:03 +00:00
// tr::lng_feed_sure_ungroup_all(tr::now),
// tr::lng_feed_ungroup_sure(tr::now),
2019-07-24 11:13:51 +00:00
// [=] { Ui::hideLayer(); feed->session().api().ungroupAllFromFeed(feed); }));
2018-03-06 17:07:42 +00:00
//}
//
void ToggleHistoryArchived(not_null<History*> history, bool archived) {
const auto callback = [=] {
2019-07-17 10:37:42 +00:00
auto toast = Ui::Toast::Config();
2020-04-13 08:34:51 +00:00
toast.text = { archived
2019-06-19 15:09:03 +00:00
? tr::lng_archived_added(tr::now)
2020-04-13 08:34:51 +00:00
: tr::lng_archived_removed(tr::now) };
2019-07-17 10:37:42 +00:00
toast.minWidth = toast.maxWidth = st::boxWideWidth;
toast.multiline = true;
2019-04-25 09:20:20 +00:00
if (archived) {
toast.durationMs = kArchivedToastDuration;
}
Ui::Toast::Show(toast);
};
history->session().api().toggleHistoryArchived(
history,
archived,
callback);
}
Fn<void()> ClearHistoryHandler(not_null<PeerData*> peer) {
return [=] {
2019-09-18 11:19:05 +00:00
Ui::show(
Box<DeleteMessagesBox>(peer, true),
Ui::LayerOption::KeepOther);
};
}
Fn<void()> DeleteAndLeaveHandler(not_null<PeerData*> peer) {
return [=] {
2019-09-18 11:19:05 +00:00
Ui::show(
Box<DeleteMessagesBox>(peer, false),
Ui::LayerOption::KeepOther);
};
}
2017-11-06 18:03:20 +00:00
void FillPeerMenu(
not_null<SessionController*> controller,
2017-11-06 18:03:20 +00:00
not_null<PeerData*> peer,
2020-03-17 13:04:30 +00:00
FilterId filterId,
2017-11-06 18:03:20 +00:00
const PeerMenuCallback &callback,
PeerMenuSource source) {
2020-03-17 13:04:30 +00:00
Filler filler(controller, peer, filterId, callback, source);
2017-11-06 18:03:20 +00:00
filler.fill();
}
2019-04-15 11:54:03 +00:00
void FillFolderMenu(
not_null<SessionController*> controller,
2019-04-15 11:54:03 +00:00
not_null<Data::Folder*> folder,
const PeerMenuCallback &callback,
PeerMenuSource source) {
2019-04-15 11:54:03 +00:00
FolderFiller filler(controller, folder, callback, source);
filler.fill();
}
2017-11-06 18:03:20 +00:00
} // namespace Window