mirror of https://github.com/procxx/kepka.git
				
				
				
			Added Invite Link button. Refactored code.
- Removed unused includes. - Improved design for boxes. - Draw buttons for EditPeerInfoBox in single place. - Simplified conditions for all buttons. - Made AddSkip more flexible.
This commit is contained in:
		
							parent
							
								
									8887272577
								
							
						
					
					
						commit
						3d7b8b3162
					
				|  | @ -866,6 +866,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | |||
| "lng_create_private_group_about" = "People can only join if they were invited or have an invite link"; | ||||
| "lng_create_group_skip" = "Skip"; | ||||
| 
 | ||||
| "lng_create_channel_link_about" = "You can use a-z, 0-9 and underscores.\nMinimum length is 5 characters."; | ||||
| 
 | ||||
| "lng_create_channel_link_invalid" = "This link is invalid"; | ||||
| "lng_create_channel_link_occupied" = "Sorry, this link is already occupied"; | ||||
| "lng_create_channel_link_too_short" = "Sorry, this link is too short"; | ||||
|  |  | |||
|  | @ -23,14 +23,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | |||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| std::shared_ptr<Ui::RadioenumGroup<HistoryVisibility>> historyVisibility; | ||||
| Ui::SlideWrap<Ui::RpWidget> *historyVisibilityWrap = nullptr; | ||||
| 
 | ||||
| void AddRoundButton( | ||||
| 	not_null<Ui::VerticalLayout*> container, | ||||
| 	HistoryVisibility value, | ||||
| 	LangKey groupTextKey, | ||||
| 	LangKey groupAboutKey) { | ||||
| 	LangKey groupAboutKey, | ||||
| 	std::shared_ptr<Ui::RadioenumGroup<HistoryVisibility>> historyVisibility) { | ||||
| 	container->add(object_ptr<Ui::FixedHeightWidget>( | ||||
| 		container, | ||||
| 		st::editPeerHistoryVisibilityTopSkip)); | ||||
|  | @ -46,12 +44,13 @@ void AddRoundButton( | |||
| 			container, | ||||
| 			Lang::Viewer(groupAboutKey), | ||||
| 			st::editPeerPrivacyLabel), | ||||
| 		st::editPeerPrivacyLabelMargins)); | ||||
| }; | ||||
| 		st::editPeerPreHistoryLabelMargins)); | ||||
| } | ||||
| 
 | ||||
| void FillContent( | ||||
| 	not_null<Ui::VerticalLayout*> parent, | ||||
| 	not_null<PeerData*> peer, | ||||
| 	std::shared_ptr<Ui::RadioenumGroup<HistoryVisibility>> historyVisibility, | ||||
| 	std::optional<HistoryVisibility> savedValue = std::nullopt) { | ||||
| 
 | ||||
| 	const auto canEdit = [&] { | ||||
|  | @ -67,13 +66,6 @@ void FillContent( | |||
| 	} | ||||
| 
 | ||||
| 	const auto channel = peer->asChannel(); | ||||
| 	 | ||||
| 	const auto result = parent->add(object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( | ||||
| 		parent, | ||||
| 		object_ptr<Ui::VerticalLayout>(parent), | ||||
| 		st::editPeerHistoryVisibilityMargins)); | ||||
| 	historyVisibilityWrap = result; | ||||
| 	const auto container = result->entity(); | ||||
| 
 | ||||
| 	const auto defaultValue = savedValue.value_or( | ||||
| 		(!channel || channel->hiddenPreHistory()) | ||||
|  | @ -81,20 +73,30 @@ void FillContent( | |||
| 			: HistoryVisibility::Visible | ||||
| 	); | ||||
| 
 | ||||
| 	historyVisibility = std::make_shared<Ui::RadioenumGroup<HistoryVisibility>>(defaultValue); | ||||
| 	historyVisibility->setValue(defaultValue); | ||||
| 	 | ||||
| 	const auto result = parent->add(object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( | ||||
| 		parent, | ||||
| 		object_ptr<Ui::VerticalLayout>(parent), | ||||
| 		st::editPeerHistoryVisibilityMargins)); | ||||
| 	const auto container = result->entity(); | ||||
| 
 | ||||
| 	Expects(historyVisibility != nullptr); | ||||
| 
 | ||||
| 	AddRoundButton( | ||||
| 		container, | ||||
| 		HistoryVisibility::Visible, | ||||
| 		lng_manage_history_visibility_shown, | ||||
| 		lng_manage_history_visibility_shown_about); | ||||
| 		lng_manage_history_visibility_shown_about, | ||||
| 		historyVisibility); | ||||
| 	AddRoundButton( | ||||
| 		container, | ||||
| 		HistoryVisibility::Hidden, | ||||
| 		lng_manage_history_visibility_hidden, | ||||
| 		(peer->isChat() | ||||
| 			? lng_manage_history_visibility_hidden_legacy | ||||
| 			: lng_manage_history_visibility_hidden_about)); | ||||
| 			: lng_manage_history_visibility_hidden_about), | ||||
| 		historyVisibility); | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
|  | @ -106,7 +108,10 @@ EditPeerHistoryVisibilityBox::EditPeerHistoryVisibilityBox( | |||
| 		std::optional<HistoryVisibility> historyVisibilitySavedValue) | ||||
| : _peer(peer) | ||||
| , _savedCallback(std::move(savedCallback)) | ||||
| , _historyVisibilitySavedValue(historyVisibilitySavedValue) { | ||||
| , _historyVisibilitySavedValue(historyVisibilitySavedValue) | ||||
| , _historyVisibility( | ||||
| 	std::make_shared<Ui::RadioenumGroup<HistoryVisibility>>( | ||||
| 		_historyVisibilitySavedValue.value_or(HistoryVisibility::Hidden))) { | ||||
| } | ||||
| 
 | ||||
| void EditPeerHistoryVisibilityBox::prepare() { | ||||
|  | @ -115,7 +120,7 @@ void EditPeerHistoryVisibilityBox::prepare() { | |||
| 	setTitle(langFactory(lng_manage_history_visibility_title)); | ||||
| 	addButton(langFactory(lng_settings_save), [=] { | ||||
| 		auto local = std::move(_savedCallback); | ||||
| 		local(historyVisibility->value()); | ||||
| 		local(_historyVisibility->value()); | ||||
| 		closeBox(); | ||||
| 	}); | ||||
| 	addButton(langFactory(lng_cancel), [=] { closeBox(); }); | ||||
|  | @ -125,6 +130,6 @@ void EditPeerHistoryVisibilityBox::prepare() { | |||
| 
 | ||||
| void EditPeerHistoryVisibilityBox::setupContent() { | ||||
| 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this); | ||||
| 	FillContent(content, _peer, _historyVisibilitySavedValue); | ||||
| 	FillContent(content, _peer, _historyVisibility, _historyVisibilitySavedValue); | ||||
| 	setDimensionsToContent(st::boxWidth, content); | ||||
| } | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | |||
| #pragma once | ||||
| 
 | ||||
| #include "boxes/abstract_box.h" | ||||
| #include "ui/widgets/checkbox.h" | ||||
| 
 | ||||
| namespace style { | ||||
| struct InfoProfileCountButton; | ||||
|  | @ -47,5 +48,6 @@ private: | |||
| 	FnMut<void(HistoryVisibility)> _savedCallback; | ||||
| 
 | ||||
| 	std::optional<HistoryVisibility> _historyVisibilitySavedValue; | ||||
| 	std::shared_ptr<Ui::RadioenumGroup<HistoryVisibility>> _historyVisibility = nullptr; | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -47,12 +47,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | |||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| Fn<QString()> ManagePeerTitle(not_null<PeerData*> peer) { | ||||
| 	return langFactory((peer->isChat() || peer->isMegagroup()) | ||||
| 		? lng_manage_group_title | ||||
| 		: lng_manage_channel_title); | ||||
| } | ||||
| 
 | ||||
| auto ToPositiveNumberString() { | ||||
| 	return rpl::map([](int count) { | ||||
| 		return count ? QString::number(count) : QString(); | ||||
|  | @ -67,11 +61,17 @@ auto ToPositiveNumberStringRestrictions() { | |||
| 	}); | ||||
| } | ||||
| 
 | ||||
| void AddSkip(not_null<Ui::VerticalLayout*> container) { | ||||
| void AddSkip( | ||||
| 		not_null<Ui::VerticalLayout*> container, | ||||
| 		int top = st::editPeerTopButtonsLayoutSkip, | ||||
| 		int bottom = st::editPeerTopButtonsLayoutSkipToBottom) { | ||||
| 	container->add(object_ptr<Ui::FixedHeightWidget>( | ||||
| 		container, | ||||
| 		st::editPeerSkip)); | ||||
| 		top)); | ||||
| 	container->add(object_ptr<BoxContentDivider>(container)); | ||||
| 	container->add(object_ptr<Ui::FixedHeightWidget>( | ||||
| 		container, | ||||
| 		bottom)); | ||||
| } | ||||
| 
 | ||||
| void AddButtonWithCount( | ||||
|  | @ -103,32 +103,6 @@ Info::Profile::Button *AddButtonWithText( | |||
| 		nullptr); | ||||
| } | ||||
| 
 | ||||
| bool HasRecentActions(not_null<ChannelData*> channel) { | ||||
| 	return channel->hasAdminRights() || channel->amCreator(); | ||||
| } | ||||
| 
 | ||||
| void ShowRecentActions( | ||||
| 		not_null<Window::Navigation*> navigation, | ||||
| 		not_null<ChannelData*> channel) { | ||||
| 	navigation->showSection(AdminLog::SectionMemento(channel)); | ||||
| } | ||||
| 
 | ||||
| bool HasEditInfoBox(not_null<PeerData*> peer) { | ||||
| 	if (const auto chat = peer->asChat()) { | ||||
| 		if (chat->canEditInformation()) { | ||||
| 			return true; | ||||
| 		} | ||||
| 	} else if (const auto channel = peer->asChannel()) { | ||||
| 		if (channel->canEditInformation()) { | ||||
| 			return true; | ||||
| 		} else if (!channel->isPublic() && channel->canAddMembers()) { | ||||
| 			// Edit invite link.
 | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| void ShowEditPermissions(not_null<PeerData*> peer) { | ||||
| 	const auto box = Ui::show( | ||||
| 		Box<EditPeerPermissionsBox>(peer), | ||||
|  | @ -147,108 +121,10 @@ void ShowEditPermissions(not_null<PeerData*> peer) { | |||
| 	}, box->lifetime()); | ||||
| } | ||||
| 
 | ||||
| void FillManageSection( | ||||
| 		not_null<Window::Navigation*> navigation, | ||||
| 		not_null<PeerData*> peer, | ||||
| 		not_null<Ui::VerticalLayout*> content) { | ||||
| 	const auto chat = peer->asChat(); | ||||
| 	const auto channel = peer->asChannel(); | ||||
| 	const auto isChannel = (!chat); | ||||
| 	if (!chat && !channel) return; | ||||
| 
 | ||||
| 	const auto canEditPermissions = [=] { | ||||
| 		return isChannel | ||||
| 			? channel->canEditPermissions() | ||||
| 			: chat->canEditPermissions(); | ||||
| 	}(); | ||||
| 	const auto canViewAdmins = [=] { | ||||
| 		return isChannel | ||||
| 			? channel->canViewAdmins() | ||||
| 			: chat->amIn(); | ||||
| 	}(); | ||||
| 	const auto canViewMembers = [=] { | ||||
| 		return isChannel | ||||
| 			? channel->canViewMembers() | ||||
| 			: chat->amIn(); | ||||
| 	}(); | ||||
| 	const auto canViewKicked = [=] { | ||||
| 		return isChannel | ||||
| 			? (!channel->isMegagroup()) | ||||
| 			: false; | ||||
| 	}(); | ||||
| 	const auto hasRecentActions = [=] { | ||||
| 		return isChannel | ||||
| 			? (channel->hasAdminRights() || channel->amCreator()) | ||||
| 			: false; | ||||
| 	}(); | ||||
| 
 | ||||
| 	if (canEditPermissions) { | ||||
| 		AddButtonWithCount( | ||||
| 			content, | ||||
| 			Lang::Viewer(lng_manage_peer_permissions), | ||||
| 			Info::Profile::RestrictionsCountValue(peer) | ||||
| 				| ToPositiveNumberStringRestrictions(), | ||||
| 			[=] { ShowEditPermissions(peer); }, | ||||
| 			st::infoIconPermissions); | ||||
| 	} | ||||
| 	if (canViewAdmins) { | ||||
| 		AddButtonWithCount( | ||||
| 			content, | ||||
| 			Lang::Viewer(lng_manage_peer_administrators), | ||||
| 			Info::Profile::AdminsCountValue(peer) | ||||
| 				| ToPositiveNumberString(), | ||||
| 			[=] { | ||||
| 				ParticipantsBoxController::Start( | ||||
| 					navigation, | ||||
| 					peer, | ||||
| 					ParticipantsBoxController::Role::Admins); | ||||
| 			}, | ||||
| 			st::infoIconAdministrators); | ||||
| 	} | ||||
| 	if (canViewMembers) { | ||||
| 		AddButtonWithCount( | ||||
| 			content, | ||||
| 			Lang::Viewer(lng_manage_peer_members), | ||||
| 			Info::Profile::MembersCountValue(peer) | ||||
| 				| ToPositiveNumberString(), | ||||
| 			[=] { | ||||
| 				ParticipantsBoxController::Start( | ||||
| 					navigation, | ||||
| 					peer, | ||||
| 					ParticipantsBoxController::Role::Members); | ||||
| 			}, | ||||
| 			st::infoIconMembers); | ||||
| 	} | ||||
| 	if (canViewKicked) { | ||||
| 		AddButtonWithCount( | ||||
| 			content, | ||||
| 			Lang::Viewer(lng_manage_peer_removed_users), | ||||
| 			Info::Profile::KickedCountValue(channel) | ||||
| 			| ToPositiveNumberString(), | ||||
| 			[=] { | ||||
| 				ParticipantsBoxController::Start( | ||||
| 					navigation, | ||||
| 					peer, | ||||
| 					ParticipantsBoxController::Role::Kicked); | ||||
| 			}, | ||||
| 			st::infoIconBlacklist); | ||||
| 	} | ||||
| 	if (hasRecentActions) { | ||||
| 		AddButtonWithCount( | ||||
| 			content, | ||||
| 			Lang::Viewer(lng_manage_peer_recent_actions), | ||||
| 			rpl::single(QString()), //Empty count.
 | ||||
| 			[=] { ShowRecentActions(navigation, channel); }, | ||||
| 			st::infoIconRecentActions); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| constexpr auto kUsernameCheckTimeout = crl::time(200); | ||||
| constexpr auto kMinUsernameLength = 5; | ||||
| constexpr auto kMaxGroupChannelTitle = 255; // See also add_contact_box.
 | ||||
| constexpr auto kMaxChannelDescription = 255; // See also add_contact_box.
 | ||||
| 
 | ||||
|  | @ -269,23 +145,8 @@ private: | |||
| 		Ui::InputField *description = nullptr; | ||||
| 		Ui::UserpicButton *photo = nullptr; | ||||
| 		rpl::lifetime initialPhotoImageWaiting; | ||||
| 
 | ||||
| 		std::shared_ptr<Ui::RadioenumGroup<Privacy>> privacy; | ||||
| 		Ui::SlideWrap<Ui::RpWidget> *usernameWrap = nullptr; | ||||
| 		Ui::UsernameInput *username = nullptr; | ||||
| 		base::unique_qptr<Ui::FlatLabel> usernameResult; | ||||
| 		const style::FlatLabel *usernameResultStyle = nullptr; | ||||
| 
 | ||||
| 		Ui::SlideWrap<Ui::RpWidget> *createInviteLinkWrap = nullptr; | ||||
| 		Ui::SlideWrap<Ui::RpWidget> *editInviteLinkWrap = nullptr; | ||||
| 		Ui::FlatLabel *inviteLink = nullptr; | ||||
| 
 | ||||
| 		Ui::SlideWrap<Ui::RpWidget> *historyVisibilityWrap = nullptr; | ||||
| 		std::optional<HistoryVisibility> historyVisibilitySavedValue = std::nullopt; | ||||
| 		std::optional<Privacy> privacySavedValue = std::nullopt; | ||||
| 		std::optional<QString> usernameSavedValue = std::nullopt; | ||||
| 
 | ||||
| 		std::optional<bool> signaturesSavedValue = std::nullopt; | ||||
| 		Ui::VerticalLayout *buttonsLayout = nullptr; | ||||
| 	}; | ||||
| 	struct Saving { | ||||
| 		std::optional<QString> username; | ||||
|  | @ -295,21 +156,24 @@ private: | |||
| 		std::optional<bool> signatures; | ||||
| 	}; | ||||
| 
 | ||||
| 	Fn<QString()> computeTitle() const; | ||||
| 	object_ptr<Ui::RpWidget> createPhotoAndTitleEdit(); | ||||
| 	object_ptr<Ui::RpWidget> createTitleEdit(); | ||||
| 	object_ptr<Ui::RpWidget> createPhotoEdit(); | ||||
| 	object_ptr<Ui::RpWidget> createDescriptionEdit(); | ||||
| 	void refreshHistoryVisibility(bool instant); | ||||
| 	void fillPrivacyTypeButton(); | ||||
| 	void fillInviteLinkButton(); | ||||
| 	void fillSignaturesButton(); | ||||
| 	void fillHistoryVisibilityButton(); | ||||
| 	void fillManageSection(not_null<Window::Navigation*> navigation, not_null<PeerData*> peer); | ||||
| 	object_ptr<Ui::RpWidget> createUsernameEdit(); | ||||
| 	object_ptr<Ui::RpWidget> createInviteLinkCreate(); | ||||
| 	object_ptr<Ui::RpWidget> createInviteLinkEdit(); | ||||
| 	object_ptr<Ui::RpWidget> createStickersEdit(); | ||||
| 	object_ptr<Ui::RpWidget> createDeleteButton(); | ||||
| 
 | ||||
| 	object_ptr<Ui::RpWidget> createPrivaciesButtons(); | ||||
| 	object_ptr<Ui::RpWidget> createManageGroupButtons(); | ||||
| 
 | ||||
| 	QString inviteLinkText() const; | ||||
| 	void observeInviteLink(); | ||||
| 
 | ||||
| 	void submitTitle(); | ||||
|  | @ -338,15 +202,17 @@ private: | |||
| 	void subscribeToMigration(); | ||||
| 	void migrate(not_null<ChannelData*> channel); | ||||
| 
 | ||||
| 	std::optional<Privacy> _privacySavedValue = std::nullopt; | ||||
| 	std::optional<HistoryVisibility> _historyVisibilitySavedValue = std::nullopt; | ||||
| 	std::optional<QString> _usernameSavedValue = std::nullopt; | ||||
| 	std::optional<bool> _signaturesSavedValue = std::nullopt; | ||||
| 
 | ||||
| 	not_null<BoxContent*> _box; | ||||
| 	not_null<PeerData*> _peer; | ||||
| 	bool _isGroup = false; | ||||
| 
 | ||||
| 	base::unique_qptr<Ui::VerticalLayout> _wrap; | ||||
| 	Controls _controls; | ||||
| 	mtpRequestId _checkUsernameRequestId = 0; | ||||
| 	UsernameState _usernameState = UsernameState::Normal; | ||||
| 	rpl::event_stream<rpl::producer<QString>> _usernameResultTexts; | ||||
| 
 | ||||
| 	std::deque<FnMut<void()>> _saveStagesQueue; | ||||
| 	Saving _savingData; | ||||
|  | @ -361,7 +227,9 @@ Controller::Controller( | |||
| : _box(box) | ||||
| , _peer(peer) | ||||
| , _isGroup(_peer->isChat() || _peer->isMegagroup()) { | ||||
| 	_box->setTitle(computeTitle()); | ||||
| 	_box->setTitle(langFactory(_isGroup | ||||
| 		? lng_edit_group | ||||
| 		: lng_edit_channel_title)); | ||||
| 	_box->addButton(langFactory(lng_settings_save), [this] { | ||||
| 		save(); | ||||
| 	}); | ||||
|  | @ -385,12 +253,6 @@ void Controller::migrate(not_null<ChannelData*> channel) { | |||
| 	_peer->updateFull(); | ||||
| } | ||||
| 
 | ||||
| Fn<QString()> Controller::computeTitle() const { | ||||
| 	return langFactory(_isGroup | ||||
| 			? lng_edit_group | ||||
| 			: lng_edit_channel_title); | ||||
| } | ||||
| 
 | ||||
| object_ptr<Ui::VerticalLayout> Controller::createContent() { | ||||
| 	auto result = object_ptr<Ui::VerticalLayout>(_box); | ||||
| 	_wrap.reset(result.data()); | ||||
|  | @ -399,11 +261,7 @@ object_ptr<Ui::VerticalLayout> Controller::createContent() { | |||
| 	_wrap->add(createPhotoAndTitleEdit()); | ||||
| 	_wrap->add(createDescriptionEdit()); | ||||
| 
 | ||||
| 	AddSkip(_wrap); // Divider.
 | ||||
| 	_wrap->add(createPrivaciesButtons()); | ||||
| 	AddSkip(_wrap); // Divider.
 | ||||
| 	_wrap->add(createManageGroupButtons()); | ||||
| 	AddSkip(_wrap); // Divider.
 | ||||
| 
 | ||||
| 	_wrap->add(createStickersEdit()); | ||||
| 	_wrap->add(createDeleteButton()); | ||||
|  | @ -532,142 +390,281 @@ object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() { | |||
| 	return std::move(result); | ||||
| } | ||||
| 
 | ||||
| object_ptr<Ui::RpWidget> Controller::createPrivaciesButtons() { | ||||
| 	Expects(_wrap != nullptr); | ||||
| 
 | ||||
| 	const auto canEditUsername = [&] { | ||||
| 		if (const auto chat = _peer->asChat()) { | ||||
| 			return chat->canEditUsername(); | ||||
| 		} else if (const auto channel = _peer->asChannel()) { | ||||
| 			return channel->canEditUsername(); | ||||
| 		} | ||||
| 		Unexpected("Peer type in Controller::createPrivaciesEdit."); | ||||
| 	}(); | ||||
| 	if (!canEditUsername) { | ||||
| 		return nullptr; | ||||
| void Controller::refreshHistoryVisibility(bool instant = false) { | ||||
| 	if (!_controls.historyVisibilityWrap) { | ||||
| 		return; | ||||
| 	} | ||||
| 	_controls.historyVisibilityWrap->toggle( | ||||
| 		_privacySavedValue == Privacy::Private, | ||||
| 		instant ? anim::type::instant : anim::type::normal); | ||||
| }; | ||||
| 
 | ||||
| 	const auto refreshHistoryVisibility = [=](bool instant = false) { | ||||
| 		if (!_controls.historyVisibilityWrap) { | ||||
| 			return; | ||||
| 		} | ||||
| 		_controls.historyVisibilityWrap->toggle( | ||||
| 			_controls.privacySavedValue == Privacy::Private, | ||||
| 			instant ? anim::type::instant : anim::type::normal); | ||||
| 	}; | ||||
| 
 | ||||
| 	const auto channel = _peer->asChannel(); | ||||
| 	auto isRealChannel = !(!channel || !channel->canEditSignatures() || channel->isMegagroup()); | ||||
| 
 | ||||
| void Controller::fillPrivacyTypeButton() { | ||||
| 	Expects(_controls.buttonsLayout != nullptr); | ||||
| 	// Create Privacy Button.
 | ||||
| 	_controls.privacySavedValue = (_peer->isChannel() | ||||
| 	_privacySavedValue = (_peer->isChannel() | ||||
| 		&& _peer->asChannel()->isPublic()) | ||||
| 		? Privacy::Public | ||||
| 		: Privacy::Private; | ||||
| 
 | ||||
| 	const auto updateType = std::make_shared<rpl::event_stream<Privacy>>(); | ||||
| 
 | ||||
| 	auto result = object_ptr<Ui::PaddingWrap<Ui::VerticalLayout>>( | ||||
| 		_wrap, | ||||
| 		object_ptr<Ui::VerticalLayout>(_wrap), | ||||
| 		st::editPeerTopButtonsLayoutMargins); | ||||
| 	auto resultContainer = result->entity(); | ||||
| 
 | ||||
| 	const auto boxCallback = [=](Privacy checked, QString publicLink) { | ||||
| 		updateType->fire(std::move(checked)); | ||||
| 		_controls.privacySavedValue = checked; | ||||
| 		_controls.usernameSavedValue = publicLink; | ||||
| 		_privacySavedValue = checked; | ||||
| 		_usernameSavedValue = publicLink; | ||||
| 		refreshHistoryVisibility(); | ||||
| 	}; | ||||
| 	const auto buttonCallback = [=]{  | ||||
| 	const auto buttonCallback = [=] { | ||||
| 		Ui::show(Box<EditPeerTypeBox>( | ||||
| 			_peer, | ||||
| 			boxCallback, | ||||
| 			_controls.privacySavedValue, | ||||
| 			_controls.usernameSavedValue | ||||
| 		), LayerOption::KeepOther); | ||||
| 			_privacySavedValue, | ||||
| 			_usernameSavedValue | ||||
| 			), LayerOption::KeepOther); | ||||
| 	}; | ||||
| 
 | ||||
| 	AddButtonWithText( | ||||
| 		resultContainer, | ||||
| 		_controls.buttonsLayout, | ||||
| 		std::move(Lang::Viewer((_peer->isChat() || _peer->isMegagroup()) | ||||
| 			? lng_manage_peer_group_type | ||||
| 			: lng_manage_peer_channel_type)), | ||||
| 
 | ||||
| 		updateType->events( | ||||
| 		) | rpl::map([](Privacy flag) { | ||||
| 			return lang(Privacy::Public == flag | ||||
| 						? lng_manage_public_peer_title | ||||
| 						: lng_manage_private_peer_title); | ||||
| 		}), | ||||
| 		return lang(Privacy::Public == flag | ||||
| 			? lng_manage_public_peer_title | ||||
| 			: lng_manage_private_peer_title); | ||||
| 	}), | ||||
| 		buttonCallback); | ||||
| 	 | ||||
| 	updateType->fire(std::move(_controls.privacySavedValue.value())); | ||||
| 
 | ||||
| 	// Create Signatures Toggle Button.
 | ||||
| 	if (isRealChannel) { | ||||
| 		AddButtonWithText( | ||||
| 			resultContainer, | ||||
| 			std::move(Lang::Viewer(lng_edit_sign_messages)), | ||||
| 			rpl::single(QString()), | ||||
| 			[=] {} | ||||
| 		)->toggleOn(rpl::single(channel->addsSignature()) | ||||
| 		)->toggledValue( | ||||
| 		) | rpl::start_with_next([=](bool toggled) { | ||||
| 			_controls.signaturesSavedValue = toggled; | ||||
| 		}, resultContainer->lifetime()); | ||||
| 	updateType->fire(std::move(_privacySavedValue.value())); | ||||
| } | ||||
| 
 | ||||
| 		return std::move(result); | ||||
| 	} | ||||
| void Controller::fillInviteLinkButton() { | ||||
| 	Expects(_controls.buttonsLayout != nullptr); | ||||
| 
 | ||||
| 	// Create History Visibility Button.
 | ||||
| 	const auto boxCallback = [=](Privacy checked, QString publicLink) {}; | ||||
| 
 | ||||
| 	const auto addHistoryVisibilityButton = [=](LangKey privacyTextKey, Ui::VerticalLayout* container) { | ||||
| 		// Bug with defaultValue here.
 | ||||
| 		_controls.historyVisibilitySavedValue = (!channel || channel->hiddenPreHistory()) | ||||
| 			? HistoryVisibility::Hidden | ||||
| 			: HistoryVisibility::Visible; | ||||
| 
 | ||||
| 		const auto updateHistoryVisibility = std::make_shared<rpl::event_stream<HistoryVisibility>>(); | ||||
| 
 | ||||
| 		const auto boxCallback = [=](HistoryVisibility checked) { | ||||
| 			updateHistoryVisibility->fire(std::move(checked)); | ||||
| 			_controls.historyVisibilitySavedValue = checked; | ||||
| 		}; | ||||
| 		const auto buttonCallback = [=]{  | ||||
| 			Ui::show(Box<EditPeerHistoryVisibilityBox>( | ||||
| 				_peer, | ||||
| 				boxCallback, | ||||
| 				_controls.historyVisibilitySavedValue | ||||
| 	const auto buttonCallback = [=] { | ||||
| 		Ui::show(Box<EditPeerTypeBox>( | ||||
| 			_peer, | ||||
| 			boxCallback | ||||
| 			), LayerOption::KeepOther); | ||||
| 		}; | ||||
| 		AddButtonWithText( | ||||
| 			container, | ||||
| 			std::move(Lang::Viewer(privacyTextKey)), | ||||
| 			updateHistoryVisibility->events( | ||||
| 			) | rpl::map([](HistoryVisibility flag) { | ||||
| 				return lang(HistoryVisibility::Visible == flag | ||||
| 						? lng_manage_history_visibility_shown | ||||
| 						: lng_manage_history_visibility_hidden); | ||||
| 			}), | ||||
| 			buttonCallback); | ||||
| 
 | ||||
| 		updateHistoryVisibility->fire( | ||||
| 			std::move(_controls.historyVisibilitySavedValue.value()) | ||||
| 		); | ||||
| 	}; | ||||
| 
 | ||||
| 	auto wrapLayout = resultContainer->add(object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( | ||||
| 		resultContainer, | ||||
| 		object_ptr<Ui::VerticalLayout>(resultContainer), | ||||
| 	AddButtonWithText( | ||||
| 		_controls.buttonsLayout, | ||||
| 		Lang::Viewer(lng_profile_invite_link_section), | ||||
| 		rpl::single(QString()), //Empty text.
 | ||||
| 		buttonCallback); | ||||
| } | ||||
| 
 | ||||
| void Controller::fillSignaturesButton() { | ||||
| 	Expects(_controls.buttonsLayout != nullptr); | ||||
| 	const auto channel = _peer->asChannel(); | ||||
| 	if (!channel) return; | ||||
| 
 | ||||
| 	AddButtonWithText( | ||||
| 		_controls.buttonsLayout, | ||||
| 		std::move(Lang::Viewer(lng_edit_sign_messages)), | ||||
| 		rpl::single(QString()), | ||||
| 		[=] {} | ||||
| 	)->toggleOn(rpl::single(channel->addsSignature()) | ||||
| 	)->toggledValue( | ||||
| 	) | rpl::start_with_next([=](bool toggled) { | ||||
| 		_signaturesSavedValue = toggled; | ||||
| 	}, _controls.buttonsLayout->lifetime()); | ||||
| } | ||||
| 
 | ||||
| void Controller::fillHistoryVisibilityButton() { | ||||
| 	Expects(_controls.buttonsLayout != nullptr); | ||||
| 
 | ||||
| 	auto wrapLayout = _controls.buttonsLayout->add(object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( | ||||
| 		_controls.buttonsLayout, | ||||
| 		object_ptr<Ui::VerticalLayout>(_controls.buttonsLayout), | ||||
| 		st::boxOptionListPadding)); // Empty margins.
 | ||||
| 	_controls.historyVisibilityWrap = wrapLayout; | ||||
| 
 | ||||
| 	addHistoryVisibilityButton(lng_manage_history_visibility_title, wrapLayout->entity()); | ||||
| 	const auto channel = _peer->asChannel(); | ||||
| 	auto container = wrapLayout->entity(); | ||||
| 
 | ||||
| 	_historyVisibilitySavedValue = (!channel || channel->hiddenPreHistory()) | ||||
| 		? HistoryVisibility::Hidden | ||||
| 		: HistoryVisibility::Visible; | ||||
| 
 | ||||
| 	const auto updateHistoryVisibility = std::make_shared<rpl::event_stream<HistoryVisibility>>(); | ||||
| 
 | ||||
| 	const auto boxCallback = [=](HistoryVisibility checked) { | ||||
| 		updateHistoryVisibility->fire(std::move(checked)); | ||||
| 		_historyVisibilitySavedValue = checked; | ||||
| 	}; | ||||
| 	const auto buttonCallback = [=] { | ||||
| 		Ui::show(Box<EditPeerHistoryVisibilityBox>( | ||||
| 			_peer, | ||||
| 			boxCallback, | ||||
| 			_historyVisibilitySavedValue | ||||
| 			), LayerOption::KeepOther); | ||||
| 	}; | ||||
| 	AddButtonWithText( | ||||
| 		container, | ||||
| 		std::move(Lang::Viewer(lng_manage_history_visibility_title)), | ||||
| 		updateHistoryVisibility->events( | ||||
| 		) | rpl::map([](HistoryVisibility flag) { | ||||
| 		return lang(HistoryVisibility::Visible == flag | ||||
| 			? lng_manage_history_visibility_shown | ||||
| 			: lng_manage_history_visibility_hidden); | ||||
| 	}), | ||||
| 		buttonCallback); | ||||
| 
 | ||||
| 	updateHistoryVisibility->fire( | ||||
| 		std::move(_historyVisibilitySavedValue.value()) | ||||
| 	); | ||||
| 
 | ||||
| 	//While appearing box we should use instant animation.
 | ||||
| 	refreshHistoryVisibility(true); | ||||
| } | ||||
| 
 | ||||
| 	return std::move(result); | ||||
| void Controller::fillManageSection( | ||||
| 	not_null<Window::Navigation*> navigation, | ||||
| 	not_null<PeerData*> peer) { | ||||
| 	Expects(_controls.buttonsLayout != nullptr); | ||||
| 
 | ||||
| 	const auto chat = peer->asChat(); | ||||
| 	const auto channel = peer->asChannel(); | ||||
| 	const auto isChannel = (!chat); | ||||
| 	if (!chat && !channel) return; | ||||
| 
 | ||||
| 	const auto canEditUsername = [=] { | ||||
| 		return isChannel | ||||
| 			? channel->canEditUsername() | ||||
| 			: chat->canEditUsername(); | ||||
| 	}(); | ||||
| 	const auto canEditInviteLink = [=] { | ||||
| 		return isChannel | ||||
| 			? (channel->amCreator() || (channel->adminRights() & ChatAdminRight::f_invite_users)) | ||||
| 			: (chat->amCreator() || (chat->adminRights() & ChatAdminRight::f_invite_users)); | ||||
| 	}(); | ||||
| 	const auto canEditSignatures = [=] { | ||||
| 		return isChannel | ||||
| 			? (channel->canEditSignatures() && !channel->isMegagroup()) | ||||
| 			: false; | ||||
| 	}(); | ||||
| 	const auto canEditPreHistoryHidden = [=] { | ||||
| 		return isChannel | ||||
| 			? channel->canEditPreHistoryHidden() | ||||
| 			: chat->canEditPreHistoryHidden(); | ||||
| 	}(); | ||||
| 
 | ||||
| 
 | ||||
| 	const auto canEditPermissions = [=] { | ||||
| 		return isChannel | ||||
| 			? channel->canEditPermissions() | ||||
| 			: chat->canEditPermissions(); | ||||
| 	}(); | ||||
| 	const auto canViewAdmins = [=] { | ||||
| 		return isChannel | ||||
| 			? channel->canViewAdmins() | ||||
| 			: chat->amIn(); | ||||
| 	}(); | ||||
| 	const auto canViewMembers = [=] { | ||||
| 		return isChannel | ||||
| 			? channel->canViewMembers() | ||||
| 			: chat->amIn(); | ||||
| 	}(); | ||||
| 	const auto canViewKicked = [=] { | ||||
| 		return isChannel | ||||
| 			? (!channel->isMegagroup()) | ||||
| 			: false; | ||||
| 	}(); | ||||
| 	const auto hasRecentActions = [=] { | ||||
| 		return isChannel | ||||
| 			? (channel->hasAdminRights() || channel->amCreator()) | ||||
| 			: false; | ||||
| 	}(); | ||||
| 
 | ||||
| 	AddSkip(_controls.buttonsLayout, 0); | ||||
| 
 | ||||
| 	if (canEditUsername) { | ||||
| 		fillPrivacyTypeButton(); | ||||
| 	} | ||||
| 	else if (canEditInviteLink) { | ||||
| 		fillInviteLinkButton(); | ||||
| 	} | ||||
| 	if (canEditSignatures) { | ||||
| 		fillSignaturesButton(); | ||||
| 	} | ||||
| 	if (canEditPreHistoryHidden) { | ||||
| 		fillHistoryVisibilityButton(); | ||||
| 	} | ||||
| 	if (canEditPreHistoryHidden || canEditSignatures || canEditInviteLink) { | ||||
| 		// Perhaps should fix extra 1-pixel line for design.
 | ||||
| 		AddSkip(_controls.buttonsLayout); | ||||
| 	} | ||||
| 
 | ||||
| 	if (canEditPermissions) { | ||||
| 		AddButtonWithCount( | ||||
| 			_controls.buttonsLayout, | ||||
| 			Lang::Viewer(lng_manage_peer_permissions), | ||||
| 			Info::Profile::RestrictionsCountValue(peer) | ||||
| 			| ToPositiveNumberStringRestrictions(), | ||||
| 			[=] { ShowEditPermissions(peer); }, | ||||
| 			st::infoIconPermissions); | ||||
| 	} | ||||
| 	if (canViewAdmins) { | ||||
| 		AddButtonWithCount( | ||||
| 			_controls.buttonsLayout, | ||||
| 			Lang::Viewer(lng_manage_peer_administrators), | ||||
| 			Info::Profile::AdminsCountValue(peer) | ||||
| 			| ToPositiveNumberString(), | ||||
| 			[=] { | ||||
| 			ParticipantsBoxController::Start( | ||||
| 				navigation, | ||||
| 				peer, | ||||
| 				ParticipantsBoxController::Role::Admins); | ||||
| 		}, | ||||
| 			st::infoIconAdministrators); | ||||
| 	} | ||||
| 	if (canViewMembers) { | ||||
| 		AddButtonWithCount( | ||||
| 			_controls.buttonsLayout, | ||||
| 			Lang::Viewer(lng_manage_peer_members), | ||||
| 			Info::Profile::MembersCountValue(peer) | ||||
| 			| ToPositiveNumberString(), | ||||
| 			[=] { | ||||
| 			ParticipantsBoxController::Start( | ||||
| 				navigation, | ||||
| 				peer, | ||||
| 				ParticipantsBoxController::Role::Members); | ||||
| 		}, | ||||
| 			st::infoIconMembers); | ||||
| 	} | ||||
| 	if (canViewKicked) { | ||||
| 		AddButtonWithCount( | ||||
| 			_controls.buttonsLayout, | ||||
| 			Lang::Viewer(lng_manage_peer_removed_users), | ||||
| 			Info::Profile::KickedCountValue(channel) | ||||
| 			| ToPositiveNumberString(), | ||||
| 			[=] { | ||||
| 			ParticipantsBoxController::Start( | ||||
| 				navigation, | ||||
| 				peer, | ||||
| 				ParticipantsBoxController::Role::Kicked); | ||||
| 		}, | ||||
| 			st::infoIconBlacklist); | ||||
| 	} | ||||
| 	if (hasRecentActions) { | ||||
| 		AddButtonWithCount( | ||||
| 			_controls.buttonsLayout, | ||||
| 			Lang::Viewer(lng_manage_peer_recent_actions), | ||||
| 			rpl::single(QString()), //Empty count.
 | ||||
| 			[=] { | ||||
| 			navigation->showSection(AdminLog::SectionMemento(channel)); | ||||
| 		}, | ||||
| 			st::infoIconRecentActions); | ||||
| 	} | ||||
| 
 | ||||
| 	AddSkip(_controls.buttonsLayout); | ||||
| } | ||||
| 
 | ||||
| object_ptr<Ui::RpWidget> Controller::createManageGroupButtons() { | ||||
|  | @ -677,10 +674,9 @@ object_ptr<Ui::RpWidget> Controller::createManageGroupButtons() { | |||
| 		_wrap, | ||||
| 		object_ptr<Ui::VerticalLayout>(_wrap), | ||||
| 		st::editPeerBottomButtonsLayoutMargins); | ||||
| 	auto container = result->entity(); | ||||
| 	_controls.buttonsLayout = result->entity(); | ||||
| 
 | ||||
| 	FillManageSection(App::wnd()->controller(), _peer, container); | ||||
| 	// setDimensionsToContent(st::boxWidth, content);
 | ||||
| 	fillManageSection(App::wnd()->controller(), _peer); | ||||
| 
 | ||||
| 	return std::move(result); | ||||
| } | ||||
|  | @ -696,7 +692,7 @@ object_ptr<Ui::RpWidget> Controller::createStickersEdit() { | |||
| 	auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( | ||||
| 		_wrap, | ||||
| 		object_ptr<Ui::VerticalLayout>(_wrap), | ||||
| 		st::editPeerInviteLinkMargins); | ||||
| 		st::editPeerInvitesMargins); | ||||
| 	auto container = result->entity(); | ||||
| 
 | ||||
| 	container->add(object_ptr<Ui::FlatLabel>( | ||||
|  | @ -786,11 +782,11 @@ std::optional<Controller::Saving> Controller::validate() const { | |||
| } | ||||
| 
 | ||||
| bool Controller::validateUsername(Saving &to) const { | ||||
| 	if (_controls.privacySavedValue != Privacy::Public) { | ||||
| 	if (_privacySavedValue != Privacy::Public) { | ||||
| 		to.username = QString(); | ||||
| 		return true; | ||||
| 	} | ||||
| 	auto username = _controls.usernameSavedValue.value_or( | ||||
| 	auto username = _usernameSavedValue.value_or( | ||||
| 		_peer->isChannel() | ||||
| 			? _peer->asChannel()->username | ||||
| 			: QString() | ||||
|  | @ -828,19 +824,19 @@ bool Controller::validateHistoryVisibility(Saving &to) const { | |||
| 	if (!_controls.historyVisibilityWrap) return true; | ||||
| 	 | ||||
| 	if (!_controls.historyVisibilityWrap->toggled() | ||||
| 		|| (_controls.privacySavedValue == Privacy::Public)) { | ||||
| 		|| (_privacySavedValue == Privacy::Public)) { | ||||
| 		return true; | ||||
| 	} | ||||
| 	to.hiddenPreHistory | ||||
| 		= (_controls.historyVisibilitySavedValue == HistoryVisibility::Hidden); | ||||
| 		= (_historyVisibilitySavedValue == HistoryVisibility::Hidden); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| bool Controller::validateSignatures(Saving &to) const { | ||||
| 	if (!_controls.signaturesSavedValue.has_value()) { | ||||
| 	if (!_signaturesSavedValue.has_value()) { | ||||
| 		return true; | ||||
| 	} | ||||
| 	to.signatures = _controls.signaturesSavedValue; | ||||
| 	to.signatures = _signaturesSavedValue; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
|  | @ -923,8 +919,7 @@ void Controller::saveUsername() { | |||
| 			} | ||||
| 			return lng_create_channel_link_invalid; | ||||
| 		}(); | ||||
| 		_controls.username->showError(); | ||||
| 		_box->scrollToWidget(_controls.username); | ||||
| 		// Probably never happend.
 | ||||
| 		// showUsernameError(Lang::Viewer(errorKey));
 | ||||
| 		cancelSave(); | ||||
| 	}).send(); | ||||
|  | @ -1187,7 +1182,7 @@ bool EditPeerInfoBox::Available(not_null<PeerData*> peer) { | |||
| 			|| channel->canViewBanned() | ||||
| 			|| channel->canEditInformation() | ||||
| 			|| channel->canEditPermissions() | ||||
| 			|| HasRecentActions(channel); | ||||
| 			|| (channel->hasAdminRights() || channel->amCreator()); | ||||
| 	} else { | ||||
| 		return false; | ||||
| 	} | ||||
|  |  | |||
|  | @ -46,7 +46,7 @@ protected: | |||
| 	void prepare() override; | ||||
| 
 | ||||
| private: | ||||
| 	not_null<PeerData*> _peer; | ||||
| 	rpl::event_stream<> _focusRequests; | ||||
| 	not_null<PeerData*> _peer; | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -8,268 +8,41 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | |||
| #include "boxes/peers/edit_peer_type_box.h" | ||||
| 
 | ||||
| #include "apiwrap.h" | ||||
| #include "apiwrap.h" | ||||
| #include "apiwrap.h" | ||||
| #include "auth_session.h" | ||||
| #include "auth_session.h" | ||||
| #include "auth_session.h" | ||||
| #include "boxes/add_contact_box.h" | ||||
| #include "boxes/add_contact_box.h" | ||||
| #include "boxes/confirm_box.h" | ||||
| #include "boxes/confirm_box.h" | ||||
| #include "boxes/peer_list_controllers.h" | ||||
| #include "boxes/peer_list_controllers.h" | ||||
| #include "boxes/peers/edit_participants_box.h" | ||||
| #include "boxes/peers/edit_participants_box.h" | ||||
| #include "boxes/peers/edit_participants_box.h" | ||||
| #include "boxes/peers/edit_peer_type_box.h" | ||||
| #include "boxes/peers/edit_peer_history_visibility_box.h" | ||||
| #include "boxes/peers/edit_peer_info_box.h" | ||||
| #include "boxes/peers/edit_peer_permissions_box.h" | ||||
| #include "boxes/peers/edit_peer_permissions_box.h" | ||||
| #include "boxes/photo_crop_box.h" | ||||
| #include "boxes/stickers_box.h" | ||||
| #include "boxes/stickers_box.h" | ||||
| #include "chat_helpers/emoji_suggestions_widget.h" | ||||
| #include "chat_helpers/emoji_suggestions_widget.h" | ||||
| #include "core/application.h" | ||||
| #include "data/data_channel.h" | ||||
| #include "data/data_channel.h" | ||||
| #include "data/data_channel.h" | ||||
| #include "data/data_chat.h" | ||||
| #include "data/data_chat.h" | ||||
| #include "data/data_chat.h" | ||||
| #include "data/data_peer.h" | ||||
| #include "data/data_peer.h" | ||||
| #include "history/admin_log/history_admin_log_section.h" | ||||
| #include "history/admin_log/history_admin_log_section.h" | ||||
| #include "info/profile/info_profile_button.h" | ||||
| #include "info/profile/info_profile_button.h" | ||||
| #include "info/profile/info_profile_button.h" | ||||
| #include "info/profile/info_profile_icon.h" | ||||
| #include "info/profile/info_profile_values.h" | ||||
| #include "data/data_session.h" | ||||
| #include "info/profile/info_profile_values.h" | ||||
| #include "lang/lang_keys.h" | ||||
| #include "lang/lang_keys.h" | ||||
| #include "lang/lang_keys.h" | ||||
| #include "mainwidget.h" | ||||
| #include "mainwidget.h" | ||||
| #include "mainwindow.h" | ||||
| #include "mainwindow.h" | ||||
| #include "mtproto/sender.h" | ||||
| #include "mtproto/sender.h" | ||||
| #include "observer_peer.h" | ||||
| #include "styles/style_boxes.h" | ||||
| #include "styles/style_boxes.h" | ||||
| #include "styles/style_boxes.h" | ||||
| #include "styles/style_info.h" | ||||
| #include "styles/style_info.h" | ||||
| #include "styles/style_info.h" | ||||
| #include "ui/rp_widget.h" | ||||
| #include "ui/special_buttons.h" | ||||
| #include "ui/special_buttons.h" | ||||
| #include "ui/toast/toast.h" | ||||
| #include "ui/toast/toast.h" | ||||
| #include "ui/widgets/checkbox.h" | ||||
| #include "ui/widgets/checkbox.h" | ||||
| #include "ui/widgets/checkbox.h" | ||||
| #include "ui/widgets/input_fields.h" | ||||
| #include "ui/widgets/input_fields.h" | ||||
| #include "ui/widgets/labels.h" | ||||
| #include "ui/widgets/labels.h" | ||||
| #include "ui/widgets/labels.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/wrap/vertical_layout.h" | ||||
| #include "ui/wrap/vertical_layout.h" | ||||
| #include "ui/wrap/vertical_layout.h" | ||||
| #include "window/window_controller.h" | ||||
| #include "window/window_controller.h" | ||||
| #include <rpl/combine.h> | ||||
| #include <rpl/flatten_latest.h> | ||||
| #include <rpl/flatten_latest.h> | ||||
| #include <rpl/range.h> | ||||
| #include <rpl/range.h> | ||||
| 
 | ||||
| 
 | ||||
| #include "styles/style_boxes.h" | ||||
| #include "styles/style_dialogs.h" | ||||
| #include "lang/lang_keys.h" | ||||
| #include "mtproto/sender.h" | ||||
| #include "base/flat_set.h" | ||||
| #include "boxes/confirm_box.h" | ||||
| #include "boxes/photo_crop_box.h" | ||||
| #include "boxes/peer_list_controllers.h" | ||||
| #include "boxes/peers/add_participants_box.h" | ||||
| #include "boxes/peers/edit_participant_box.h" | ||||
| #include "boxes/peers/edit_participants_box.h" | ||||
| #include "core/file_utilities.h" | ||||
| #include "core/application.h" | ||||
| #include "chat_helpers/emoji_suggestions_widget.h" | ||||
| #include "ui/widgets/checkbox.h" | ||||
| #include "ui/widgets/buttons.h" | ||||
| #include "ui/widgets/checkbox.h" | ||||
| #include "ui/widgets/input_fields.h" | ||||
| #include "ui/widgets/labels.h" | ||||
| #include "ui/toast/toast.h" | ||||
| #include "ui/special_buttons.h" | ||||
| #include "ui/text_options.h" | ||||
| #include "data/data_channel.h" | ||||
| #include "data/data_chat.h" | ||||
| #include "data/data_user.h" | ||||
| #include "data/data_session.h" | ||||
| #include "mainwidget.h" | ||||
| #include "mainwindow.h" | ||||
| #include "apiwrap.h" | ||||
| #include "observer_peer.h" | ||||
| #include "auth_session.h" | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| 
 | ||||
| std::optional<Privacy> privacySavedValue; | ||||
| std::optional<QString> usernameSavedValue; | ||||
| 
 | ||||
| std::shared_ptr<Ui::RadioenumGroup<Privacy>> privacyButtons; | ||||
| Ui::SlideWrap<Ui::RpWidget> *usernameWrap = nullptr; | ||||
| Ui::UsernameInput *usernameInput = nullptr; | ||||
| base::unique_qptr<Ui::FlatLabel> usernameResult; | ||||
| const style::FlatLabel *usernameResultStyle = nullptr; | ||||
| 
 | ||||
| Ui::SlideWrap<Ui::RpWidget> *createInviteLinkWrap = nullptr; | ||||
| // Ui::SlideWrap<Ui::RpWidget> *_editInviteLinkWrap = nullptr;
 | ||||
| Ui::FlatLabel *inviteLink = nullptr; | ||||
| 
 | ||||
| PeerData *peer = nullptr; | ||||
| 
 | ||||
| bool allowSave = false; | ||||
| 
 | ||||
| 
 | ||||
| mtpRequestId _checkUsernameRequestId = 0; | ||||
| UsernameState _usernameState = UsernameState::Normal; | ||||
| rpl::event_stream<rpl::producer<QString>> _usernameResultTexts; | ||||
| 
 | ||||
| bool isGroup = false; | ||||
| 
 | ||||
| void AddRoundButton( | ||||
| 	not_null<Ui::VerticalLayout*> container, | ||||
| 	Privacy value, | ||||
| 	LangKey groupTextKey, | ||||
| 	LangKey channelTextKey, | ||||
| 	LangKey groupAboutKey, | ||||
| 	LangKey channelAboutKey) { | ||||
| 	container->add(object_ptr<Ui::Radioenum<Privacy>>( | ||||
| 		container, | ||||
| 		privacyButtons, | ||||
| 		value, | ||||
| 		lang(isGroup ? groupTextKey : channelTextKey), | ||||
| 		st::defaultBoxCheckbox)); | ||||
| 	container->add(object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>( | ||||
| 		container, | ||||
| 		object_ptr<Ui::FlatLabel>( | ||||
| 			container, | ||||
| 			Lang::Viewer(isGroup ? groupAboutKey : channelAboutKey), | ||||
| 			st::editPeerPrivacyLabel), | ||||
| 		st::editPeerPrivacyLabelMargins)); | ||||
| 	container->add(object_ptr<Ui::FixedHeightWidget>( | ||||
| 			container, | ||||
| 			st::editPeerPrivacyBottomSkip)); | ||||
| }; | ||||
| 
 | ||||
| void FillGroupedButtons( | ||||
| 	not_null<Ui::VerticalLayout*> parent, | ||||
| 	not_null<PeerData*> peer, | ||||
| 	std::optional<Privacy> savedValue = std::nullopt) { | ||||
| 
 | ||||
| 	const auto canEditUsername = [&] { | ||||
| 		if (const auto chat = peer->asChat()) { | ||||
| 			return chat->canEditUsername(); | ||||
| 		} else if (const auto channel = peer->asChannel()) { | ||||
| 			return channel->canEditUsername(); | ||||
| 		} | ||||
| 		Unexpected("Peer type in Controller::createPrivaciesEdit."); | ||||
| 	}(); | ||||
| 	if (!canEditUsername) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	const auto result = parent->add(object_ptr<Ui::PaddingWrap<Ui::VerticalLayout>>( | ||||
| 		parent, | ||||
| 		object_ptr<Ui::VerticalLayout>(parent), | ||||
| 		st::editPeerPrivaciesMargins)); | ||||
| 	auto container = result->entity(); | ||||
| 
 | ||||
| 	const auto isPublic = peer->isChannel() | ||||
| 		&& peer->asChannel()->isPublic(); | ||||
| 	privacyButtons = std::make_shared<Ui::RadioenumGroup<Privacy>>( | ||||
| 		savedValue.value_or(isPublic ? Privacy::Public : Privacy::Private)); | ||||
| 
 | ||||
| 	AddRoundButton( | ||||
| 		container, | ||||
| 		Privacy::Public, | ||||
| 		lng_create_public_group_title, | ||||
| 		lng_create_public_channel_title, | ||||
| 		lng_create_public_group_about, | ||||
| 		lng_create_public_channel_about); | ||||
| 	AddRoundButton( | ||||
| 		container, | ||||
| 		Privacy::Private, | ||||
| 		lng_create_private_group_title, | ||||
| 		lng_create_private_channel_title, | ||||
| 		lng_create_private_group_about, | ||||
| 		lng_create_private_channel_about); | ||||
| 
 | ||||
| 	// privacyButtons->setChangedCallback([this](Privacy value) {
 | ||||
| 	// 	privacyChanged(value);
 | ||||
| 	// });
 | ||||
| 	if (!isPublic) { | ||||
| 		// checkUsernameAvailability();
 | ||||
| 	} | ||||
| 
 | ||||
| 	// return std::move(result);
 | ||||
| } | ||||
| 
 | ||||
| void FillContent( | ||||
| 	not_null<Ui::VerticalLayout*> parent, | ||||
| 	not_null<PeerData*> peer, | ||||
| 	std::optional<Privacy> savedValue = std::nullopt) { | ||||
| 
 | ||||
| 	FillGroupedButtons(parent, peer, savedValue); | ||||
| } | ||||
| 
 | ||||
| void SetFocusUsername() { | ||||
| 	if (usernameInput) { | ||||
| 		usernameInput->setFocus(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| QString GetUsernameInput() { | ||||
| 	return usernameInput->getLastText().trimmed(); | ||||
| } | ||||
| 
 | ||||
| bool InviteLinkShown() { | ||||
| 	return !privacyButtons | ||||
| 		|| (privacyButtons->value() == Privacy::Private); | ||||
| } | ||||
| 
 | ||||
| QString InviteLinkText() { | ||||
| 	if (const auto channel = peer->asChannel()) { | ||||
| 		return channel->inviteLink(); | ||||
| 	} else if (const auto chat = peer->asChat()) { | ||||
| 		return chat->inviteLink(); | ||||
| 	} | ||||
| 	return QString(); | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
| #include "ui/wrap/padding_wrap.h" | ||||
| #include "ui/wrap/slide_wrap.h" | ||||
| #include "ui/wrap/vertical_layout.h" | ||||
| #include "window/window_controller.h" | ||||
| #include <rpl/flatten_latest.h> | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| constexpr auto kUsernameCheckTimeout = crl::time(200); | ||||
| constexpr auto kMinUsernameLength = 5; | ||||
| constexpr auto kMaxGroupChannelTitle = 255; // See also add_contact_box.
 | ||||
| constexpr auto kMaxChannelDescription = 255; // See also add_contact_box.
 | ||||
| 
 | ||||
| class Controller | ||||
| 	: public base::has_weak_ptr | ||||
|  | @ -277,12 +50,50 @@ class Controller | |||
| public: | ||||
| 	Controller( | ||||
| 		not_null<Ui::VerticalLayout*> container, | ||||
| 		not_null<PeerData*> peer); | ||||
| 		not_null<PeerData*> peer, | ||||
| 		std::optional<Privacy> privacySavedValue, | ||||
| 		std::optional<QString> usernameSavedValue); | ||||
| 
 | ||||
| 	void createContent(); | ||||
| 	QString getUsernameInput(); | ||||
| 	void setFocusUsername(); | ||||
| 
 | ||||
| 	LangKey getTitle() { | ||||
| 		return _isInviteLink  | ||||
| 		? lng_profile_invite_link_section | ||||
| 		: _isGroup | ||||
| 			? lng_manage_peer_group_type | ||||
| 			: lng_manage_peer_channel_type; | ||||
| 	} | ||||
| 
 | ||||
| 	bool isInviteLink() { | ||||
| 		return _isInviteLink; | ||||
| 	} | ||||
| 
 | ||||
| 	bool isAllowSave() { | ||||
| 		return _isAllowSave; | ||||
| 	} | ||||
| 
 | ||||
| 	Privacy getPrivacy() { | ||||
| 		return _controls.privacy->value(); | ||||
| 	} | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
| 	struct Controls { | ||||
| 		std::shared_ptr<Ui::RadioenumGroup<Privacy>> privacy; | ||||
| 		Ui::SlideWrap<Ui::RpWidget> *usernameWrap = nullptr; | ||||
| 		Ui::UsernameInput *usernameInput = nullptr; | ||||
| 		base::unique_qptr<Ui::FlatLabel> usernameResult; | ||||
| 		const style::FlatLabel *usernameResultStyle = nullptr; | ||||
| 
 | ||||
| 		Ui::SlideWrap<Ui::RpWidget> *createInviteLinkWrap = nullptr; | ||||
| 		Ui::SlideWrap<Ui::RpWidget> *editInviteLinkWrap = nullptr; | ||||
| 		Ui::FlatLabel *inviteLink = nullptr; | ||||
| 	}; | ||||
| 
 | ||||
| 	Controls _controls; | ||||
| 
 | ||||
| 	object_ptr<Ui::RpWidget> createPrivaciesEdit(); | ||||
| 	object_ptr<Ui::RpWidget> createUsernameEdit(); | ||||
| 	object_ptr<Ui::RpWidget> createInviteLinkCreate(); | ||||
|  | @ -308,11 +119,30 @@ private: | |||
| 	void revokeInviteLink(); | ||||
| 	void exportInviteLink(const QString &confirmation); | ||||
| 
 | ||||
| 	void fillPrivaciesButtons( | ||||
| 		not_null<Ui::VerticalLayout*> parent, | ||||
| 		std::optional<Privacy> savedValue = std::nullopt); | ||||
| 	void addRoundButton( | ||||
| 		not_null<Ui::VerticalLayout*> container, | ||||
| 		Privacy value, | ||||
| 		LangKey groupTextKey, | ||||
| 		LangKey channelTextKey, | ||||
| 		LangKey groupAboutKey, | ||||
| 		LangKey channelAboutKey); | ||||
| 
 | ||||
| 	bool inviteLinkShown(); | ||||
| 	QString inviteLinkText(); | ||||
| 
 | ||||
| 	void subscribeToMigration(); | ||||
| 	void migrate(not_null<ChannelData*> channel); | ||||
| 
 | ||||
| 	not_null<PeerData*> _peer; | ||||
| 	std::optional<Privacy> _privacySavedValue = std::nullopt; | ||||
| 	std::optional<QString> _usernameSavedValue = std::nullopt; | ||||
| 
 | ||||
| 	bool _isGroup = false; | ||||
| 	bool _isInviteLink = false; | ||||
| 	bool _isAllowSave = false; | ||||
| 
 | ||||
| 	base::unique_qptr<Ui::VerticalLayout> _wrap; | ||||
| 	base::Timer _checkUsernameTimer; | ||||
|  | @ -320,17 +150,21 @@ private: | |||
| 	UsernameState _usernameState = UsernameState::Normal; | ||||
| 	rpl::event_stream<rpl::producer<QString>> _usernameResultTexts; | ||||
| 
 | ||||
| 	Ui::SlideWrap<Ui::RpWidget> *_editInviteLinkWrap = nullptr; | ||||
| 
 | ||||
| 	rpl::lifetime _lifetime; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| Controller::Controller( | ||||
| 	not_null<Ui::VerticalLayout*> container, | ||||
| 	not_null<PeerData*> peer) | ||||
| 	not_null<PeerData*> peer, | ||||
| 	std::optional<Privacy> privacySavedValue, | ||||
| 	std::optional<QString> usernameSavedValue) | ||||
| : _peer(peer) | ||||
| , _privacySavedValue(privacySavedValue) | ||||
| , _usernameSavedValue(usernameSavedValue) | ||||
| , _isGroup(_peer->isChat() || _peer->isMegagroup()) | ||||
| , _isInviteLink(!_privacySavedValue.has_value() && !_usernameSavedValue.has_value()) | ||||
| , _isAllowSave(!_usernameSavedValue.value_or(QString()).isEmpty()) | ||||
| , _wrap(container) | ||||
| , _checkUsernameTimer([=] { checkUsernameAvailability(); }) { | ||||
| 	subscribeToMigration(); | ||||
|  | @ -351,41 +185,150 @@ void Controller::migrate(not_null<ChannelData*> channel) { | |||
| } | ||||
| 
 | ||||
| void Controller::createContent() { | ||||
| 	privacyButtons->setChangedCallback([this](Privacy value) { | ||||
| 		privacyChanged(value); | ||||
| 	}); | ||||
| 	_controls = Controls(); | ||||
| 	 | ||||
| 	if (_isInviteLink) { | ||||
| 		_wrap->add(createInviteLinkCreate()); | ||||
| 		_wrap->add(createInviteLinkEdit()); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	// _wrap->add(createPrivaciesEdit());
 | ||||
| 	fillPrivaciesButtons(_wrap, _privacySavedValue); | ||||
| 	// Skip.
 | ||||
| 	_wrap->add(object_ptr<BoxContentDivider>(_wrap)); | ||||
| 	//
 | ||||
| 	_wrap->add(createInviteLinkCreate()); | ||||
| 	_wrap->add(createInviteLinkEdit()); | ||||
| 	_wrap->add(createUsernameEdit()); | ||||
| 
 | ||||
| 	if (privacyButtons->value() == Privacy::Private) { | ||||
| 	if (_controls.privacy->value() == Privacy::Private) { | ||||
| 		checkUsernameAvailability(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void Controller::addRoundButton( | ||||
| 	not_null<Ui::VerticalLayout*> container, | ||||
| 	Privacy value, | ||||
| 	LangKey groupTextKey, | ||||
| 	LangKey channelTextKey, | ||||
| 	LangKey groupAboutKey, | ||||
| 	LangKey channelAboutKey) { | ||||
| 	container->add(object_ptr<Ui::Radioenum<Privacy>>( | ||||
| 		container, | ||||
| 		_controls.privacy, | ||||
| 		value, | ||||
| 		lang(_isGroup ? groupTextKey : channelTextKey), | ||||
| 		st::editPeerPrivacyBoxCheckbox)); | ||||
| 	container->add(object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>( | ||||
| 		container, | ||||
| 		object_ptr<Ui::FlatLabel>( | ||||
| 			container, | ||||
| 			Lang::Viewer(_isGroup ? groupAboutKey : channelAboutKey), | ||||
| 			st::editPeerPrivacyLabel), | ||||
| 		st::editPeerPrivacyLabelMargins)); | ||||
| 	container->add(object_ptr<Ui::FixedHeightWidget>( | ||||
| 			container, | ||||
| 			st::editPeerPrivacyBottomSkip)); | ||||
| }; | ||||
| 
 | ||||
| void Controller::fillPrivaciesButtons( | ||||
| 	not_null<Ui::VerticalLayout*> parent, | ||||
| 	std::optional<Privacy> savedValue) { | ||||
| 
 | ||||
| 	const auto canEditUsername = [&] { | ||||
| 		if (const auto chat = _peer->asChat()) { | ||||
| 			return chat->canEditUsername(); | ||||
| 		} else if (const auto channel = _peer->asChannel()) { | ||||
| 			return channel->canEditUsername(); | ||||
| 		} | ||||
| 		Unexpected("Peer type in Controller::createPrivaciesEdit."); | ||||
| 	}(); | ||||
| 	if (!canEditUsername) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	const auto result = parent->add(object_ptr<Ui::PaddingWrap<Ui::VerticalLayout>>( | ||||
| 		parent, | ||||
| 		object_ptr<Ui::VerticalLayout>(parent), | ||||
| 		st::editPeerPrivaciesMargins)); | ||||
| 	auto container = result->entity(); | ||||
| 
 | ||||
| 	const auto isPublic = _peer->isChannel() | ||||
| 		&& _peer->asChannel()->isPublic(); | ||||
| 	_controls.privacy = std::make_shared<Ui::RadioenumGroup<Privacy>>( | ||||
| 		savedValue.value_or(isPublic ? Privacy::Public : Privacy::Private)); | ||||
| 
 | ||||
| 	addRoundButton( | ||||
| 		container, | ||||
| 		Privacy::Public, | ||||
| 		lng_create_public_group_title, | ||||
| 		lng_create_public_channel_title, | ||||
| 		lng_create_public_group_about, | ||||
| 		lng_create_public_channel_about); | ||||
| 	addRoundButton( | ||||
| 		container, | ||||
| 		Privacy::Private, | ||||
| 		lng_create_private_group_title, | ||||
| 		lng_create_private_channel_title, | ||||
| 		lng_create_private_group_about, | ||||
| 		lng_create_private_channel_about); | ||||
| 
 | ||||
| 	_controls.privacy->setChangedCallback([=](Privacy value) { | ||||
| 		privacyChanged(value); | ||||
| 	}); | ||||
| 	 | ||||
| 	if (!isPublic) { | ||||
| 		// checkUsernameAvailability();
 | ||||
| 	} | ||||
| 
 | ||||
| 	// return std::move(result);
 | ||||
| } | ||||
| 
 | ||||
| void Controller::setFocusUsername() { | ||||
| 	if (_controls.usernameInput) { | ||||
| 		_controls.usernameInput->setFocus(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| QString Controller::getUsernameInput() { | ||||
| 	return _controls.usernameInput->getLastText().trimmed(); | ||||
| } | ||||
| 
 | ||||
| QString Controller::inviteLinkText() { | ||||
| 	if (const auto channel = _peer->asChannel()) { | ||||
| 		return channel->inviteLink(); | ||||
| 	} else if (const auto chat = _peer->asChat()) { | ||||
| 		return chat->inviteLink(); | ||||
| 	} | ||||
| 	return QString(); | ||||
| } | ||||
| 
 | ||||
| object_ptr<Ui::RpWidget> Controller::createUsernameEdit() { | ||||
| 	Expects(_wrap != nullptr); | ||||
| 
 | ||||
| 	const auto channel = _peer->asChannel(); | ||||
| 	const auto username = usernameSavedValue.value_or(channel ? channel->username : QString()); | ||||
| 	const auto username = _usernameSavedValue.value_or(channel ? channel->username : QString()); | ||||
| 
 | ||||
| 	auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( | ||||
| 		_wrap, | ||||
| 		object_ptr<Ui::VerticalLayout>(_wrap), | ||||
| 		st::editPeerUsernameMargins); | ||||
| 	usernameWrap = result.data(); | ||||
| 	_controls.usernameWrap = result.data(); | ||||
| 
 | ||||
| 	auto container = result->entity(); | ||||
| 	container->add(object_ptr<Ui::FlatLabel>( | ||||
| 	container->add(object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>( | ||||
| 		container, | ||||
| 		Lang::Viewer(lng_create_group_link), | ||||
| 		st::editPeerSectionLabel)); | ||||
| 		object_ptr<Ui::FlatLabel>( | ||||
| 			container, | ||||
| 			Lang::Viewer(lng_create_group_link), | ||||
| 			st::editPeerSectionLabel), | ||||
| 		st::editPeerUsernameTitleLabelMargins)); | ||||
| 
 | ||||
| 	auto placeholder = container->add(object_ptr<Ui::RpWidget>( | ||||
| 		container)); | ||||
| 	placeholder->setAttribute(Qt::WA_TransparentForMouseEvents); | ||||
| 	usernameInput = Ui::AttachParentChild( | ||||
| 	_controls.usernameInput = Ui::AttachParentChild( | ||||
| 		container, | ||||
| 		object_ptr<Ui::UsernameInput>( | ||||
| 			container, | ||||
|  | @ -393,24 +336,32 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() { | |||
| 			Fn<QString()>(), | ||||
| 			username, | ||||
| 			true)); | ||||
| 	usernameInput->heightValue( | ||||
| 	_controls.usernameInput->heightValue( | ||||
| 	) | rpl::start_with_next([placeholder](int height) { | ||||
| 		placeholder->resize(placeholder->width(), height); | ||||
| 	}, placeholder->lifetime()); | ||||
| 	placeholder->widthValue( | ||||
| 	) | rpl::start_with_next([this](int width) { | ||||
| 		usernameInput->resize( | ||||
| 		_controls.usernameInput->resize( | ||||
| 			width, | ||||
| 			usernameInput->height()); | ||||
| 			_controls.usernameInput->height()); | ||||
| 	}, placeholder->lifetime()); | ||||
| 	usernameInput->move(placeholder->pos()); | ||||
| 	_controls.usernameInput->move(placeholder->pos()); | ||||
| 
 | ||||
| 	container->add(object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>( | ||||
| 		container, | ||||
| 		object_ptr<Ui::FlatLabel>( | ||||
| 			container, | ||||
| 			Lang::Viewer(lng_create_channel_link_about), | ||||
| 			st::editPeerPrivacyLabel), | ||||
| 		st::editPeerUsernameAboutLabelMargins)); | ||||
| 
 | ||||
| 	QObject::connect( | ||||
| 		usernameInput, | ||||
| 		_controls.usernameInput, | ||||
| 		&Ui::UsernameInput::changed, | ||||
| 		[this] { usernameChanged(); }); | ||||
| 
 | ||||
| 	auto shown = (privacyButtons->value() == Privacy::Public); | ||||
| 	auto shown = (_controls.privacy->value() == Privacy::Public); | ||||
| 	result->toggle(shown, anim::type::instant); | ||||
| 
 | ||||
| 	return std::move(result); | ||||
|  | @ -418,7 +369,7 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() { | |||
| 
 | ||||
| void Controller::privacyChanged(Privacy value) { | ||||
| 	auto toggleEditUsername = [&] { | ||||
| 		usernameWrap->toggle( | ||||
| 		_controls.usernameWrap->toggle( | ||||
| 			(value == Privacy::Public), | ||||
| 			anim::type::instant); | ||||
| 	}; | ||||
|  | @ -431,7 +382,7 @@ void Controller::privacyChanged(Privacy value) { | |||
| 			refreshEditInviteLink(); | ||||
| 			toggleEditUsername(); | ||||
| 
 | ||||
| 			usernameResult = nullptr; | ||||
| 			_controls.usernameResult = nullptr; | ||||
| 			checkUsernameAvailability(); | ||||
| 		} else { | ||||
| 			toggleEditUsername(); | ||||
|  | @ -444,29 +395,29 @@ void Controller::privacyChanged(Privacy value) { | |||
| 			askUsernameRevoke(); | ||||
| 			return; | ||||
| 		} else if (_usernameState == UsernameState::NotAvailable) { | ||||
| 			privacyButtons->setValue(Privacy::Private); | ||||
| 			_controls.privacy->setValue(Privacy::Private); | ||||
| 			return; | ||||
| 		} | ||||
| 		refreshVisibilities(); | ||||
| 		usernameInput->setDisplayFocused(true); | ||||
| 		SetFocusUsername(); | ||||
| 		// _box->scrollToWidget(usernameInput);
 | ||||
| 		_controls.usernameInput->setDisplayFocused(true); | ||||
| 		setFocusUsername(); | ||||
| 		// _box->scrollToWidget(_controls.usernameInput);
 | ||||
| 	} else { | ||||
| 		request(base::take(_checkUsernameRequestId)).cancel(); | ||||
| 		_checkUsernameTimer.cancel(); | ||||
| 		refreshVisibilities(); | ||||
| 		SetFocusUsername(); | ||||
| 		setFocusUsername(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Controller::checkUsernameAvailability() { | ||||
| 	if (!usernameInput) { | ||||
| 	if (!_controls.usernameInput) { | ||||
| 		return; | ||||
| 	} | ||||
| 	auto initial = (privacyButtons->value() != Privacy::Public); | ||||
| 	auto initial = (_controls.privacy->value() != Privacy::Public); | ||||
| 	auto checking = initial | ||||
| 		? qsl(".bad.") | ||||
| 		: GetUsernameInput(); | ||||
| 		: getUsernameInput(); | ||||
| 	if (checking.size() < kMinUsernameLength) { | ||||
| 		return; | ||||
| 	} | ||||
|  | @ -495,17 +446,17 @@ void Controller::checkUsernameAvailability() { | |||
| 		_usernameState = UsernameState::Normal; | ||||
| 		if (type == qstr("CHANNEL_PUBLIC_GROUP_NA")) { | ||||
| 			_usernameState = UsernameState::NotAvailable; | ||||
| 			privacyButtons->setValue(Privacy::Private); | ||||
| 			_controls.privacy->setValue(Privacy::Private); | ||||
| 		} else if (type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) { | ||||
| 			_usernameState = UsernameState::TooMany; | ||||
| 			if (privacyButtons->value() == Privacy::Public) { | ||||
| 			if (_controls.privacy->value() == Privacy::Public) { | ||||
| 				askUsernameRevoke(); | ||||
| 			} | ||||
| 		} else if (initial) { | ||||
| 			if (privacyButtons->value() == Privacy::Public) { | ||||
| 				usernameResult = nullptr; | ||||
| 				SetFocusUsername(); | ||||
| 				// _box->scrollToWidget(usernameInput);
 | ||||
| 			if (_controls.privacy->value() == Privacy::Public) { | ||||
| 				_controls.usernameResult = nullptr; | ||||
| 				setFocusUsername(); | ||||
| 				// _box->scrollToWidget(_controls.usernameInput);
 | ||||
| 			} | ||||
| 		} else if (type == qstr("USERNAME_INVALID")) { | ||||
| 			showUsernameError( | ||||
|  | @ -519,10 +470,10 @@ void Controller::checkUsernameAvailability() { | |||
| } | ||||
| 
 | ||||
| void Controller::askUsernameRevoke() { | ||||
| 	privacyButtons->setValue(Privacy::Private); | ||||
| 	_controls.privacy->setValue(Privacy::Private); | ||||
| 	auto revokeCallback = crl::guard(this, [this] { | ||||
| 		_usernameState = UsernameState::Normal; | ||||
| 		privacyButtons->setValue(Privacy::Public); | ||||
| 		_controls.privacy->setValue(Privacy::Public); | ||||
| 		checkUsernameAvailability(); | ||||
| 	}); | ||||
| 	Ui::show( | ||||
|  | @ -531,10 +482,10 @@ void Controller::askUsernameRevoke() { | |||
| } | ||||
| 
 | ||||
| void Controller::usernameChanged() { | ||||
| 	allowSave = false; | ||||
| 	auto username = GetUsernameInput(); | ||||
| 	_isAllowSave = false; | ||||
| 	auto username = getUsernameInput(); | ||||
| 	if (username.isEmpty()) { | ||||
| 		usernameResult = nullptr; | ||||
| 		_controls.usernameResult = nullptr; | ||||
| 		_checkUsernameTimer.cancel(); | ||||
| 		return; | ||||
| 	} | ||||
|  | @ -551,7 +502,7 @@ void Controller::usernameChanged() { | |||
| 		showUsernameError( | ||||
| 			Lang::Viewer(lng_create_channel_link_too_short)); | ||||
| 	} else { | ||||
| 		usernameResult = nullptr; | ||||
| 		_controls.usernameResult = nullptr; | ||||
| 		_checkUsernameTimer.callOnce(kUsernameCheckTimeout); | ||||
| 	} | ||||
| } | ||||
|  | @ -561,7 +512,7 @@ void Controller::showUsernameError(rpl::producer<QString> &&error) { | |||
| } | ||||
| 
 | ||||
| void Controller::showUsernameGood() { | ||||
| 	allowSave = true; | ||||
| 	_isAllowSave = true; | ||||
| 	showUsernameResult( | ||||
| 		Lang::Viewer(lng_create_channel_link_available), | ||||
| 		&st::editPeerUsernameGood); | ||||
|  | @ -570,14 +521,14 @@ void Controller::showUsernameGood() { | |||
| void Controller::showUsernameResult( | ||||
| 		rpl::producer<QString> &&text, | ||||
| 		not_null<const style::FlatLabel*> st) { | ||||
| 	if (!usernameResult | ||||
| 		|| usernameResultStyle != st) { | ||||
| 		usernameResultStyle = st; | ||||
| 		usernameResult = base::make_unique_q<Ui::FlatLabel>( | ||||
| 			usernameWrap, | ||||
| 	if (!_controls.usernameResult | ||||
| 		|| _controls.usernameResultStyle != st) { | ||||
| 		_controls.usernameResultStyle = st; | ||||
| 		_controls.usernameResult = base::make_unique_q<Ui::FlatLabel>( | ||||
| 			_controls.usernameWrap, | ||||
| 			_usernameResultTexts.events() | rpl::flatten_latest(), | ||||
| 			*st); | ||||
| 		auto label = usernameResult.get(); | ||||
| 		auto label = _controls.usernameResult.get(); | ||||
| 		label->show(); | ||||
| 		label->widthValue( | ||||
| 		) | rpl::start_with_next([label] { | ||||
|  | @ -625,7 +576,7 @@ bool Controller::canEditInviteLink() const { | |||
| } | ||||
| 
 | ||||
| void Controller::observeInviteLink() { | ||||
| 	if (!_editInviteLinkWrap) { | ||||
| 	if (!_controls.editInviteLinkWrap) { | ||||
| 		return; | ||||
| 	} | ||||
| 	// return; //
 | ||||
|  | @ -635,7 +586,7 @@ void Controller::observeInviteLink() { | |||
| 	) | rpl::start_with_next([=] { | ||||
| 		refreshCreateInviteLink(); | ||||
| 		refreshEditInviteLink(); | ||||
| 	}, _editInviteLinkWrap->lifetime()); | ||||
| 	}, _controls.editInviteLinkWrap->lifetime()); | ||||
| } | ||||
| 
 | ||||
| object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() { | ||||
|  | @ -648,26 +599,28 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() { | |||
| 	auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( | ||||
| 		_wrap, | ||||
| 		object_ptr<Ui::VerticalLayout>(_wrap), | ||||
| 		st::editPeerInviteLinkMargins); | ||||
| 	_editInviteLinkWrap = result.data(); | ||||
| 		st::editPeerInvitesMargins); | ||||
| 	_controls.editInviteLinkWrap = result.data(); | ||||
| 
 | ||||
| 	auto container = result->entity(); | ||||
| 	container->add(object_ptr<Ui::FlatLabel>( | ||||
| 		container, | ||||
| 		Lang::Viewer(lng_profile_invite_link_section), | ||||
| 		st::editPeerSectionLabel)); | ||||
| 	container->add(object_ptr<Ui::FixedHeightWidget>( | ||||
| 		container, | ||||
| 		st::editPeerInviteLinkSkip)); | ||||
| 	if (!_isInviteLink) { | ||||
| 		container->add(object_ptr<Ui::FlatLabel>( | ||||
| 			container, | ||||
| 			Lang::Viewer(lng_profile_invite_link_section), | ||||
| 			st::editPeerSectionLabel)); | ||||
| 		container->add(object_ptr<Ui::FixedHeightWidget>( | ||||
| 			container, | ||||
| 			st::editPeerInviteLinkBoxBottomSkip)); | ||||
| 	} | ||||
| 
 | ||||
| 	inviteLink = container->add(object_ptr<Ui::FlatLabel>( | ||||
| 	_controls.inviteLink = container->add(object_ptr<Ui::FlatLabel>( | ||||
| 		container, | ||||
| 		st::editPeerInviteLink)); | ||||
| 	inviteLink->setSelectable(true); | ||||
| 	inviteLink->setContextCopyText(QString()); | ||||
| 	inviteLink->setBreakEverywhere(true); | ||||
| 	inviteLink->setClickHandlerFilter([=](auto&&...) { | ||||
| 		QApplication::clipboard()->setText(InviteLinkText()); | ||||
| 	_controls.inviteLink->setSelectable(true); | ||||
| 	_controls.inviteLink->setContextCopyText(QString()); | ||||
| 	_controls.inviteLink->setBreakEverywhere(true); | ||||
| 	_controls.inviteLink->setClickHandlerFilter([=](auto&&...) { | ||||
| 		QApplication::clipboard()->setText(inviteLinkText()); | ||||
| 		Ui::Toast::Show(lang(lng_group_invite_copied)); | ||||
| 		return false; | ||||
| 	}); | ||||
|  | @ -687,7 +640,7 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() { | |||
| } | ||||
| 
 | ||||
| void Controller::refreshEditInviteLink() { | ||||
| 	auto link = InviteLinkText(); | ||||
| 	auto link = inviteLinkText(); | ||||
| 	auto text = TextWithEntities(); | ||||
| 	if (!link.isEmpty()) { | ||||
| 		text.text = link; | ||||
|  | @ -701,13 +654,13 @@ void Controller::refreshEditInviteLink() { | |||
| 			text.text.size(), | ||||
| 			link)); | ||||
| 	} | ||||
| 	inviteLink->setMarkedText(text); | ||||
| 	_controls.inviteLink->setMarkedText(text); | ||||
| 
 | ||||
| 	// Hack to expand FlatLabel width to naturalWidth again.
 | ||||
| 	_editInviteLinkWrap->resizeToWidth(st::boxWideWidth); | ||||
| 	_controls.editInviteLinkWrap->resizeToWidth(st::boxWideWidth); | ||||
| 
 | ||||
| 	_editInviteLinkWrap->toggle( | ||||
| 		InviteLinkShown() && !link.isEmpty(), | ||||
| 	_controls.editInviteLinkWrap->toggle( | ||||
| 		inviteLinkShown() && !link.isEmpty(), | ||||
| 		anim::type::instant); | ||||
| } | ||||
| 
 | ||||
|  | @ -721,16 +674,18 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkCreate() { | |||
| 	auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( | ||||
| 		_wrap, | ||||
| 		object_ptr<Ui::VerticalLayout>(_wrap), | ||||
| 		st::editPeerInviteLinkMargins); | ||||
| 		st::editPeerInvitesMargins); | ||||
| 	auto container = result->entity(); | ||||
| 
 | ||||
| 	container->add(object_ptr<Ui::FlatLabel>( | ||||
| 		container, | ||||
| 		Lang::Viewer(lng_profile_invite_link_section), | ||||
| 		st::editPeerSectionLabel)); | ||||
| 	container->add(object_ptr<Ui::FixedHeightWidget>( | ||||
| 		container, | ||||
| 		st::editPeerInviteLinkSkip)); | ||||
| 	if (!_isInviteLink) { | ||||
| 		container->add(object_ptr<Ui::FlatLabel>( | ||||
| 			container, | ||||
| 			Lang::Viewer(lng_profile_invite_link_section), | ||||
| 			st::editPeerSectionLabel)); | ||||
| 		container->add(object_ptr<Ui::FixedHeightWidget>( | ||||
| 			container, | ||||
| 			st::editPeerInviteLinkSkip)); | ||||
| 	} | ||||
| 
 | ||||
| 	container->add(object_ptr<Ui::LinkButton>( | ||||
| 		_wrap, | ||||
|  | @ -739,7 +694,7 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkCreate() { | |||
| 	)->addClickHandler([this] { | ||||
| 		createInviteLink(); | ||||
| 	}); | ||||
| 	createInviteLinkWrap = result.data(); | ||||
| 	_controls.createInviteLinkWrap = result.data(); | ||||
| 
 | ||||
| 	observeInviteLink(); | ||||
| 
 | ||||
|  | @ -747,65 +702,72 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkCreate() { | |||
| } | ||||
| 
 | ||||
| void Controller::refreshCreateInviteLink() { | ||||
| 	createInviteLinkWrap->toggle( | ||||
| 		InviteLinkShown() && InviteLinkText().isEmpty(), | ||||
| 	_controls.createInviteLinkWrap->toggle( | ||||
| 		inviteLinkShown() && inviteLinkText().isEmpty(), | ||||
| 		anim::type::instant); | ||||
| } | ||||
| 
 | ||||
| bool Controller::inviteLinkShown() { | ||||
| 	return !_controls.privacy | ||||
| 		|| (_controls.privacy->value() == Privacy::Private) | ||||
| 		|| _isInviteLink; | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
| 
 | ||||
| EditPeerTypeBox::EditPeerTypeBox( | ||||
| 		QWidget*, | ||||
| 		not_null<PeerData*> p, | ||||
| 		not_null<PeerData*> peer, | ||||
| 		FnMut<void(Privacy, QString)> savedCallback, | ||||
| 		std::optional<Privacy> privacySaved, | ||||
| 		std::optional<QString> usernameSaved) | ||||
| : _peer(p) | ||||
| , _savedCallback(std::move(savedCallback)) { | ||||
| 	peer = p; | ||||
| 	privacySavedValue = privacySaved; | ||||
| 	usernameSavedValue = usernameSaved; | ||||
| 	allowSave = !usernameSaved->isEmpty() && usernameSaved.has_value(); | ||||
| : _peer(peer) | ||||
| , _savedCallback(std::move(savedCallback)) | ||||
| , _privacySavedValue(privacySaved) | ||||
| , _usernameSavedValue(usernameSaved) { | ||||
| } | ||||
| 
 | ||||
| void EditPeerTypeBox::prepare() { | ||||
| 	_peer->updateFull(); | ||||
| 	 | ||||
| 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this); | ||||
| 
 | ||||
| 	setTitle(langFactory((peer->isChat() || peer->isMegagroup()) | ||||
| 		? lng_manage_peer_group_type | ||||
| 		: lng_manage_peer_channel_type)); | ||||
| 	auto controller = Ui::CreateChild<Controller>( | ||||
| 		this, | ||||
| 		content, | ||||
| 		_peer, | ||||
| 		_privacySavedValue, | ||||
| 		_usernameSavedValue); | ||||
| 	_focusRequests.events( | ||||
| 	) | rpl::start_with_next( | ||||
| 		[=] { controller->setFocusUsername(); }, | ||||
| 		lifetime()); | ||||
| 	controller->createContent(); | ||||
| 
 | ||||
| 	addButton(langFactory(lng_settings_save), [=] { | ||||
| 		const auto v = privacyButtons->value(); | ||||
| 		if (!allowSave && (v == Privacy::Public)) { | ||||
| 			SetFocusUsername(); | ||||
| 			return; | ||||
| 		} | ||||
| 	setTitle(langFactory(controller->getTitle())); | ||||
| 
 | ||||
| 		auto local = std::move(_savedCallback); | ||||
| 		local(v, | ||||
| 			(v == Privacy::Public) | ||||
| 				? GetUsernameInput() | ||||
| 				: QString()); // We dont need username with private type.
 | ||||
| 		closeBox(); | ||||
| 	}); | ||||
| 	addButton(langFactory(lng_cancel), [=] { closeBox(); }); | ||||
| 	if (!controller->isInviteLink()) { | ||||
| 		addButton(langFactory(lng_settings_save), [=] { | ||||
| 			const auto v = controller->getPrivacy(); | ||||
| 			if (!controller->isAllowSave() && (v == Privacy::Public)) { | ||||
| 				controller->setFocusUsername(); | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 	setupContent(); | ||||
| 			auto local = std::move(_savedCallback); | ||||
| 			local(v, | ||||
| 				(v == Privacy::Public) | ||||
| 					? controller->getUsernameInput() | ||||
| 					: QString()); // We dont need username with private type.
 | ||||
| 			closeBox(); | ||||
| 		}); | ||||
| 	} | ||||
| 	addButton(langFactory(controller->isInviteLink() ? lng_close : lng_cancel), [=] { closeBox(); }); | ||||
| 
 | ||||
| 	setDimensionsToContent(st::boxWideWidth, content); | ||||
| } | ||||
| 
 | ||||
| void EditPeerTypeBox::setupContent() { | ||||
| 	isGroup = (_peer->isChat() || _peer->isMegagroup()); | ||||
| 
 | ||||
| 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this); | ||||
| 	FillContent(content, _peer, privacySavedValue); | ||||
| 
 | ||||
| 	auto controller = Ui::CreateChild<Controller>(this, content, _peer); | ||||
| 	_focusRequests.events( | ||||
| 	) | rpl::start_with_next( | ||||
| 		[=] { SetFocusUsername(); }, | ||||
| 		lifetime()); | ||||
| 	controller->createContent(); | ||||
| 	// setDimensionsToContent(st::boxWidth, content);
 | ||||
| 	setDimensionsToContent(st::boxWideWidth, content); | ||||
| 	 | ||||
| 	 | ||||
| } | ||||
|  |  | |||
|  | @ -54,6 +54,9 @@ private: | |||
| 	not_null<PeerData*> _peer; | ||||
| 	FnMut<void(Privacy, QString)> _savedCallback; | ||||
| 
 | ||||
| 	std::optional<Privacy> _privacySavedValue = std::nullopt; | ||||
| 	std::optional<QString> _usernameSavedValue = std::nullopt; | ||||
| 
 | ||||
| 	rpl::event_stream<> _focusRequests; | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -639,9 +639,12 @@ infoEmptyLabel: FlatLabel(defaultFlatLabel) { | |||
| 	textFg: windowSubTextFg; | ||||
| } | ||||
| 
 | ||||
| editPeerBottomButtonsLayoutMargins: margins(0px, 11px, 0px, 7px); | ||||
| editPeerBottomButtonsLayoutMargins: margins(0px, 7px, 0px, 0px); | ||||
| editPeerTopButtonsLayoutMargins: margins(0px, 12px, 0px, 6px); | ||||
| 
 | ||||
| editPeerTopButtonsLayoutSkip: 13px; | ||||
| editPeerTopButtonsLayoutSkipToBottom: 12px; | ||||
| 
 | ||||
| editPeerHistoryVisibilityTopSkip: 8px; | ||||
| 
 | ||||
| editPeerDeleteButtonMargins: margins(23px, 16px, 23px, 16px); | ||||
|  | @ -651,15 +654,20 @@ editPeerTitle: defaultInputField; | |||
| editPeerTitleMargins: margins(27px, 21px, 23px, 8px); | ||||
| editPeerDescription: newGroupDescription; | ||||
| editPeerDescriptionMargins: margins(23px, 5px, 23px, 16px); | ||||
| editPeerPrivaciesMargins: margins(15px, 0px, 23px, 0px); | ||||
| editPeerPrivaciesMargins: margins(15px, 7px, 23px, 0px); | ||||
| editPeerPrivacyTopSkip: 10px; | ||||
| editPeerPrivacyBottomSkip: 16px; | ||||
| editPeerPrivacyLabel: FlatLabel(defaultFlatLabel) { | ||||
| 	minWidth: 220px; | ||||
| 	textFg: windowSubTextFg; | ||||
| } | ||||
| editPeerPrivacyBoxCheckbox: Checkbox(defaultBoxCheckbox) { | ||||
| 	margin: margins(0px, 8px, 0px, 8px); | ||||
| 	style: boxTextStyle; | ||||
| } | ||||
| editPeerHistoryVisibilityLabelMargins: margins(34px, 0px, 48px, 0px); | ||||
| editPeerPrivacyLabelMargins: margins(34px, 0px, 34px, 0px); | ||||
| editPeerPrivacyLabelMargins: margins(42px, 0px, 34px, 0px); | ||||
| editPeerPreHistoryLabelMargins: margins(34px, 0px, 34px, 0px); | ||||
| editPeerSectionLabel: FlatLabel(boxTitle) { | ||||
| 	style: TextStyle(defaultTextStyle) { | ||||
| 		font: font(15px semibold); | ||||
|  | @ -667,6 +675,8 @@ editPeerSectionLabel: FlatLabel(boxTitle) { | |||
| 		linkFontOver: font(15px semibold underline); | ||||
| 	} | ||||
| } | ||||
| editPeerUsernameTitleLabelMargins: margins(0px, 0px, 0px, 10px); | ||||
| editPeerUsernameAboutLabelMargins: margins(0px, 15px, 34px, 15px); | ||||
| editPeerUsername: setupChannelLink; | ||||
| editPeerUsernameSkip: 8px; | ||||
| editPeerInviteLink: FlatLabel(defaultFlatLabel) { | ||||
|  | @ -674,7 +684,7 @@ editPeerInviteLink: FlatLabel(defaultFlatLabel) { | |||
| 	style: boxTextStyle; | ||||
| } | ||||
| editPeerInviteLinkButton: boxLinkButton; | ||||
| editPeerUsernameMargins: margins(15px, 2px, 35px, 2px); | ||||
| editPeerUsernameMargins: margins(23px, 17px, 23px, 2px); | ||||
| editPeerUsernameGood: FlatLabel(defaultFlatLabel) { | ||||
| 	textFg: boxTextFgGood; | ||||
| 	style: boxTextStyle; | ||||
|  | @ -682,12 +692,12 @@ editPeerUsernameGood: FlatLabel(defaultFlatLabel) { | |||
| editPeerUsernameError: FlatLabel(editPeerUsernameGood) { | ||||
| 	textFg: boxTextFgError; | ||||
| } | ||||
| editPeerUsernamePosition: point(35px, 3px); | ||||
| editPeerUsernamePosition: point(23px, 18px); | ||||
| editPeerInviteLinkSkip: 10px; | ||||
| editPeerInviteLinkMargins: margins(15px, 10px, 14px, 16px); | ||||
| editPeerInvitesMargins: margins(23px, 10px, 23px, 16px); | ||||
| editPeerInvitesMargins: margins(23px, 17px, 23px, 16px); | ||||
| editPeerInvitesTopSkip: 10px; | ||||
| editPeerInvitesSkip: 10px; | ||||
| editPeerInviteLinkBoxBottomSkip: 15px; | ||||
| 
 | ||||
| historyTopBarBack: IconButton(infoTopBarBack) { | ||||
| 	width: 52px; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue