Improve main menu bots disclaimer acceptance.
This commit is contained in:
		
							parent
							
								
									229f7a2c15
								
							
						
					
					
						commit
						ef969df86e
					
				
					 8 changed files with 205 additions and 121 deletions
				
			
		|  | @ -2268,6 +2268,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | |||
| "lng_bot_reload_page" = "Reload Page"; | ||||
| "lng_bot_add_to_menu" = "{bot} asks your permission to be added as an option to your attachments menu so you can access it from any chat."; | ||||
| "lng_bot_add_to_menu_done" = "Bot added to the menu."; | ||||
| "lng_bot_will_be_added" = "{bot} shortcuts will be added to the attachment options and the main menu."; | ||||
| "lng_bot_side_menu_new" = "NEW"; | ||||
| "lng_bot_menu_not_supported" = "This bot isn't supported in the attach menu."; | ||||
| "lng_bot_menu_already_added" = "This bot is already added in your attach menu."; | ||||
| "lng_bot_menu_button" = "Menu"; | ||||
|  |  | |||
|  | @ -24,4 +24,5 @@ struct BotAppData { | |||
| 
 | ||||
| 	uint64 accessHash = 0; | ||||
| 	uint64 hash = 0; | ||||
| 	bool hasSettings = false; | ||||
| }; | ||||
|  |  | |||
|  | @ -124,6 +124,10 @@ constexpr auto kRefreshBotsTimeout = 60 * 60 * crl::time(1000); | |||
| 	if (result && result->icon) { | ||||
| 		result->icon->forceToCache(true); | ||||
| 	} | ||||
| 	if (const auto icon = result->icon) { | ||||
| 		result->media = icon->createMediaView(); | ||||
| 		icon->save(Data::FileOrigin(), {}); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
|  | @ -194,6 +198,78 @@ void ShowChooseBox( | |||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| void FillDisclaimerBox(not_null<Ui::GenericBox*> box, Fn<void()> done) { | ||||
| 	const auto updateCheck = std::make_shared<Fn<void()>>(); | ||||
| 	const auto validateCheck = std::make_shared<Fn<bool()>>(); | ||||
| 
 | ||||
| 	const auto callback = [=](Fn<void()> close) { | ||||
| 		if (validateCheck && (*validateCheck)()) { | ||||
| 			done(); | ||||
| 			close(); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const auto padding = st::boxRowPadding; | ||||
| 	Ui::ConfirmBox(box, { | ||||
| 		.text = tr::lng_mini_apps_disclaimer_text( | ||||
| 			tr::now, | ||||
| 			Ui::Text::RichLangValue), | ||||
| 		.confirmed = callback, | ||||
| 		.confirmText = tr::lng_box_ok(), | ||||
| 		.labelPadding = QMargins(padding.left(), 0, padding.right(), 0), | ||||
| 		.title = tr::lng_mini_apps_disclaimer_title(), | ||||
| 	}); | ||||
| 
 | ||||
| 	auto checkView = std::make_unique<Ui::CheckView>( | ||||
| 		st::defaultCheck, | ||||
| 		false, | ||||
| 		[=] { if (*updateCheck) { (*updateCheck)(); } }); | ||||
| 	const auto check = checkView.get(); | ||||
| 	const auto row = box->addRow( | ||||
| 		object_ptr<Ui::Checkbox>( | ||||
| 			box.get(), | ||||
| 			tr::lng_mini_apps_disclaimer_button( | ||||
| 				lt_link, | ||||
| 				rpl::single(Ui::Text::Link( | ||||
| 					tr::lng_mini_apps_disclaimer_link(tr::now), | ||||
| 					tr::lng_mini_apps_tos_url(tr::now))), | ||||
| 				Ui::Text::WithEntities), | ||||
| 			st::defaultBoxCheckbox, | ||||
| 			std::move(checkView)), | ||||
| 		{ | ||||
| 			st::boxRowPadding.left(), | ||||
| 			st::boxRowPadding.left(), | ||||
| 			st::boxRowPadding.right(), | ||||
| 			0, | ||||
| 		}); | ||||
| 	row->setAllowTextLines(5); | ||||
| 	row->setClickHandlerFilter([=]( | ||||
| 			const ClickHandlerPtr &link, | ||||
| 			Qt::MouseButton button) { | ||||
| 		ActivateClickHandler(row, link, ClickContext{ | ||||
| 			.button = button, | ||||
| 			.other = QVariant::fromValue(ClickHandlerContext{ | ||||
| 				.show = box->uiShow(), | ||||
| 			}) | ||||
| 		}); | ||||
| 		return false; | ||||
| 	}); | ||||
| 
 | ||||
| 	(*updateCheck) = [=] { row->update(); }; | ||||
| 
 | ||||
| 	const auto showError = Ui::CheckView::PrepareNonToggledError( | ||||
| 		check, | ||||
| 		box->lifetime()); | ||||
| 
 | ||||
| 	(*validateCheck) = [=] { | ||||
| 		if (check->checked()) { | ||||
| 			return true; | ||||
| 		} | ||||
| 		showError(); | ||||
| 		return false; | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| class BotAction final : public Ui::Menu::ItemBase { | ||||
| public: | ||||
| 	BotAction( | ||||
|  | @ -818,10 +894,6 @@ void AttachWebView::requestBots() { | |||
| 			_attachBots.reserve(data.vbots().v.size()); | ||||
| 			for (const auto &bot : data.vbots().v) { | ||||
| 				if (auto parsed = ParseAttachBot(_session, bot)) { | ||||
| 					if (const auto icon = parsed->icon) { | ||||
| 						parsed->media = icon->createMediaView(); | ||||
| 						icon->save(Data::FileOrigin(), {}); | ||||
| 					} | ||||
| 					_attachBots.push_back(std::move(*parsed)); | ||||
| 				} | ||||
| 			} | ||||
|  | @ -832,6 +904,11 @@ void AttachWebView::requestBots() { | |||
| 	}).send(); | ||||
| } | ||||
| 
 | ||||
| bool AttachWebView::showingDisclaimer(const AttachWebViewBot &bot) const { | ||||
| 	return bot.disclaimerRequired | ||||
| 		&& !_disclaimerAccepted.contains(bot.user); | ||||
| } | ||||
| 
 | ||||
| void AttachWebView::requestAddToMenu( | ||||
| 		not_null<UserData*> bot, | ||||
| 		std::optional<QString> startCommand) { | ||||
|  | @ -885,9 +962,7 @@ void AttachWebView::requestAddToMenu( | |||
| 				} | ||||
| 			} else if (!startCommand) { | ||||
| 				_bot = bot; | ||||
| 				acceptDisclaimer(strong, [=] { | ||||
| 				requestSimple(strong, bot, { .fromMainMenu = true }); | ||||
| 				}); | ||||
| 				return true; | ||||
| 			} else if (const auto useTypes = chooseTypes & types) { | ||||
| 				const auto done = [=](not_null<Data::Thread*> thread) { | ||||
|  | @ -1128,6 +1203,7 @@ void AttachWebView::requestApp( | |||
| 			_bot->id, | ||||
| 			data.vapp()); | ||||
| 		_app = received ? received : already; | ||||
| 		_app->hasSettings = data.is_has_settings(); | ||||
| 		if (!_app) { | ||||
| 			cancel(); | ||||
| 			showToast(tr::lng_username_app_not_found(tr::now)); | ||||
|  | @ -1140,8 +1216,8 @@ void AttachWebView::requestApp( | |||
| 			requestAppView(false); | ||||
| 		} | ||||
| 	}).fail([=] { | ||||
| 		cancel(); | ||||
| 		showToast(tr::lng_username_app_not_found(tr::now)); | ||||
| 		cancel(); | ||||
| 	}).send(); | ||||
| } | ||||
| 
 | ||||
|  | @ -1190,13 +1266,14 @@ void AttachWebView::requestAppView(bool allowWrite) { | |||
| 		return; | ||||
| 	} | ||||
| 	using Flag = MTPmessages_RequestAppWebView::Flag; | ||||
| 	const auto app = _app; | ||||
| 	const auto flags = Flag::f_theme_params | ||||
| 		| (_startCommand.isEmpty() ? Flag(0) : Flag::f_start_param) | ||||
| 		| (allowWrite ? Flag::f_write_allowed : Flag(0)); | ||||
| 	_requestId = _session->api().request(MTPmessages_RequestAppWebView( | ||||
| 		MTP_flags(flags), | ||||
| 		_context->action.history->peer->input, | ||||
| 		MTP_inputBotAppID(MTP_long(_app->id), MTP_long(_app->accessHash)), | ||||
| 		MTP_inputBotAppID(MTP_long(app->id), MTP_long(app->accessHash)), | ||||
| 		MTP_string(_startCommand), | ||||
| 		MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json)), | ||||
| 		MTP_string("tdesktop") | ||||
|  | @ -1204,7 +1281,7 @@ void AttachWebView::requestAppView(bool allowWrite) { | |||
| 		_requestId = 0; | ||||
| 		const auto &data = result.data(); | ||||
| 		const auto queryId = uint64(); | ||||
| 		show(queryId, qs(data.vurl())); | ||||
| 		show(queryId, qs(data.vurl()), QString(), false, app); | ||||
| 	}).fail([=](const MTP::Error &error) { | ||||
| 		_requestId = 0; | ||||
| 		if (error.type() == u"BOT_INVALID"_q) { | ||||
|  | @ -1256,93 +1333,17 @@ void AttachWebView::acceptDisclaimer( | |||
| 	} else if (i->inactive) { | ||||
| 		requestAddToMenu(_bot, {}, controller, {}, {}); | ||||
| 		return; | ||||
| 	} else if (!i->disclaimerRequired) { | ||||
| 	} else if (!showingDisclaimer(*i)) { | ||||
| 		done(); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	const auto weak = base::make_weak(this); | ||||
| 	controller->show(Box([=](not_null<Ui::GenericBox*> box) { | ||||
| 		const auto updateCheck = std::make_shared<Fn<void()>>(); | ||||
| 		const auto validateCheck = std::make_shared<Fn<bool()>>(); | ||||
| 
 | ||||
| 		const auto callback = [=](Fn<void()> close) { | ||||
| 			if (validateCheck && (*validateCheck)() && weak) { | ||||
| 				const auto i = ranges::find( | ||||
| 					_attachBots, | ||||
| 					not_null(_bot), | ||||
| 					&AttachWebViewBot::user); | ||||
| 				if (i == end(_attachBots)) { | ||||
| 	controller->show(Box(FillDisclaimerBox, crl::guard(this, [=] { | ||||
| 		_disclaimerAccepted.emplace(_bot); | ||||
| 		_attachBotsUpdates.fire({}); | ||||
| 				} else if (i->inactive) { | ||||
| 					requestAddToMenu(_bot, std::nullopt); | ||||
| 				} else { | ||||
| 					i->disclaimerRequired = false; | ||||
| 					requestBots(); | ||||
| 		done(); | ||||
| 				} | ||||
| 				close(); | ||||
| 			} | ||||
| 		}; | ||||
| 
 | ||||
| 		Ui::ConfirmBox(box, { | ||||
| 			.text = tr::lng_mini_apps_disclaimer_text( | ||||
| 				tr::now, | ||||
| 				Ui::Text::RichLangValue), | ||||
| 			.confirmed = callback, | ||||
| 			.confirmText = tr::lng_box_ok(), | ||||
| 			.title = tr::lng_mini_apps_disclaimer_title(), | ||||
| 		}); | ||||
| 
 | ||||
| 		auto checkView = std::make_unique<Ui::CheckView>( | ||||
| 			st::defaultCheck, | ||||
| 			false, | ||||
| 			[=] { if (*updateCheck) { (*updateCheck)(); } }); | ||||
| 		const auto check = checkView.get(); | ||||
| 		const auto row = box->addRow( | ||||
| 			object_ptr<Ui::Checkbox>( | ||||
| 				box.get(), | ||||
| 				tr::lng_mini_apps_disclaimer_button( | ||||
| 					lt_link, | ||||
| 					rpl::single(Ui::Text::Link( | ||||
| 						tr::lng_mini_apps_disclaimer_link(tr::now), | ||||
| 						tr::lng_mini_apps_tos_url(tr::now))), | ||||
| 					Ui::Text::WithEntities), | ||||
| 				st::defaultBoxCheckbox, | ||||
| 				std::move(checkView)), | ||||
| 			{ | ||||
| 				st::boxRowPadding.left(), | ||||
| 				st::boxRowPadding.left(), | ||||
| 				st::boxRowPadding.right(), | ||||
| 				st::defaultBoxCheckbox.margin.bottom(), | ||||
| 			}); | ||||
| 		row->setAllowTextLines(5); | ||||
| 		row->setClickHandlerFilter([=]( | ||||
| 				const ClickHandlerPtr &link, | ||||
| 				Qt::MouseButton button) { | ||||
| 			ActivateClickHandler(row, link, ClickContext{ | ||||
| 				.button = button, | ||||
| 				.other = QVariant::fromValue(ClickHandlerContext{ | ||||
| 					.show = box->uiShow(), | ||||
| 				}) | ||||
| 			}); | ||||
| 			return false; | ||||
| 		}); | ||||
| 
 | ||||
| 		(*updateCheck) = [=] { row->update(); }; | ||||
| 
 | ||||
| 		const auto showError = Ui::CheckView::PrepareNonToggledError( | ||||
| 			check, | ||||
| 			box->lifetime()); | ||||
| 
 | ||||
| 		(*validateCheck) = [=] { | ||||
| 			if (check->checked()) { | ||||
| 				return true; | ||||
| 			} | ||||
| 			showError(); | ||||
| 			return false; | ||||
| 		}; | ||||
| 	})); | ||||
| 	}))); | ||||
| } | ||||
| 
 | ||||
| void AttachWebView::ClearAll() { | ||||
|  | @ -1355,7 +1356,8 @@ void AttachWebView::show( | |||
| 		uint64 queryId, | ||||
| 		const QString &url, | ||||
| 		const QString &buttonText, | ||||
| 		bool allowClipboardRead) { | ||||
| 		bool allowClipboardRead, | ||||
| 		const BotAppData *app) { | ||||
| 	Expects(_bot != nullptr && _context != nullptr); | ||||
| 
 | ||||
| 	auto title = Info::Profile::NameValue(_bot); | ||||
|  | @ -1366,12 +1368,15 @@ void AttachWebView::show( | |||
| 		_attachBots, | ||||
| 		not_null{ _bot }, | ||||
| 		&AttachWebViewBot::user); | ||||
| 	const auto hasSettings = (attached != end(_attachBots)) | ||||
| 	const auto hasSettings = app | ||||
| 		? app->hasSettings | ||||
| 		: ((attached != end(_attachBots)) | ||||
| 			&& !attached->inactive | ||||
| 		&& attached->hasSettings; | ||||
| 			&& attached->hasSettings); | ||||
| 	const auto hasOpenBot = !_context | ||||
| 		|| (_bot != _context->action.history->peer); | ||||
| 	const auto hasRemoveFromMenu = (attached != end(_attachBots)) | ||||
| 	const auto hasRemoveFromMenu = !app | ||||
| 		&& (attached != end(_attachBots)) | ||||
| 		&& (!attached->inactive || attached->inMainMenu); | ||||
| 	const auto buttons = (hasSettings ? Button::Settings : Button::None) | ||||
| 		| (hasOpenBot ? Button::OpenBot : Button::None) | ||||
|  | @ -1473,6 +1478,14 @@ void AttachWebView::confirmAddToMenu( | |||
| 			}); | ||||
| 			close(); | ||||
| 		}; | ||||
| 		const auto disclaimer = showingDisclaimer(bot); | ||||
| 		if (disclaimer) { | ||||
| 			FillDisclaimerBox(box, [=] { | ||||
| 				_disclaimerAccepted.emplace(bot.user); | ||||
| 				_attachBotsUpdates.fire({}); | ||||
| 				done([] {}); | ||||
| 			}); | ||||
| 		} else { | ||||
| 			Ui::ConfirmBox(box, { | ||||
| 				(bot.inMainMenu | ||||
| 					? tr::lng_bot_add_to_side_menu | ||||
|  | @ -1483,6 +1496,7 @@ void AttachWebView::confirmAddToMenu( | |||
| 						Ui::Text::WithEntities), | ||||
| 				done, | ||||
| 			}); | ||||
| 		} | ||||
| 		if (bot.requestWriteAccess) { | ||||
| 			(*allowed) = box->addRow( | ||||
| 				object_ptr<Ui::Checkbox>( | ||||
|  | @ -1496,11 +1510,27 @@ void AttachWebView::confirmAddToMenu( | |||
| 					st::urlAuthCheckbox), | ||||
| 				style::margins( | ||||
| 					st::boxRowPadding.left(), | ||||
| 					st::boxPhotoCaptionSkip, | ||||
| 					(disclaimer | ||||
| 						? st::boxPhotoCaptionSkip | ||||
| 						: st::boxRowPadding.left()), | ||||
| 					st::boxRowPadding.right(), | ||||
| 					st::boxPhotoCaptionSkip)); | ||||
| 					st::boxRowPadding.left())); | ||||
| 			(*allowed)->setAllowTextLines(); | ||||
| 		} | ||||
| 		if (disclaimer) { | ||||
| 			if (!bot.requestWriteAccess) { | ||||
| 				box->addRow(object_ptr<Ui::FixedHeightWidget>( | ||||
| 					box, | ||||
| 					st::boxRowPadding.left())); | ||||
| 			} | ||||
| 			box->addRow(object_ptr<Ui::FlatLabel>( | ||||
| 				box, | ||||
| 				tr::lng_bot_will_be_added( | ||||
| 					lt_bot, | ||||
| 					rpl::single(Ui::Text::Bold(bot.name)), | ||||
| 					Ui::Text::WithEntities), | ||||
| 				st::boxLabel)); | ||||
| 		} | ||||
| 	})); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -119,6 +119,7 @@ public: | |||
| 	[[nodiscard]] rpl::producer<> attachBotsUpdates() const { | ||||
| 		return _attachBotsUpdates.events(); | ||||
| 	} | ||||
| 	[[nodiscard]] bool showingDisclaimer(const AttachWebViewBot &bot) const; | ||||
| 
 | ||||
| 	void requestAddToMenu( | ||||
| 		not_null<UserData*> bot, | ||||
|  | @ -195,7 +196,8 @@ private: | |||
| 		uint64 queryId, | ||||
| 		const QString &url, | ||||
| 		const QString &buttonText = QString(), | ||||
| 		bool allowClipboardRead = false); | ||||
| 		bool allowClipboardRead = false, | ||||
| 		const BotAppData *app = nullptr); | ||||
| 	void confirmAddToMenu( | ||||
| 		AttachWebViewBot bot, | ||||
| 		Fn<void()> callback = nullptr); | ||||
|  | @ -238,6 +240,7 @@ private: | |||
| 
 | ||||
| 	std::vector<AttachWebViewBot> _attachBots; | ||||
| 	rpl::event_stream<> _attachBotsUpdates; | ||||
| 	base::flat_set<not_null<UserData*>> _disclaimerAccepted; | ||||
| 
 | ||||
| 	std::unique_ptr<Ui::BotWebView::Panel> _panel; | ||||
| 
 | ||||
|  |  | |||
|  | @ -24,7 +24,9 @@ void ConfirmBox(not_null<Ui::GenericBox*> box, ConfirmBoxArgs &&args) { | |||
| 
 | ||||
| 	if (!v::is_null(args.text)) { | ||||
| 		const auto padding = st::boxPadding; | ||||
| 		const auto use = withTitle | ||||
| 		const auto use = args.labelPadding | ||||
| 			? *args.labelPadding | ||||
| 			: withTitle | ||||
| 			? QMargins(padding.left(), 0, padding.right(), padding.bottom()) | ||||
| 			: padding; | ||||
| 		const auto label = box->addRow( | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ struct ConfirmBoxArgs { | |||
| 
 | ||||
| 	const style::FlatLabel *labelStyle = nullptr; | ||||
| 	Fn<bool(const ClickHandlerPtr&, Qt::MouseButton)> labelFilter; | ||||
| 	std::optional<QMargins> labelPadding; | ||||
| 
 | ||||
| 	v::text::data title = v::null; | ||||
| 
 | ||||
|  |  | |||
|  | @ -561,10 +561,6 @@ bool Panel::createWebview(const Webview::ThemeParams ¶ms) { | |||
| 	_widget->showInner(std::move(outer)); | ||||
| 	_webviewParent = container; | ||||
| 
 | ||||
| 	container->paintRequest() | rpl::start_with_next([=] { | ||||
| 		QPainter(container).fillRect(container->rect(), QColor(0, 128, 0, 255)); | ||||
| 	}, container->lifetime()); | ||||
| 
 | ||||
| 	_webviewBottom = std::make_unique<RpWidget>(_widget.get()); | ||||
| 	const auto bottom = _webviewBottom.get(); | ||||
| 	bottom->show(); | ||||
|  |  | |||
|  | @ -211,15 +211,19 @@ void SetupMenuBots( | |||
| 	) | rpl::then( | ||||
| 		bots->attachBotsUpdates() | ||||
| 	) | rpl::start_with_next([=] { | ||||
| 		const auto width = wrap->widthNoMargins(); | ||||
| 		wrap->clear(); | ||||
| 		for (const auto &bot : bots->attachBots()) { | ||||
| 			if (!bot.inMainMenu) { | ||||
| 				continue; | ||||
| 			} | ||||
| 			const auto button = Settings::AddButton( | ||||
| 				container, | ||||
| 				wrap, | ||||
| 				rpl::single(bot.name), | ||||
| 				st::mainMenuButton); | ||||
| 			const auto menu = button->lifetime().make_state< | ||||
| 				base::unique_qptr<Ui::PopupMenu> | ||||
| 			>(); | ||||
| 			const auto icon = Ui::CreateChild<InlineBots::MenuBotIcon>( | ||||
| 				button.get(), | ||||
| 				bot.media); | ||||
|  | @ -229,12 +233,57 @@ void SetupMenuBots( | |||
| 					st::mainMenuButton.iconLeft, | ||||
| 					(height - icon->height()) / 2); | ||||
| 			}, button->lifetime()); | ||||
| 			button->setClickedCallback([=] { | ||||
| 				bots->requestSimple(controller, bot.user, { | ||||
| 			const auto user = bot.user; | ||||
| 			button->setAcceptBoth(true); | ||||
| 			button->clicks( | ||||
| 			) | rpl::start_with_next([=](Qt::MouseButton which) { | ||||
| 				if (which == Qt::LeftButton) { | ||||
| 					bots->requestSimple(controller, user, { | ||||
| 						.fromMainMenu = true, | ||||
| 					}); | ||||
| 			}); | ||||
| 				} else { | ||||
| 					(*menu) = nullptr; | ||||
| 					(*menu) = base::make_unique_q<Ui::PopupMenu>( | ||||
| 						button, | ||||
| 						st::popupMenuWithIcons); | ||||
| 					(*menu)->addAction( | ||||
| 						tr::lng_bot_remove_from_menu(tr::now), | ||||
| 						[=] { bots->removeFromMenu(user); }, | ||||
| 						&st::menuIconDelete); | ||||
| 					(*menu)->popup(QCursor::pos()); | ||||
| 				} | ||||
| 			}, button->lifetime()); | ||||
| 
 | ||||
| 			const auto badge = bots->showingDisclaimer(bot) | ||||
| 				? Ui::CreateChild<Ui::PaddingWrap<Ui::FlatLabel>>( | ||||
| 					button.get(), | ||||
| 					object_ptr<Ui::FlatLabel>( | ||||
| 						button, | ||||
| 						tr::lng_bot_side_menu_new(), | ||||
| 						st::settingsPremiumNewBadge), | ||||
| 					st::settingsPremiumNewBadgePadding) | ||||
| 				: nullptr; | ||||
| 			if (badge) { | ||||
| 				badge->setAttribute(Qt::WA_TransparentForMouseEvents); | ||||
| 				badge->paintRequest() | rpl::start_with_next([=] { | ||||
| 					auto p = QPainter(badge); | ||||
| 					auto hq = PainterHighQualityEnabler(p); | ||||
| 					p.setPen(Qt::NoPen); | ||||
| 					p.setBrush(st::windowBgActive); | ||||
| 					const auto r = st::settingsPremiumNewBadgePadding.left(); | ||||
| 					p.drawRoundedRect(badge->rect(), r, r); | ||||
| 				}, badge->lifetime()); | ||||
| 
 | ||||
| 				button->sizeValue( | ||||
| 				) | rpl::start_with_next([=](QSize size) { | ||||
| 					badge->moveToRight( | ||||
| 						st::mainMenuButton.padding.right(), | ||||
| 						(size.height() - badge->height()) / 2, | ||||
| 						size.width()); | ||||
| 				}, badge->lifetime()); | ||||
| 			} | ||||
| 		} | ||||
| 		wrap->resizeToWidth(width); | ||||
| 	}, wrap->lifetime()); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 John Preston
						John Preston