Allow reporting / banning from reactions in groups.
This commit is contained in:
		
							parent
							
								
									c06699e8e7
								
							
						
					
					
						commit
						5401d00548
					
				
					 12 changed files with 241 additions and 44 deletions
				
			
		|  | @ -1505,6 +1505,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| "lng_report_messages_none" = "Select Messages"; | "lng_report_messages_none" = "Select Messages"; | ||||||
| "lng_report_messages_count#one" = "Report {count} Message"; | "lng_report_messages_count#one" = "Report {count} Message"; | ||||||
| "lng_report_messages_count#other" = "Report {count} Messages"; | "lng_report_messages_count#other" = "Report {count} Messages"; | ||||||
|  | "lng_report_reaction" = "Report reaction"; | ||||||
|  | "lng_report_and_ban" = "Ban and report"; | ||||||
|  | "lng_report_reaction_title" = "Report reaction"; | ||||||
|  | "lng_report_reaction_about" = "Are you sure you want to report reactions from this user?"; | ||||||
|  | "lng_report_and_ban_button" = "Ban user"; | ||||||
| "lng_report_details_about" = "Please enter any additional details relevant to your report."; | "lng_report_details_about" = "Please enter any additional details relevant to your report."; | ||||||
| "lng_report_details" = "Additional Details"; | "lng_report_details" = "Additional Details"; | ||||||
| "lng_report_reason_spam" = "Spam"; | "lng_report_reason_spam" = "Spam"; | ||||||
|  |  | ||||||
|  | @ -331,7 +331,7 @@ void FieldAutocomplete::showFiltered( | ||||||
| 		plainQuery = base::StringViewMid(query, 1); | 		plainQuery = base::StringViewMid(query, 1); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	bool resetScroll = (_type != type || _filter != plainQuery); | 	const auto resetScroll = (_type != type || _filter != plainQuery); | ||||||
| 	if (resetScroll) { | 	if (resetScroll) { | ||||||
| 		_type = type; | 		_type = type; | ||||||
| 		_filter = TextUtilities::RemoveAccents(plainQuery.toString()); | 		_filter = TextUtilities::RemoveAccents(plainQuery.toString()); | ||||||
|  | @ -342,10 +342,11 @@ void FieldAutocomplete::showFiltered( | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void FieldAutocomplete::showStickers(EmojiPtr emoji) { | void FieldAutocomplete::showStickers(EmojiPtr emoji) { | ||||||
| 	bool resetScroll = (_emoji != emoji); | 	const auto resetScroll = (_emoji != emoji); | ||||||
| 	_emoji = emoji; | 	if (resetScroll || emoji) { | ||||||
| 	_type = Type::Stickers; | 		_emoji = emoji; | ||||||
| 	if (!emoji) { | 		_type = Type::Stickers; | ||||||
|  | 	} else if (!emoji) { | ||||||
| 		rowsUpdated( | 		rowsUpdated( | ||||||
| 			base::take(_mrows), | 			base::take(_mrows), | ||||||
| 			base::take(_hrows), | 			base::take(_hrows), | ||||||
|  |  | ||||||
|  | @ -26,6 +26,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| #include "history/view/media/history_view_media.h" | #include "history/view/media/history_view_media.h" | ||||||
| #include "history/view/media/history_view_web_page.h" | #include "history/view/media/history_view_web_page.h" | ||||||
| #include "history/view/reactions/history_view_reactions_list.h" | #include "history/view/reactions/history_view_reactions_list.h" | ||||||
|  | #include "info/info_memento.h" | ||||||
|  | #include "info/profile/info_profile_widget.h" | ||||||
| #include "ui/widgets/popup_menu.h" | #include "ui/widgets/popup_menu.h" | ||||||
| #include "ui/widgets/menu/menu_action.h" | #include "ui/widgets/menu/menu_action.h" | ||||||
| #include "ui/widgets/menu/menu_common.h" | #include "ui/widgets/menu/menu_common.h" | ||||||
|  | @ -1089,6 +1091,28 @@ void EditTagBox( | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ShowWhoReadInfo( | ||||||
|  | 		not_null<Window::SessionController*> controller, | ||||||
|  | 		FullMsgId itemId, | ||||||
|  | 		Ui::WhoReadParticipant who) { | ||||||
|  | 	const auto peer = controller->session().data().peer(itemId.peer); | ||||||
|  | 	const auto participant = peer->owner().peer(PeerId(who.id)); | ||||||
|  | 	const auto migrated = participant->migrateFrom(); | ||||||
|  | 	const auto origin = who.dateReacted | ||||||
|  | 		? Info::Profile::Origin{ | ||||||
|  | 			Info::Profile::GroupReactionOrigin{ peer, itemId.msg }, | ||||||
|  | 		} | ||||||
|  | 		: Info::Profile::Origin(); | ||||||
|  | 	auto memento = std::make_shared<Info::Memento>( | ||||||
|  | 		std::vector<std::shared_ptr<Info::ContentMemento>>{ | ||||||
|  | 		std::make_shared<Info::Profile::Memento>( | ||||||
|  | 			participant, | ||||||
|  | 			migrated ? migrated->id : PeerId(), | ||||||
|  | 			origin), | ||||||
|  | 	}); | ||||||
|  | 	controller->showSection(std::move(memento)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
| 
 | 
 | ||||||
| ContextMenuRequest::ContextMenuRequest( | ContextMenuRequest::ContextMenuRequest( | ||||||
|  | @ -1392,11 +1416,12 @@ void AddWhoReactedAction( | ||||||
| 		}); | 		}); | ||||||
| 		controller->show(std::move(box)); | 		controller->show(std::move(box)); | ||||||
| 	}; | 	}; | ||||||
| 	const auto participantChosen = [=](uint64 id) { | 	const auto itemId = item->fullId(); | ||||||
|  | 	const auto participantChosen = [=](Ui::WhoReadParticipant who) { | ||||||
| 		if (const auto strong = weak.data()) { | 		if (const auto strong = weak.data()) { | ||||||
| 			strong->hideMenu(); | 			strong->hideMenu(); | ||||||
| 		} | 		} | ||||||
| 		controller->showPeerInfo(PeerId(id)); | 		ShowWhoReadInfo(controller, itemId, who); | ||||||
| 	}; | 	}; | ||||||
| 	const auto showAllChosen = [=, itemId = item->fullId()]{ | 	const auto showAllChosen = [=, itemId = item->fullId()]{ | ||||||
| 		// Pressing on an item that has a submenu doesn't hide it :(
 | 		// Pressing on an item that has a submenu doesn't hide it :(
 | ||||||
|  | @ -1508,8 +1533,9 @@ void ShowWhoReactedMenu( | ||||||
| 	struct State { | 	struct State { | ||||||
| 		int addedToBottom = 0; | 		int addedToBottom = 0; | ||||||
| 	}; | 	}; | ||||||
| 	const auto participantChosen = [=](uint64 id) { | 	const auto itemId = item->fullId(); | ||||||
| 		controller->showPeerInfo(PeerId(id)); | 	const auto participantChosen = [=](Ui::WhoReadParticipant who) { | ||||||
|  | 		ShowWhoReadInfo(controller, itemId, who); | ||||||
| 	}; | 	}; | ||||||
| 	const auto showAllChosen = [=, itemId = item->fullId()]{ | 	const auto showAllChosen = [=, itemId = item->fullId()]{ | ||||||
| 		if (const auto item = controller->session().data().message(itemId)) { | 		if (const auto item = controller->session().data().message(itemId)) { | ||||||
|  |  | ||||||
|  | @ -478,6 +478,10 @@ infoMainButton: SettingsButton(infoProfileButton) { | ||||||
| 	textFgOver: lightButtonFgOver; | 	textFgOver: lightButtonFgOver; | ||||||
| 	style: semiboldTextStyle; | 	style: semiboldTextStyle; | ||||||
| } | } | ||||||
|  | infoMainButtonAttention: SettingsButton(infoMainButton) { | ||||||
|  | 	textFg: attentionButtonFg; | ||||||
|  | 	textFgOver: attentionButtonFgOver; | ||||||
|  | } | ||||||
| infoSharedMediaButton: infoProfileButton; | infoSharedMediaButton: infoProfileButton; | ||||||
| infoSharedMediaBottomSkip: 12px; | infoSharedMediaBottomSkip: 12px; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| */ | */ | ||||||
| #include "info/profile/info_profile_actions.h" | #include "info/profile/info_profile_actions.h" | ||||||
| 
 | 
 | ||||||
|  | #include "api/api_chat_participants.h" | ||||||
| #include "base/options.h" | #include "base/options.h" | ||||||
| #include "data/data_peer_values.h" | #include "data/data_peer_values.h" | ||||||
| #include "data/data_session.h" | #include "data/data_session.h" | ||||||
|  | @ -14,12 +15,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| #include "data/data_forum_topic.h" | #include "data/data_forum_topic.h" | ||||||
| #include "data/data_channel.h" | #include "data/data_channel.h" | ||||||
| #include "data/data_changes.h" | #include "data/data_changes.h" | ||||||
|  | #include "data/data_chat.h" | ||||||
| #include "data/data_user.h" | #include "data/data_user.h" | ||||||
| #include "data/notify/data_notify_settings.h" | #include "data/notify/data_notify_settings.h" | ||||||
| #include "ui/vertical_list.h" | #include "ui/vertical_list.h" | ||||||
| #include "ui/wrap/vertical_layout.h" | #include "ui/wrap/vertical_layout.h" | ||||||
| #include "ui/wrap/padding_wrap.h" | #include "ui/wrap/padding_wrap.h" | ||||||
| #include "ui/wrap/slide_wrap.h" | #include "ui/wrap/slide_wrap.h" | ||||||
|  | #include "ui/widgets/checkbox.h" | ||||||
| #include "ui/widgets/shadow.h" | #include "ui/widgets/shadow.h" | ||||||
| #include "ui/widgets/labels.h" | #include "ui/widgets/labels.h" | ||||||
| #include "ui/widgets/buttons.h" | #include "ui/widgets/buttons.h" | ||||||
|  | @ -44,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| #include "info/profile/info_profile_phone_menu.h" | #include "info/profile/info_profile_phone_menu.h" | ||||||
| #include "info/profile/info_profile_values.h" | #include "info/profile/info_profile_values.h" | ||||||
| #include "info/profile/info_profile_text.h" | #include "info/profile/info_profile_text.h" | ||||||
|  | #include "info/profile/info_profile_widget.h" | ||||||
| #include "support/support_helper.h" | #include "support/support_helper.h" | ||||||
| #include "window/window_session_controller.h" | #include "window/window_session_controller.h" | ||||||
| #include "window/window_controller.h" // Window::Controller::show.
 | #include "window/window_controller.h" // Window::Controller::show.
 | ||||||
|  | @ -56,6 +60,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| #include "apiwrap.h" | #include "apiwrap.h" | ||||||
| #include "api/api_blocked_peers.h" | #include "api/api_blocked_peers.h" | ||||||
| #include "styles/style_info.h" | #include "styles/style_info.h" | ||||||
|  | #include "styles/style_layers.h" | ||||||
| #include "styles/style_boxes.h" | #include "styles/style_boxes.h" | ||||||
| #include "styles/style_menu_icons.h" | #include "styles/style_menu_icons.h" | ||||||
| 
 | 
 | ||||||
|  | @ -190,14 +195,15 @@ template <typename Text, typename ToggleOn, typename Callback> | ||||||
| 		Text &&text, | 		Text &&text, | ||||||
| 		ToggleOn &&toggleOn, | 		ToggleOn &&toggleOn, | ||||||
| 		Callback &&callback, | 		Callback &&callback, | ||||||
| 		Ui::MultiSlideTracker &tracker) { | 		Ui::MultiSlideTracker &tracker, | ||||||
|  | 		const style::SettingsButton &st = st::infoMainButton) { | ||||||
| 	tracker.track(AddActionButton( | 	tracker.track(AddActionButton( | ||||||
| 		parent, | 		parent, | ||||||
| 		std::move(text) | Ui::Text::ToUpper(), | 		std::move(text) | Ui::Text::ToUpper(), | ||||||
| 		std::move(toggleOn), | 		std::move(toggleOn), | ||||||
| 		std::move(callback), | 		std::move(callback), | ||||||
| 		nullptr, | 		nullptr, | ||||||
| 		st::infoMainButton)); | 		st)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class DetailsFiller { | class DetailsFiller { | ||||||
|  | @ -205,7 +211,8 @@ public: | ||||||
| 	DetailsFiller( | 	DetailsFiller( | ||||||
| 		not_null<Controller*> controller, | 		not_null<Controller*> controller, | ||||||
| 		not_null<Ui::RpWidget*> parent, | 		not_null<Ui::RpWidget*> parent, | ||||||
| 		not_null<PeerData*> peer); | 		not_null<PeerData*> peer, | ||||||
|  | 		Origin origin); | ||||||
| 	DetailsFiller( | 	DetailsFiller( | ||||||
| 		not_null<Controller*> controller, | 		not_null<Controller*> controller, | ||||||
| 		not_null<Ui::RpWidget*> parent, | 		not_null<Ui::RpWidget*> parent, | ||||||
|  | @ -223,6 +230,12 @@ private: | ||||||
| 	Ui::MultiSlideTracker fillChannelButtons( | 	Ui::MultiSlideTracker fillChannelButtons( | ||||||
| 		not_null<ChannelData*> channel); | 		not_null<ChannelData*> channel); | ||||||
| 
 | 
 | ||||||
|  | 	void addReportReaction(Ui::MultiSlideTracker &tracker); | ||||||
|  | 	void addReportReaction( | ||||||
|  | 		GroupReactionOrigin data, | ||||||
|  | 		bool ban, | ||||||
|  | 		Ui::MultiSlideTracker &tracker); | ||||||
|  | 
 | ||||||
| 	template < | 	template < | ||||||
| 		typename Widget, | 		typename Widget, | ||||||
| 		typename = std::enable_if_t< | 		typename = std::enable_if_t< | ||||||
|  | @ -239,6 +252,7 @@ private: | ||||||
| 	not_null<Ui::RpWidget*> _parent; | 	not_null<Ui::RpWidget*> _parent; | ||||||
| 	not_null<PeerData*> _peer; | 	not_null<PeerData*> _peer; | ||||||
| 	Data::ForumTopic *_topic = nullptr; | 	Data::ForumTopic *_topic = nullptr; | ||||||
|  | 	Origin _origin; | ||||||
| 	object_ptr<Ui::VerticalLayout> _wrap; | 	object_ptr<Ui::VerticalLayout> _wrap; | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
|  | @ -272,13 +286,65 @@ private: | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | void ReportReactionBox( | ||||||
|  | 		not_null<Ui::GenericBox*> box, | ||||||
|  | 		not_null<Window::SessionController*> controller, | ||||||
|  | 		not_null<PeerData*> participant, | ||||||
|  | 		GroupReactionOrigin data, | ||||||
|  | 		bool ban, | ||||||
|  | 		Fn<void()> sent) { | ||||||
|  | 	box->setTitle(tr::lng_report_reaction_title()); | ||||||
|  | 	box->addRow(object_ptr<Ui::FlatLabel>( | ||||||
|  | 		box, | ||||||
|  | 		tr::lng_report_reaction_about(), | ||||||
|  | 		st::boxLabel)); | ||||||
|  | 	const auto check = ban | ||||||
|  | 		? box->addRow( | ||||||
|  | 			object_ptr<Ui::Checkbox>( | ||||||
|  | 				box, | ||||||
|  | 				tr::lng_report_and_ban_button(tr::now), | ||||||
|  | 				true), | ||||||
|  | 			st::boxRowPadding + QMargins{ 0, st::boxLittleSkip, 0, 0 }) | ||||||
|  | 		: nullptr; | ||||||
|  | 	box->addButton(tr::lng_report_button(), [=] { | ||||||
|  | 		const auto chat = data.group->asChat(); | ||||||
|  | 		const auto channel = data.group->asMegagroup(); | ||||||
|  | 		if (check && check->checked()) { | ||||||
|  | 			if (chat) { | ||||||
|  | 				chat->session().api().chatParticipants().kick( | ||||||
|  | 					chat, | ||||||
|  | 					participant); | ||||||
|  | 			} else if (channel) { | ||||||
|  | 				channel->session().api().chatParticipants().kick( | ||||||
|  | 					channel, | ||||||
|  | 					participant, | ||||||
|  | 					ChatRestrictionsInfo()); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		data.group->session().api().request(MTPmessages_ReportReaction( | ||||||
|  | 			data.group->input, | ||||||
|  | 			MTP_int(data.messageId.bare), | ||||||
|  | 			participant->input | ||||||
|  | 		)).done(crl::guard(controller, [=] { | ||||||
|  | 			controller->showToast(tr::lng_report_thanks(tr::now)); | ||||||
|  | 		})).send(); | ||||||
|  | 		sent(); | ||||||
|  | 		box->closeBox(); | ||||||
|  | 	}, st::attentionBoxButton); | ||||||
|  | 	box->addButton(tr::lng_cancel(), [=] { | ||||||
|  | 		box->closeBox(); | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| DetailsFiller::DetailsFiller( | DetailsFiller::DetailsFiller( | ||||||
| 	not_null<Controller*> controller, | 	not_null<Controller*> controller, | ||||||
| 	not_null<Ui::RpWidget*> parent, | 	not_null<Ui::RpWidget*> parent, | ||||||
| 	not_null<PeerData*> peer) | 	not_null<PeerData*> peer, | ||||||
|  | 	Origin origin) | ||||||
| : _controller(controller) | : _controller(controller) | ||||||
| , _parent(parent) | , _parent(parent) | ||||||
| , _peer(peer) | , _peer(peer) | ||||||
|  | , _origin(origin) | ||||||
| , _wrap(_parent) { | , _wrap(_parent) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -653,6 +719,58 @@ void DetailsFiller::setupMainButtons() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void DetailsFiller::addReportReaction(Ui::MultiSlideTracker &tracker) { | ||||||
|  | 	v::match(_origin.data, [&](GroupReactionOrigin data) { | ||||||
|  | 		const auto user = _peer->asUser(); | ||||||
|  | 		if (_peer->isSelf()) { | ||||||
|  | 			return; | ||||||
|  | 		} else if (const auto chat = data.group->asChat()) { | ||||||
|  | 			const auto ban = chat->canBanMembers() | ||||||
|  | 				&& (!user || !chat->admins.contains(_peer)) | ||||||
|  | 				&& (!user || chat->creator != user->id); | ||||||
|  | 			addReportReaction(data, ban, tracker); | ||||||
|  | 		} else if (const auto channel = data.group->asMegagroup()) { | ||||||
|  | 			const auto ban = channel->canBanMembers() | ||||||
|  | 				&& (!user || !channel->mgInfo->admins.contains(user->id)) | ||||||
|  | 				&& (!user || channel->mgInfo->creator != user); | ||||||
|  | 			addReportReaction(data, ban, tracker); | ||||||
|  | 		} | ||||||
|  | 	}, [](const auto &) {}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DetailsFiller::addReportReaction( | ||||||
|  | 		GroupReactionOrigin data, | ||||||
|  | 		bool ban, | ||||||
|  | 		Ui::MultiSlideTracker &tracker) { | ||||||
|  | 	const auto peer = _peer; | ||||||
|  | 	if (!peer) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	const auto controller = _controller->parentController(); | ||||||
|  | 	const auto forceHidden = std::make_shared<rpl::variable<bool>>(false); | ||||||
|  | 	const auto user = peer->asUser(); | ||||||
|  | 	auto shown = user | ||||||
|  | 		? rpl::combine( | ||||||
|  | 			Info::Profile::IsContactValue(user), | ||||||
|  | 			forceHidden->value(), | ||||||
|  | 			!rpl::mappers::_1 && !rpl::mappers::_2 | ||||||
|  | 		) | rpl::type_erased() | ||||||
|  | 		: (forceHidden->value() | rpl::map(!rpl::mappers::_1)); | ||||||
|  | 	const auto sent = [=] { | ||||||
|  | 		*forceHidden = true; | ||||||
|  | 	}; | ||||||
|  | 	AddMainButton( | ||||||
|  | 		_wrap, | ||||||
|  | 		(ban | ||||||
|  | 			? tr::lng_report_and_ban() | ||||||
|  | 			: tr::lng_report_reaction()), | ||||||
|  | 		std::move(shown), | ||||||
|  | 		[=] { controller->show( | ||||||
|  | 			Box(ReportReactionBox, controller, peer, data, ban, sent)); }, | ||||||
|  | 		tracker, | ||||||
|  | 		st::infoMainButtonAttention); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Ui::MultiSlideTracker DetailsFiller::fillTopicButtons() { | Ui::MultiSlideTracker DetailsFiller::fillTopicButtons() { | ||||||
| 	using namespace rpl::mappers; | 	using namespace rpl::mappers; | ||||||
| 
 | 
 | ||||||
|  | @ -719,6 +837,9 @@ Ui::MultiSlideTracker DetailsFiller::fillUserButtons( | ||||||
| 	} else { | 	} else { | ||||||
| 		addSendMessageButton(); | 		addSendMessageButton(); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	addReportReaction(tracker); | ||||||
|  | 
 | ||||||
| 	return tracker; | 	return tracker; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1055,8 +1176,9 @@ const char kOptionShowPeerIdBelowAbout[] = "show-peer-id-below-about"; | ||||||
| object_ptr<Ui::RpWidget> SetupDetails( | object_ptr<Ui::RpWidget> SetupDetails( | ||||||
| 		not_null<Controller*> controller, | 		not_null<Controller*> controller, | ||||||
| 		not_null<Ui::RpWidget*> parent, | 		not_null<Ui::RpWidget*> parent, | ||||||
| 		not_null<PeerData*> peer) { | 		not_null<PeerData*> peer, | ||||||
| 	DetailsFiller filler(controller, parent, peer); | 		Origin origin) { | ||||||
|  | 	DetailsFiller filler(controller, parent, peer, origin); | ||||||
| 	return filler.fill(); | 	return filler.fill(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,10 +25,13 @@ namespace Info::Profile { | ||||||
| 
 | 
 | ||||||
| extern const char kOptionShowPeerIdBelowAbout[]; | extern const char kOptionShowPeerIdBelowAbout[]; | ||||||
| 
 | 
 | ||||||
|  | struct Origin; | ||||||
|  | 
 | ||||||
| object_ptr<Ui::RpWidget> SetupDetails( | object_ptr<Ui::RpWidget> SetupDetails( | ||||||
| 	not_null<Controller*> controller, | 	not_null<Controller*> controller, | ||||||
| 	not_null<Ui::RpWidget*> parent, | 	not_null<Ui::RpWidget*> parent, | ||||||
| 	not_null<PeerData*> peer); | 	not_null<PeerData*> peer, | ||||||
|  | 	Origin origin); | ||||||
| 
 | 
 | ||||||
| object_ptr<Ui::RpWidget> SetupDetails( | object_ptr<Ui::RpWidget> SetupDetails( | ||||||
| 	not_null<Controller*> controller, | 	not_null<Controller*> controller, | ||||||
|  |  | ||||||
|  | @ -49,13 +49,14 @@ namespace Profile { | ||||||
| 
 | 
 | ||||||
| InnerWidget::InnerWidget( | InnerWidget::InnerWidget( | ||||||
| 	QWidget *parent, | 	QWidget *parent, | ||||||
| 	not_null<Controller*> controller) | 	not_null<Controller*> controller, | ||||||
|  | 	Origin origin) | ||||||
| : RpWidget(parent) | : RpWidget(parent) | ||||||
| , _controller(controller) | , _controller(controller) | ||||||
| , _peer(_controller->key().peer()) | , _peer(_controller->key().peer()) | ||||||
| , _migrated(_controller->migrated()) | , _migrated(_controller->migrated()) | ||||||
| , _topic(_controller->key().topic()) | , _topic(_controller->key().topic()) | ||||||
| , _content(setupContent(this)) { | , _content(setupContent(this, origin)) { | ||||||
| 	_content->heightValue( | 	_content->heightValue( | ||||||
| 	) | rpl::start_with_next([this](int height) { | 	) | rpl::start_with_next([this](int height) { | ||||||
| 		if (!_inResize) { | 		if (!_inResize) { | ||||||
|  | @ -66,7 +67,8 @@ InnerWidget::InnerWidget( | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| object_ptr<Ui::RpWidget> InnerWidget::setupContent( | object_ptr<Ui::RpWidget> InnerWidget::setupContent( | ||||||
| 		not_null<RpWidget*> parent) { | 		not_null<RpWidget*> parent, | ||||||
|  | 		Origin origin) { | ||||||
| 	auto result = object_ptr<Ui::VerticalLayout>(parent); | 	auto result = object_ptr<Ui::VerticalLayout>(parent); | ||||||
| 	if (const auto user = _peer->asUser()) { | 	if (const auto user = _peer->asUser()) { | ||||||
| 		user->session().changes().peerFlagsValue( | 		user->session().changes().peerFlagsValue( | ||||||
|  | @ -104,7 +106,7 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent( | ||||||
| 		} | 		} | ||||||
| 		result->add(SetupDetails(_controller, parent, _topic)); | 		result->add(SetupDetails(_controller, parent, _topic)); | ||||||
| 	} else { | 	} else { | ||||||
| 		result->add(SetupDetails(_controller, parent, _peer)); | 		result->add(SetupDetails(_controller, parent, _peer, origin)); | ||||||
| 	} | 	} | ||||||
| 	result->add(setupSharedMedia(result.data())); | 	result->add(setupSharedMedia(result.data())); | ||||||
| 	if (_topic) { | 	if (_topic) { | ||||||
|  |  | ||||||
|  | @ -37,12 +37,14 @@ namespace Profile { | ||||||
| class Memento; | class Memento; | ||||||
| class Members; | class Members; | ||||||
| class Cover; | class Cover; | ||||||
|  | struct Origin; | ||||||
| 
 | 
 | ||||||
| class InnerWidget final : public Ui::RpWidget { | class InnerWidget final : public Ui::RpWidget { | ||||||
| public: | public: | ||||||
| 	InnerWidget( | 	InnerWidget( | ||||||
| 		QWidget *parent, | 		QWidget *parent, | ||||||
| 		not_null<Controller*> controller); | 		not_null<Controller*> controller, | ||||||
|  | 		Origin origin); | ||||||
| 
 | 
 | ||||||
| 	void saveState(not_null<Memento*> memento); | 	void saveState(not_null<Memento*> memento); | ||||||
| 	void restoreState(not_null<Memento*> memento); | 	void restoreState(not_null<Memento*> memento); | ||||||
|  | @ -57,7 +59,9 @@ protected: | ||||||
| 		int visibleBottom) override; | 		int visibleBottom) override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	object_ptr<RpWidget> setupContent(not_null<RpWidget*> parent); | 	object_ptr<RpWidget> setupContent( | ||||||
|  | 		not_null<RpWidget*> parent, | ||||||
|  | 		Origin origin); | ||||||
| 	object_ptr<RpWidget> setupSharedMedia(not_null<RpWidget*> parent); | 	object_ptr<RpWidget> setupSharedMedia(not_null<RpWidget*> parent); | ||||||
| 	void setupMembers(not_null<Ui::VerticalLayout*> container); | 	void setupMembers(not_null<Ui::VerticalLayout*> container); | ||||||
| 
 | 
 | ||||||
|  | @ -71,6 +75,8 @@ private: | ||||||
| 	PeerData * const _migrated = nullptr; | 	PeerData * const _migrated = nullptr; | ||||||
| 	Data::ForumTopic * const _topic = nullptr; | 	Data::ForumTopic * const _topic = nullptr; | ||||||
| 
 | 
 | ||||||
|  | 	PeerData *_reactionGroup = nullptr; | ||||||
|  | 
 | ||||||
| 	std::shared_ptr<Data::PhotoMedia> _nonPersonalView; | 	std::shared_ptr<Data::PhotoMedia> _nonPersonalView; | ||||||
| 
 | 
 | ||||||
| 	Members *_members = nullptr; | 	Members *_members = nullptr; | ||||||
|  |  | ||||||
|  | @ -25,18 +25,24 @@ Memento::Memento(not_null<Controller*> controller) | ||||||
| : Memento( | : Memento( | ||||||
| 	controller->peer(), | 	controller->peer(), | ||||||
| 	controller->topic(), | 	controller->topic(), | ||||||
| 	controller->migratedPeerId()) { | 	controller->migratedPeerId(), | ||||||
|  | 	{ v::null }) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Memento::Memento(not_null<PeerData*> peer, PeerId migratedPeerId) | Memento::Memento( | ||||||
| : Memento(peer, nullptr, migratedPeerId) { | 	not_null<PeerData*> peer, | ||||||
|  | 	PeerId migratedPeerId, | ||||||
|  | 	Origin origin) | ||||||
|  | : Memento(peer, nullptr, migratedPeerId, origin) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Memento::Memento( | Memento::Memento( | ||||||
| 	not_null<PeerData*> peer, | 	not_null<PeerData*> peer, | ||||||
| 	Data::ForumTopic *topic, | 	Data::ForumTopic *topic, | ||||||
| 	PeerId migratedPeerId) | 	PeerId migratedPeerId, | ||||||
| : ContentMemento(peer, topic, migratedPeerId) { | 	Origin origin) | ||||||
|  | : ContentMemento(peer, topic, migratedPeerId) | ||||||
|  | , _origin(origin) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Memento::Memento(not_null<Data::ForumTopic*> topic) | Memento::Memento(not_null<Data::ForumTopic*> topic) | ||||||
|  | @ -51,7 +57,7 @@ object_ptr<ContentWidget> Memento::createWidget( | ||||||
| 		QWidget *parent, | 		QWidget *parent, | ||||||
| 		not_null<Controller*> controller, | 		not_null<Controller*> controller, | ||||||
| 		const QRect &geometry) { | 		const QRect &geometry) { | ||||||
| 	auto result = object_ptr<Widget>(parent, controller); | 	auto result = object_ptr<Widget>(parent, controller, _origin); | ||||||
| 	result->setInternalState(geometry, this); | 	result->setInternalState(geometry, this); | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  | @ -66,13 +72,17 @@ std::unique_ptr<MembersState> Memento::membersState() { | ||||||
| 
 | 
 | ||||||
| Memento::~Memento() = default; | Memento::~Memento() = default; | ||||||
| 
 | 
 | ||||||
| Widget::Widget(QWidget *parent, not_null<Controller*> controller) | Widget::Widget( | ||||||
|  | 	QWidget *parent, | ||||||
|  | 	not_null<Controller*> controller, | ||||||
|  | 	Origin origin) | ||||||
| : ContentWidget(parent, controller) { | : ContentWidget(parent, controller) { | ||||||
| 	controller->setSearchEnabledByContent(false); | 	controller->setSearchEnabledByContent(false); | ||||||
| 
 | 
 | ||||||
| 	_inner = setInnerWidget(object_ptr<InnerWidget>( | 	_inner = setInnerWidget(object_ptr<InnerWidget>( | ||||||
| 		this, | 		this, | ||||||
| 		controller)); | 		controller, | ||||||
|  | 		origin)); | ||||||
| 	_inner->move(0, 0); | 	_inner->move(0, 0); | ||||||
| 	_inner->scrollToRequests( | 	_inner->scrollToRequests( | ||||||
| 	) | rpl::start_with_next([this](Ui::ScrollToRequest request) { | 	) | rpl::start_with_next([this](Ui::ScrollToRequest request) { | ||||||
|  |  | ||||||
|  | @ -18,10 +18,22 @@ namespace Info::Profile { | ||||||
| class InnerWidget; | class InnerWidget; | ||||||
| struct MembersState; | struct MembersState; | ||||||
| 
 | 
 | ||||||
|  | struct GroupReactionOrigin { | ||||||
|  | 	not_null<PeerData*> group; | ||||||
|  | 	MsgId messageId = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct Origin { | ||||||
|  | 	std::variant<v::null_t, GroupReactionOrigin> data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| class Memento final : public ContentMemento { | class Memento final : public ContentMemento { | ||||||
| public: | public: | ||||||
| 	explicit Memento(not_null<Controller*> controller); | 	explicit Memento(not_null<Controller*> controller); | ||||||
| 	Memento(not_null<PeerData*> peer, PeerId migratedPeerId); | 	Memento( | ||||||
|  | 		not_null<PeerData*> peer, | ||||||
|  | 		PeerId migratedPeerId, | ||||||
|  | 		Origin origin = { v::null }); | ||||||
| 	explicit Memento(not_null<Data::ForumTopic*> topic); | 	explicit Memento(not_null<Data::ForumTopic*> topic); | ||||||
| 
 | 
 | ||||||
| 	object_ptr<ContentWidget> createWidget( | 	object_ptr<ContentWidget> createWidget( | ||||||
|  | @ -31,6 +43,10 @@ public: | ||||||
| 
 | 
 | ||||||
| 	Section section() const override; | 	Section section() const override; | ||||||
| 
 | 
 | ||||||
|  | 	[[nodiscard]] Origin origin() const { | ||||||
|  | 		return _origin; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	void setMembersState(std::unique_ptr<MembersState> state); | 	void setMembersState(std::unique_ptr<MembersState> state); | ||||||
| 	std::unique_ptr<MembersState> membersState(); | 	std::unique_ptr<MembersState> membersState(); | ||||||
| 
 | 
 | ||||||
|  | @ -40,15 +56,17 @@ private: | ||||||
| 	Memento( | 	Memento( | ||||||
| 		not_null<PeerData*> peer, | 		not_null<PeerData*> peer, | ||||||
| 		Data::ForumTopic *topic, | 		Data::ForumTopic *topic, | ||||||
| 		PeerId migratedPeerId); | 		PeerId migratedPeerId, | ||||||
|  | 		Origin origin); | ||||||
| 
 | 
 | ||||||
| 	std::unique_ptr<MembersState> _membersState; | 	std::unique_ptr<MembersState> _membersState; | ||||||
|  | 	Origin _origin; | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class Widget final : public ContentWidget { | class Widget final : public ContentWidget { | ||||||
| public: | public: | ||||||
| 	Widget(QWidget *parent, not_null<Controller*> controller); | 	Widget(QWidget *parent, not_null<Controller*> controller, Origin origin); | ||||||
| 
 | 
 | ||||||
| 	bool showInternal( | 	bool showInternal( | ||||||
| 		not_null<ContentMemento*> memento) override; | 		not_null<ContentMemento*> memento) override; | ||||||
|  |  | ||||||
|  | @ -80,7 +80,7 @@ public: | ||||||
| 		not_null<PopupMenu*> parentMenu, | 		not_null<PopupMenu*> parentMenu, | ||||||
| 		rpl::producer<WhoReadContent> content, | 		rpl::producer<WhoReadContent> content, | ||||||
| 		CustomEmojiFactory factory, | 		CustomEmojiFactory factory, | ||||||
| 		Fn<void(uint64)> participantChosen, | 		Fn<void(WhoReadParticipant)> participantChosen, | ||||||
| 		Fn<void()> showAllChosen); | 		Fn<void()> showAllChosen); | ||||||
| 
 | 
 | ||||||
| 	bool isEnabled() const override; | 	bool isEnabled() const override; | ||||||
|  | @ -105,7 +105,7 @@ private: | ||||||
| 
 | 
 | ||||||
| 	const not_null<PopupMenu*> _parentMenu; | 	const not_null<PopupMenu*> _parentMenu; | ||||||
| 	const not_null<QAction*> _dummyAction; | 	const not_null<QAction*> _dummyAction; | ||||||
| 	const Fn<void(uint64)> _participantChosen; | 	const Fn<void(WhoReadParticipant)> _participantChosen; | ||||||
| 	const Fn<void()> _showAllChosen; | 	const Fn<void()> _showAllChosen; | ||||||
| 	const std::unique_ptr<GroupCallUserpics> _userpics; | 	const std::unique_ptr<GroupCallUserpics> _userpics; | ||||||
| 	const style::Menu &_st; | 	const style::Menu &_st; | ||||||
|  | @ -186,7 +186,7 @@ Action::Action( | ||||||
| 	not_null<PopupMenu*> parentMenu, | 	not_null<PopupMenu*> parentMenu, | ||||||
| 	rpl::producer<WhoReadContent> content, | 	rpl::producer<WhoReadContent> content, | ||||||
| 	Text::CustomEmojiFactory factory, | 	Text::CustomEmojiFactory factory, | ||||||
| 	Fn<void(uint64)> participantChosen, | 	Fn<void(WhoReadParticipant)> participantChosen, | ||||||
| 	Fn<void()> showAllChosen) | 	Fn<void()> showAllChosen) | ||||||
| : ItemBase(parentMenu->menu(), parentMenu->menu()->st()) | : ItemBase(parentMenu->menu(), parentMenu->menu()->st()) | ||||||
| , _parentMenu(parentMenu) | , _parentMenu(parentMenu) | ||||||
|  | @ -252,7 +252,7 @@ Action::Action( | ||||||
| 	) | rpl::start_with_next([=] { | 	) | rpl::start_with_next([=] { | ||||||
| 		if (_content.participants.size() == 1) { | 		if (_content.participants.size() == 1) { | ||||||
| 			if (const auto onstack = _participantChosen) { | 			if (const auto onstack = _participantChosen) { | ||||||
| 				onstack(_content.participants.front().id); | 				onstack(_content.participants.front()); | ||||||
| 			} | 			} | ||||||
| 		} else if (_content.fullReactionsCount > 0) { | 		} else if (_content.fullReactionsCount > 0) { | ||||||
| 			if (const auto onstack = _showAllChosen) { | 			if (const auto onstack = _showAllChosen) { | ||||||
|  | @ -909,7 +909,7 @@ base::unique_qptr<Menu::ItemBase> WhoReactedContextAction( | ||||||
| 		not_null<PopupMenu*> menu, | 		not_null<PopupMenu*> menu, | ||||||
| 		rpl::producer<WhoReadContent> content, | 		rpl::producer<WhoReadContent> content, | ||||||
| 		CustomEmojiFactory factory, | 		CustomEmojiFactory factory, | ||||||
| 		Fn<void(uint64)> participantChosen, | 		Fn<void(WhoReadParticipant)> participantChosen, | ||||||
| 		Fn<void()> showAllChosen) { | 		Fn<void()> showAllChosen) { | ||||||
| 	return base::make_unique_q<Action>( | 	return base::make_unique_q<Action>( | ||||||
| 		menu, | 		menu, | ||||||
|  | @ -931,7 +931,7 @@ base::unique_qptr<Menu::ItemBase> WhenReadContextAction( | ||||||
| 
 | 
 | ||||||
| WhoReactedListMenu::WhoReactedListMenu( | WhoReactedListMenu::WhoReactedListMenu( | ||||||
| 	CustomEmojiFactory factory, | 	CustomEmojiFactory factory, | ||||||
| 	Fn<void(uint64)> participantChosen, | 	Fn<void(WhoReadParticipant)> participantChosen, | ||||||
| 	Fn<void()> showAllChosen) | 	Fn<void()> showAllChosen) | ||||||
| : _customEmojiFactory(std::move(factory)) | : _customEmojiFactory(std::move(factory)) | ||||||
| , _participantChosen(std::move(participantChosen)) | , _participantChosen(std::move(participantChosen)) | ||||||
|  | @ -983,8 +983,8 @@ void WhoReactedListMenu::populate( | ||||||
| 		++index; | 		++index; | ||||||
| 	}; | 	}; | ||||||
| 	for (const auto &participant : content.participants) { | 	for (const auto &participant : content.participants) { | ||||||
| 		const auto chosen = [call = _participantChosen, id = participant.id]{ | 		const auto chosen = [call = _participantChosen, participant] { | ||||||
| 			call(id); | 			call(participant); | ||||||
| 		}; | 		}; | ||||||
| 		append({ | 		append({ | ||||||
| 			.text = participant.name, | 			.text = participant.name, | ||||||
|  |  | ||||||
|  | @ -59,7 +59,7 @@ struct WhoReadContent { | ||||||
| 	not_null<PopupMenu*> menu, | 	not_null<PopupMenu*> menu, | ||||||
| 	rpl::producer<WhoReadContent> content, | 	rpl::producer<WhoReadContent> content, | ||||||
| 	Text::CustomEmojiFactory factory, | 	Text::CustomEmojiFactory factory, | ||||||
| 	Fn<void(uint64)> participantChosen, | 	Fn<void(WhoReadParticipant)> participantChosen, | ||||||
| 	Fn<void()> showAllChosen); | 	Fn<void()> showAllChosen); | ||||||
| 
 | 
 | ||||||
| [[nodiscard]] base::unique_qptr<Menu::ItemBase> WhenReadContextAction( | [[nodiscard]] base::unique_qptr<Menu::ItemBase> WhenReadContextAction( | ||||||
|  | @ -123,7 +123,7 @@ class WhoReactedListMenu final { | ||||||
| public: | public: | ||||||
| 	WhoReactedListMenu( | 	WhoReactedListMenu( | ||||||
| 		Text::CustomEmojiFactory factory, | 		Text::CustomEmojiFactory factory, | ||||||
| 		Fn<void(uint64)> participantChosen, | 		Fn<void(WhoReadParticipant)> participantChosen, | ||||||
| 		Fn<void()> showAllChosen); | 		Fn<void()> showAllChosen); | ||||||
| 
 | 
 | ||||||
| 	void clear(); | 	void clear(); | ||||||
|  | @ -136,7 +136,7 @@ public: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	const Text::CustomEmojiFactory _customEmojiFactory; | 	const Text::CustomEmojiFactory _customEmojiFactory; | ||||||
| 	const Fn<void(uint64)> _participantChosen; | 	const Fn<void(WhoReadParticipant)> _participantChosen; | ||||||
| 	const Fn<void()> _showAllChosen; | 	const Fn<void()> _showAllChosen; | ||||||
| 
 | 
 | ||||||
| 	std::vector<not_null<WhoReactedEntryAction*>> _actions; | 	std::vector<not_null<WhoReactedEntryAction*>> _actions; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 John Preston
						John Preston