928 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			928 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 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/generic_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 "auth_session.h"
 | |
| #include "apiwrap.h"
 | |
| #include "mainwidget.h"
 | |
| #include "mainwindow.h"
 | |
| #include "observer_peer.h"
 | |
| #include "history/history.h"
 | |
| #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 "dialogs/dialogs_key.h"
 | |
| #include "boxes/peers/edit_peer_info_box.h"
 | |
| #include "styles/style_boxes.h"
 | |
| #include "styles/style_window.h" // st::windowMinWidth
 | |
| 
 | |
| namespace Window {
 | |
| namespace {
 | |
| 
 | |
| constexpr auto kArchivedToastDuration = crl::time(3000);
 | |
| 
 | |
| class Filler {
 | |
| public:
 | |
| 	Filler(
 | |
| 		not_null<SessionController*> controller,
 | |
| 		not_null<PeerData*> peer,
 | |
| 		const PeerMenuCallback &addAction,
 | |
| 		PeerMenuSource source);
 | |
| 	void fill();
 | |
| 
 | |
| private:
 | |
| 	bool showInfo();
 | |
| 	bool showToggleArchived();
 | |
| 	bool showTogglePin();
 | |
| 	void addTogglePin();
 | |
| 	void addInfo();
 | |
| 	//void addSearch();
 | |
| 	void addToggleUnreadMark();
 | |
| 	void addToggleArchive();
 | |
| 	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;
 | |
| 	not_null<PeerData*> _peer;
 | |
| 	const PeerMenuCallback &_addAction;
 | |
| 	PeerMenuSource _source;
 | |
| 
 | |
| };
 | |
| 
 | |
| class FolderFiller {
 | |
| public:
 | |
| 	FolderFiller(
 | |
| 		not_null<SessionController*> controller,
 | |
| 		not_null<Data::Folder*> 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<SessionController*> _controller;
 | |
| 	not_null<Data::Folder*> _folder;
 | |
| 	const PeerMenuCallback &_addAction;
 | |
| 	PeerMenuSource _source;
 | |
| 
 | |
| };
 | |
| 
 | |
| History *FindWastedPin(Data::Folder *folder) {
 | |
| 	const auto &order = Auth().data().pinnedChatsOrder(folder);
 | |
| 	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<ChatData*> chat) {
 | |
| 	AddParticipantsBoxController::Start(chat);
 | |
| }
 | |
| 
 | |
| bool PinnedLimitReached(Dialogs::Key key) {
 | |
| 	Expects(key.entry()->folderKnown());
 | |
| 
 | |
| 	const auto folder = key.entry()->folder();
 | |
| 	const auto pinnedCount = Auth().data().pinnedChatsCount(folder);
 | |
| 	const auto pinnedMax = Auth().data().pinnedChatsLimit(folder);
 | |
| 	if (pinnedCount < pinnedMax) {
 | |
| 		return false;
 | |
| 	}
 | |
| 	// Some old chat, that was converted, maybe is still pinned.
 | |
| 	if (const auto wasted = FindWastedPin(folder)) {
 | |
| 		Auth().data().setChatPinned(wasted, false);
 | |
| 		Auth().data().setChatPinned(key, true);
 | |
| 		Auth().api().savePinnedOrder(folder);
 | |
| 	} else {
 | |
| 		auto errorText = lng_error_pinned_max(
 | |
| 			lt_count,
 | |
| 			pinnedMax);
 | |
| 		Ui::show(Box<InformBox>(errorText));
 | |
| 	}
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| void TogglePinnedDialog(Dialogs::Key key) {
 | |
| 	if (!key.entry()->folderKnown()) {
 | |
| 		return;
 | |
| 	}
 | |
| 	const auto isPinned = !key.entry()->isPinnedDialog();
 | |
| 	if (isPinned && PinnedLimitReached(key)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	Auth().data().setChatPinned(key, 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) {
 | |
| 			Auth().data().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();
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| Filler::Filler(
 | |
| 	not_null<SessionController*> controller,
 | |
| 	not_null<PeerData*> peer,
 | |
| 	const PeerMenuCallback &addAction,
 | |
| 	PeerMenuSource source)
 | |
| : _controller(controller)
 | |
| , _peer(peer)
 | |
| , _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 (
 | |
| 		!Auth().settings().thirdSectionInfoEnabled() &&
 | |
| 		!Auth().settings().tabbedReplacedWithInfo()) {
 | |
| 		return true;
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| 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);
 | |
| }
 | |
| 
 | |
| bool Filler::showTogglePin() {
 | |
| 	if (_source != PeerMenuSource::ChatsList) {
 | |
| 		return false;
 | |
| 	}
 | |
| 	const auto history = _peer->owner().historyLoaded(_peer);
 | |
| 	return history && !history->fixedOnTopIndex();
 | |
| }
 | |
| 
 | |
| void Filler::addTogglePin() {
 | |
| 	auto peer = _peer;
 | |
| 	auto isPinned = false;
 | |
| 	if (auto history = peer->owner().historyLoaded(peer)) {
 | |
| 		isPinned = history->isPinnedDialog();
 | |
| 	}
 | |
| 	auto pinText = [](bool isPinned) {
 | |
| 		return lang(isPinned
 | |
| 			? lng_context_unpin_from_top
 | |
| 			: lng_context_pin_to_top);
 | |
| 	};
 | |
| 	auto pinToggle = [=] {
 | |
| 		TogglePinnedDialog(peer->owner().history(peer));
 | |
| 	};
 | |
| 	auto pinAction = _addAction(pinText(isPinned), pinToggle);
 | |
| 
 | |
| 	const auto lifetime = Ui::CreateChild<rpl::lifetime>(pinAction);
 | |
| 	Notify::PeerUpdateViewer(
 | |
| 		peer,
 | |
| 		Notify::PeerUpdate::Flag::ChatPinnedChanged
 | |
| 	) | rpl::start_with_next([peer, pinAction, pinText] {
 | |
| 		auto isPinned = peer->owner().history(peer)->isPinnedDialog();
 | |
| 		pinAction->setText(pinText(isPinned));
 | |
| 	}, *lifetime);
 | |
| }
 | |
| 
 | |
| void Filler::addInfo() {
 | |
| 	const auto controller = _controller;
 | |
| 	const auto peer = _peer;
 | |
| 	const auto infoKey = (peer->isChat() || peer->isMegagroup())
 | |
| 		? lng_context_view_group
 | |
| 		: (peer->isUser()
 | |
| 			? lng_context_view_profile
 | |
| 			: lng_context_view_channel);
 | |
| 	_addAction(lang(infoKey), [=] {
 | |
| 		controller->showPeerInfo(peer);
 | |
| 	});
 | |
| }
 | |
| 
 | |
| //void Filler::addSearch() {
 | |
| //	_addAction(lang(lng_profile_search_messages), [peer = _peer] {
 | |
| //		App::main()->searchInChat(peer->owner().history(peer));
 | |
| //	});
 | |
| //}
 | |
| 
 | |
| void Filler::addToggleUnreadMark() {
 | |
| 	const auto peer = _peer;
 | |
| 	const auto isUnread = [](not_null<PeerData*> peer) {
 | |
| 		if (const auto history = peer->owner().historyLoaded(peer)) {
 | |
| 			return (history->chatListUnreadCount() > 0)
 | |
| 				|| (history->chatListUnreadMark());
 | |
| 		}
 | |
| 		return false;
 | |
| 	};
 | |
| 	const auto label = [=](not_null<PeerData*> peer) {
 | |
| 		return lang(isUnread(peer)
 | |
| 			? lng_context_mark_read
 | |
| 			: lng_context_mark_unread);
 | |
| 	};
 | |
| 	auto action = _addAction(label(peer), [=] {
 | |
| 		const auto markAsRead = isUnread(peer);
 | |
| 		const auto handle = [&](not_null<History*> history) {
 | |
| 			if (markAsRead) {
 | |
| 				Auth().api().readServerHistory(history);
 | |
| 			} else {
 | |
| 				Auth().api().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<rpl::lifetime>(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 archived = [&] {
 | |
| 		const auto history = peer->owner().historyLoaded(peer);
 | |
| 		return history && history->folder();
 | |
| 	}();
 | |
| 	const auto toggle = [=] {
 | |
| 		ToggleHistoryArchived(
 | |
| 			peer->owner().history(peer),
 | |
| 			!archived);
 | |
| 	};
 | |
| 	_addAction(
 | |
| 		lang(archived ? lng_archived_remove : lng_archived_add),
 | |
| 		toggle);
 | |
| }
 | |
| 
 | |
| void Filler::addBlockUser(not_null<UserData*> user) {
 | |
| 	const auto window = &_controller->window()->controller();
 | |
| 	const auto blockText = [](not_null<UserData*> user) {
 | |
| 		return lang(user->isBlocked()
 | |
| 			? ((user->isBot() && !user->isSupport())
 | |
| 				? lng_profile_restart_bot
 | |
| 				: lng_profile_unblock_user)
 | |
| 			: ((user->isBot() && !user->isSupport())
 | |
| 				? lng_profile_block_bot
 | |
| 				: lng_profile_block_user));
 | |
| 	};
 | |
| 	const auto blockAction = _addAction(blockText(user), [=] {
 | |
| 		if (user->isBlocked()) {
 | |
| 			user->session().api().unblockUser(user);
 | |
| 		} else if (user->isBot()) {
 | |
| 			user->session().api().blockUser(user);
 | |
| 		} else {
 | |
| 			window->show(Box(PeerMenuBlockUserBox, window, user));
 | |
| 		}
 | |
| 	});
 | |
| 
 | |
| 	const auto lifetime = Ui::CreateChild<rpl::lifetime>(blockAction);
 | |
| 	Notify::PeerUpdateViewer(
 | |
| 		_peer,
 | |
| 		Notify::PeerUpdate::Flag::UserIsBlocked
 | |
| 	) | rpl::start_with_next([=] {
 | |
| 		blockAction->setText(blockText(user));
 | |
| 	}, *lifetime);
 | |
| 
 | |
| 	if (user->blockStatus() == UserData::BlockStatus::Unknown) {
 | |
| 		Auth().api().requestFullPeer(user);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void Filler::addUserActions(not_null<UserData*> user) {
 | |
| 	const auto window = &_controller->window()->controller();
 | |
| 	if (_source != PeerMenuSource::ChatsList) {
 | |
| 		if (Auth().supportMode()) {
 | |
| 			_addAction("Edit support info", [=] {
 | |
| 				Auth().supportHelper().editInfo(user);
 | |
| 			});
 | |
| 		}
 | |
| 		if (!user->isContact() && !user->isSelf() && !user->isBot()) {
 | |
| 			_addAction(
 | |
| 				lang(lng_info_add_as_contact),
 | |
| 				[=] { window->show(Box(EditContactBox, window, user)); });
 | |
| 		}
 | |
| 		if (user->canShareThisContact()) {
 | |
| 			_addAction(
 | |
| 				lang(lng_info_share_contact),
 | |
| 				[=] { PeerMenuShareContactBox(user); });
 | |
| 		}
 | |
| 		if (user->isContact() && !user->isSelf()) {
 | |
| 			_addAction(
 | |
| 				lang(lng_info_edit_contact),
 | |
| 				[=] { window->show(Box(EditContactBox, window, user)); });
 | |
| 			_addAction(
 | |
| 				lang(lng_info_delete_contact),
 | |
| 				[=] { PeerMenuDeleteContact(user); });
 | |
| 		}
 | |
| 		if (user->isBot() && !user->botInfo->cantJoinGroups) {
 | |
| 			_addAction(
 | |
| 				lang(lng_profile_invite_to_group),
 | |
| 				[=] { AddBotToGroupBoxController::Start(user); });
 | |
| 		}
 | |
| 		if (user->canExportChatHistory()) {
 | |
| 			_addAction(
 | |
| 				lang(lng_profile_export_chat),
 | |
| 				[=] { PeerMenuExportChat(user); });
 | |
| 		}
 | |
| 	}
 | |
| 	_addAction(
 | |
| 		lang(lng_profile_delete_conversation),
 | |
| 		DeleteAndLeaveHandler(user));
 | |
| 	_addAction(
 | |
| 		lang(lng_profile_clear_history),
 | |
| 		ClearHistoryHandler(user));
 | |
| 	if (!user->isInaccessible()
 | |
| 		&& user != Auth().user()
 | |
| 		&& _source != PeerMenuSource::ChatsList) {
 | |
| 		addBlockUser(user);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void Filler::addChatActions(not_null<ChatData*> chat) {
 | |
| 	if (_source != PeerMenuSource::ChatsList) {
 | |
| 		const auto controller = _controller;
 | |
| 		if (EditPeerInfoBox::Available(chat)) {
 | |
| 			const auto text = lang(lng_manage_group_title);
 | |
| 			_addAction(text, [=] {
 | |
| 				controller->showEditPeerBox(chat);
 | |
| 			});
 | |
| 		}
 | |
| 		if (chat->canAddMembers()) {
 | |
| 			_addAction(
 | |
| 				lang(lng_profile_add_participant),
 | |
| 				[chat] { AddChatMembers(chat); });
 | |
| 		}
 | |
| 		if (chat->canSendPolls()) {
 | |
| 			_addAction(
 | |
| 				lang(lng_polls_create),
 | |
| 				[=] { PeerMenuCreatePoll(chat); });
 | |
| 		}
 | |
| 		if (chat->canExportChatHistory()) {
 | |
| 			_addAction(
 | |
| 				lang(lng_profile_export_chat),
 | |
| 				[=] { PeerMenuExportChat(chat); });
 | |
| 		}
 | |
| 	}
 | |
| 	_addAction(
 | |
| 		lang(lng_profile_clear_and_exit),
 | |
| 		DeleteAndLeaveHandler(_peer));
 | |
| 	_addAction(
 | |
| 		lang(lng_profile_clear_history),
 | |
| 		ClearHistoryHandler(_peer));
 | |
| }
 | |
| 
 | |
| void Filler::addChannelActions(not_null<ChannelData*> channel) {
 | |
| 	auto isGroup = channel->isMegagroup();
 | |
| 	//if (!isGroup) { // #feed
 | |
| 	//	const auto feed = channel->feed();
 | |
| 	//	const auto grouped = (feed != nullptr);
 | |
| 	//	if (!grouped || feed->channels().size() > 1) {
 | |
| 	//		_addAction( // #feed
 | |
| 	//			lang(grouped ? lng_feed_ungroup : lng_feed_group),
 | |
| 	//			[=] { ToggleChannelGrouping(channel, !grouped); });
 | |
| 	//	}
 | |
| 	//}
 | |
| 	if (_source != PeerMenuSource::ChatsList) {
 | |
| 		if (EditPeerInfoBox::Available(channel)) {
 | |
| 			const auto controller = _controller;
 | |
| 			const auto text = lang(isGroup
 | |
| 				? lng_manage_group_title
 | |
| 				: lng_manage_channel_title);
 | |
| 			_addAction(text, [=] {
 | |
| 				controller->showEditPeerBox(channel);
 | |
| 			});
 | |
| 		}
 | |
| 		if (channel->canAddMembers()) {
 | |
| 			_addAction(
 | |
| 				lang(lng_channel_add_members),
 | |
| 				[channel] { PeerMenuAddChannelMembers(channel); });
 | |
| 		}
 | |
| 		if (channel->canSendPolls()) {
 | |
| 			_addAction(
 | |
| 				lang(lng_polls_create),
 | |
| 				[=] { PeerMenuCreatePoll(channel); });
 | |
| 		}
 | |
| 		if (channel->canExportChatHistory()) {
 | |
| 			_addAction(
 | |
| 				lang(isGroup
 | |
| 					? lng_profile_export_chat
 | |
| 					: lng_profile_export_channel),
 | |
| 				[=] { PeerMenuExportChat(channel); });
 | |
| 		}
 | |
| 	}
 | |
| 	if (channel->amIn()) {
 | |
| 		if (isGroup && !channel->isPublic()) {
 | |
| 			_addAction(
 | |
| 				lang(lng_profile_clear_history),
 | |
| 				ClearHistoryHandler(channel));
 | |
| 		}
 | |
| 		auto text = lang(isGroup
 | |
| 			? lng_profile_leave_group
 | |
| 			: lng_profile_leave_channel);
 | |
| 		_addAction(text, DeleteAndLeaveHandler(channel));
 | |
| 	} else {
 | |
| 		auto text = lang(isGroup
 | |
| 			? lng_profile_join_group
 | |
| 			: lng_profile_join_channel);
 | |
| 		_addAction(
 | |
| 			text,
 | |
| 			[channel] { Auth().api().joinChannel(channel); });
 | |
| 	}
 | |
| 	if (_source != PeerMenuSource::ChatsList) {
 | |
| 		auto needReport = !channel->amCreator()
 | |
| 			&& (!isGroup || channel->isPublic());
 | |
| 		if (needReport) {
 | |
| 			_addAction(lang(lng_profile_report), [channel] {
 | |
| 				Ui::show(Box<ReportBox>(channel));
 | |
| 			});
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void Filler::fill() {
 | |
| 	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<SessionController*> controller,
 | |
| 	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();
 | |
| 	const auto text = lang(hidden
 | |
| 		? lng_context_archive_expand
 | |
| 		: lng_context_archive_collapse);
 | |
| 	_addAction(text, [=] {
 | |
| 		controller->session().settings().setArchiveCollapsed(!hidden);
 | |
| 		controller->session().saveSettingsDelayed();
 | |
| 	});
 | |
| 
 | |
| 	_addAction(lang(lng_context_archive_to_menu), [=] {
 | |
| 		Ui::Toast::Config toast;
 | |
| 		toast.text = lang(lng_context_archive_to_menu_info);
 | |
| 		toast.maxWidth = st::boxWideWidth;
 | |
| 		toast.durationMs = kArchivedToastDuration;
 | |
| 		Ui::Toast::Show(toast);
 | |
| 
 | |
| 		controller->session().settings().setArchiveInMainMenu(
 | |
| 			!controller->session().settings().archiveInMainMenu());
 | |
| 		controller->session().saveSettingsDelayed();
 | |
| 	});
 | |
| }
 | |
| //
 | |
| //void FolderFiller::addInfo() {
 | |
| //	auto controller = _controller;
 | |
| //	auto feed = _feed;
 | |
| //	_addAction(lang(lng_context_view_feed_info), [=] {
 | |
| //		controller->showSection(Info::Memento(
 | |
| //			feed,
 | |
| //			Info::Section(Info::Section::Type::Profile)));
 | |
| //	});
 | |
| //}
 | |
| //
 | |
| //void FolderFiller::addNotifications() {
 | |
| //	const auto feed = _feed;
 | |
| //	_addAction(lang(lng_feed_notifications), [=] {
 | |
| //		Info::FeedProfile::NotificationsController::Start(feed);
 | |
| //	});
 | |
| //}
 | |
| //
 | |
| //void FolderFiller::addSearch() {
 | |
| //	const auto feed = _feed;
 | |
| //	_addAction(lang(lng_profile_search_messages), [=] {
 | |
| //		App::main()->searchInChat(feed);
 | |
| //	});
 | |
| //}
 | |
| //
 | |
| //void FolderFiller::addUngroup() {
 | |
| //	const auto feed = _feed;
 | |
| //	//_addAction(lang(lng_feed_ungroup_all), [=] { // #feed
 | |
| //	//	PeerMenuUngroupFeed(feed);
 | |
| //	//});
 | |
| //}
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| void PeerMenuExportChat(not_null<PeerData*> peer) {
 | |
| 	Auth().data().startExport(peer);
 | |
| }
 | |
| 
 | |
| void PeerMenuDeleteContact(not_null<UserData*> user) {
 | |
| 	const auto text = lng_sure_delete_contact(
 | |
| 		lt_contact,
 | |
| 		App::peerName(user));
 | |
| 	const auto deleteSure = [=] {
 | |
| 		Ui::hideLayer();
 | |
| 		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,
 | |
| 		lang(lng_box_delete),
 | |
| 		deleteSure));
 | |
| }
 | |
| 
 | |
| void PeerMenuShareContactBox(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>(
 | |
| 				lang(lng_forward_share_cant)),
 | |
| 				LayerOption::KeepOther);
 | |
| 			return;
 | |
| 		} else if (peer->isSelf()) {
 | |
| 			auto options = ApiWrap::SendOptions(peer->owner().history(peer));
 | |
| 			Auth().api().shareContact(user, options);
 | |
| 			Ui::Toast::Show(lang(lng_share_done));
 | |
| 			if (auto strong = *weak) {
 | |
| 				strong->closeBox();
 | |
| 			}
 | |
| 			return;
 | |
| 		}
 | |
| 		auto recipient = peer->isUser()
 | |
| 			? peer->name
 | |
| 			: '\xAB' + peer->name + '\xBB';
 | |
| 		Ui::show(Box<ConfirmBox>(
 | |
| 			lng_forward_share_contact(lt_recipient, recipient),
 | |
| 			lang(lng_forward_send),
 | |
| 			[peer, user] {
 | |
| 				const auto history = peer->owner().history(peer);
 | |
| 				Ui::showPeerHistory(history, ShowAtTheEndMsgId);
 | |
| 				auto options = ApiWrap::SendOptions(history);
 | |
| 				Auth().api().shareContact(user, options);
 | |
| 			}), LayerOption::KeepOther);
 | |
| 	};
 | |
| 	*weak = Ui::show(Box<PeerListBox>(
 | |
| 		std::make_unique<ChooseRecipientBoxController>(std::move(callback)),
 | |
| 		[](not_null<PeerListBox*> box) {
 | |
| 			box->addButton(tr::lng_cancel(), [box] {
 | |
| 				box->closeBox();
 | |
| 			});
 | |
| 		}));
 | |
| }
 | |
| 
 | |
| void PeerMenuCreatePoll(not_null<PeerData*> peer) {
 | |
| 	const auto box = Ui::show(Box<CreatePollBox>());
 | |
| 	const auto lock = box->lifetime().make_state<bool>(false);
 | |
| 	box->submitRequests(
 | |
| 	) | rpl::start_with_next([=](const PollData &result) {
 | |
| 		if (std::exchange(*lock, true)) {
 | |
| 			return;
 | |
| 		}
 | |
| 		auto options = ApiWrap::SendOptions(peer->owner().history(peer));
 | |
| 		if (const auto id = App::main()->currentReplyToIdFor(options.history)) {
 | |
| 			options.replyTo = id;
 | |
| 		}
 | |
| 		if (const auto localDraft = options.history->localDraft()) {
 | |
| 			options.clearDraft = localDraft->textWithTags.text.isEmpty();
 | |
| 		}
 | |
| 
 | |
| 		Auth().api().createPoll(result, options, crl::guard(box, [=] {
 | |
| 			box->closeBox();
 | |
| 		}), crl::guard(box, [=](const RPCError &error) {
 | |
| 			*lock = false;
 | |
| 			box->submitFailed(lang(lng_attach_failed));
 | |
| 		}));
 | |
| 	}, box->lifetime());
 | |
| }
 | |
| 
 | |
| void PeerMenuBlockUserBox(
 | |
| 		not_null<GenericBox*> box,
 | |
| 		not_null<Window::Controller*> window,
 | |
| 		not_null<UserData*> user) {
 | |
| 	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),
 | |
| 		st::blockUserConfirmation));
 | |
| 
 | |
| 	box->addSkip(st::boxMediumSkip);
 | |
| 
 | |
| 	const auto report = (settings & Flag::f_report_spam)
 | |
| 		? box->addRow(object_ptr<Ui::Checkbox>(
 | |
| 			box,
 | |
| 			lang(lng_report_spam),
 | |
| 			true,
 | |
| 			st::defaultBoxCheckbox))
 | |
| 		: nullptr;
 | |
| 
 | |
| 	if (report) {
 | |
| 		box->addSkip(st::boxMediumSkip);
 | |
| 	}
 | |
| 
 | |
| 	const auto clear = box->addRow(object_ptr<Ui::Checkbox>(
 | |
| 		box,
 | |
| 		lang(lng_blocked_list_confirm_clear),
 | |
| 		true,
 | |
| 		st::defaultBoxCheckbox));
 | |
| 
 | |
| 	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->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(
 | |
| 			lng_new_contact_block_done(lt_user, user->shortName()));
 | |
| 	}, st::attentionBoxButton);
 | |
| 
 | |
| 	box->addButton(tr::lng_cancel(), [=] {
 | |
| 		box->closeBox();
 | |
| 	});
 | |
| }
 | |
| 
 | |
| QPointer<Ui::RpWidget> ShowForwardMessagesBox(
 | |
| 		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()) {
 | |
| 			auto items = Auth().data().idsToItems(ids);
 | |
| 			if (!items.empty()) {
 | |
| 				auto options = ApiWrap::SendOptions(peer->owner().history(peer));
 | |
| 				options.generateLocal = false;
 | |
| 				Auth().api().forwardMessages(std::move(items), options, [] {
 | |
| 					Ui::Toast::Show(lang(lng_share_done));
 | |
| 				});
 | |
| 			}
 | |
| 		} else {
 | |
| 			App::main()->setForwardDraft(peer->id, std::move(ids));
 | |
| 		}
 | |
| 		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>(
 | |
| 		std::make_unique<ChooseRecipientBoxController>(std::move(callback)),
 | |
| 		std::move(initBox)), LayerOption::KeepOther);
 | |
| 	return weak->data();
 | |
| }
 | |
| 
 | |
| void PeerMenuAddChannelMembers(not_null<ChannelData*> channel) {
 | |
| 	if (!channel->isMegagroup()
 | |
| 		&& channel->membersCount() >= Global::ChatSizeMax()) {
 | |
| 		Ui::show(
 | |
| 			Box<MaxInviteBox>(channel),
 | |
| 			LayerOption::KeepOther);
 | |
| 		return;
 | |
| 	}
 | |
| 	auto callback = [=](const MTPchannels_ChannelParticipants &result) {
 | |
| 		Auth().api().parseChannelParticipants(channel, result, [&](
 | |
| 				int availableCount,
 | |
| 				const QVector<MTPChannelParticipant> &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 Auth().data().userLoaded(userId);
 | |
| 			}) | ranges::view::filter([](UserData *user) {
 | |
| 				return (user != nullptr);
 | |
| 			}) | ranges::to_vector;
 | |
| 
 | |
| 			AddParticipantsBoxController::Start(
 | |
| 				channel,
 | |
| 				{ already.begin(), already.end() });
 | |
| 		});
 | |
| 	};
 | |
| 	Auth().api().requestChannelMembersForAdd(channel, callback);
 | |
| }
 | |
| 
 | |
| void PeerMenuAddMuteAction(
 | |
| 		not_null<PeerData*> peer,
 | |
| 		const PeerMenuCallback &addAction) {
 | |
| 	Auth().data().requestNotifySettings(peer);
 | |
| 	const auto muteText = [](bool isMuted) {
 | |
| 		return lang(isMuted
 | |
| 			? lng_enable_notifications_from_tray
 | |
| 			: lng_disable_notifications_from_tray);
 | |
| 	};
 | |
| 	const auto muteAction = addAction(QString("-"), [=] {
 | |
| 		if (!Auth().data().notifyIsMuted(peer)) {
 | |
| 			Ui::show(Box<MuteSettingsBox>(peer));
 | |
| 		} else {
 | |
| 			Auth().data().updateNotifySettings(peer, 0);
 | |
| 		}
 | |
| 	});
 | |
| 
 | |
| 	const auto lifetime = Ui::CreateChild<rpl::lifetime>(muteAction);
 | |
| 	Info::Profile::NotificationsEnabledValue(
 | |
| 		peer
 | |
| 	) | rpl::start_with_next([=](bool enabled) {
 | |
| 		muteAction->setText(muteText(!enabled));
 | |
| 	}, *lifetime);
 | |
| }
 | |
| // #feed
 | |
| //void PeerMenuUngroupFeed(not_null<Data::Feed*> feed) {
 | |
| //	Ui::show(Box<ConfirmBox>(
 | |
| //		lang(lng_feed_sure_ungroup_all),
 | |
| //		lang(lng_feed_ungroup_sure),
 | |
| //		[=] { Ui::hideLayer(); Auth().api().ungroupAllFromFeed(feed); }));
 | |
| //}
 | |
| //
 | |
| void ToggleHistoryArchived(not_null<History*> history, bool archived) {
 | |
| 	const auto callback = [=] {
 | |
| 		Ui::Toast::Config toast;
 | |
| 		toast.text = lang(archived
 | |
| 			? lng_archived_added
 | |
| 			: lng_archived_removed);
 | |
| 		toast.maxWidth = st::boxWideWidth;
 | |
| 		if (archived) {
 | |
| 			toast.durationMs = kArchivedToastDuration;
 | |
| 		}
 | |
| 		Ui::Toast::Show(toast);
 | |
| 	};
 | |
| 	history->session().api().toggleHistoryArchived(
 | |
| 		history,
 | |
| 		archived,
 | |
| 		callback);
 | |
| }
 | |
| 
 | |
| Fn<void()> ClearHistoryHandler(not_null<PeerData*> peer) {
 | |
| 	return [=] {
 | |
| 		Ui::show(Box<DeleteMessagesBox>(peer, true), LayerOption::KeepOther);
 | |
| 	};
 | |
| }
 | |
| 
 | |
| Fn<void()> DeleteAndLeaveHandler(not_null<PeerData*> peer) {
 | |
| 	return [=] {
 | |
| 		Ui::show(Box<DeleteMessagesBox>(peer, false), LayerOption::KeepOther);
 | |
| 	};
 | |
| }
 | |
| 
 | |
| void FillPeerMenu(
 | |
| 		not_null<SessionController*> controller,
 | |
| 		not_null<PeerData*> peer,
 | |
| 		const PeerMenuCallback &callback,
 | |
| 		PeerMenuSource source) {
 | |
| 	Filler filler(controller, peer, callback, source);
 | |
| 	filler.fill();
 | |
| }
 | |
| 
 | |
| void FillFolderMenu(
 | |
| 		not_null<SessionController*> controller,
 | |
| 		not_null<Data::Folder*> folder,
 | |
| 		const PeerMenuCallback &callback,
 | |
| 		PeerMenuSource source) {
 | |
| 	FolderFiller filler(controller, folder, callback, source);
 | |
| 	filler.fill();
 | |
| }
 | |
| 
 | |
| } // namespace Window
 | 
