From 7bf78b3317f919a7540a199526c9f9d5f67d96a9 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Mon, 11 Nov 2024 19:08:06 +0400
Subject: [PATCH] Use initial bot web app header/body colors.

---
 Telegram/SourceFiles/data/data_user.cpp       |  18 +++
 Telegram/SourceFiles/data/data_user.h         |   5 +
 .../inline_bots/bot_attach_web_view.cpp       |  18 ++-
 .../payments/ui/payments_panel.cpp            |   4 +-
 .../ui/chat/attach/attach_bot_webview.cpp     | 111 +++++++++++-------
 .../ui/chat/attach/attach_bot_webview.h       |  53 +++++----
 .../window/themes/window_theme.cpp            |   3 +-
 Telegram/lib_webview                          |   2 +-
 8 files changed, 142 insertions(+), 72 deletions(-)

diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp
index 7433dbd9a..c8e5466b9 100644
--- a/Telegram/SourceFiles/data/data_user.cpp
+++ b/Telegram/SourceFiles/data/data_user.cpp
@@ -345,6 +345,24 @@ void UserData::setBotInfo(const MTPBotInfo &info) {
 		const auto privacyChanged = (botInfo->privacyPolicyUrl != privacy);
 		botInfo->privacyPolicyUrl = privacy;
 
+		if (const auto settings = d.vapp_settings()) {
+			const auto &data = settings->data();
+			botInfo->botAppColorTitleDay = Ui::MaybeColorFromSerialized(
+				data.vheader_color()).value_or(QColor(0, 0, 0, 0));
+			botInfo->botAppColorTitleNight = Ui::MaybeColorFromSerialized(
+				data.vheader_dark_color()).value_or(QColor(0, 0, 0, 0));
+			botInfo->botAppColorBodyDay = Ui::MaybeColorFromSerialized(
+				data.vbackground_color()).value_or(QColor(0, 0, 0, 0));
+			botInfo->botAppColorBodyNight = Ui::MaybeColorFromSerialized(
+				data.vbackground_dark_color()).value_or(QColor(0, 0, 0, 0));
+		} else {
+			botInfo->botAppColorTitleDay
+				= botInfo->botAppColorTitleNight
+				= botInfo->botAppColorBodyDay
+				= botInfo->botAppColorBodyNight
+				= QColor(0, 0, 0, 0);
+		}
+
 		if (changedCommands || changedButton || privacyChanged) {
 			owner().botCommandsChanged(this);
 		}
diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h
index 8dfc2b4f7..63f03549a 100644
--- a/Telegram/SourceFiles/data/data_user.h
+++ b/Telegram/SourceFiles/data/data_user.h
@@ -33,6 +33,11 @@ struct BotInfo {
 	QString botMenuButtonUrl;
 	QString privacyPolicyUrl;
 
+	QColor botAppColorTitleDay = QColor(0, 0, 0, 0);
+	QColor botAppColorTitleNight = QColor(0, 0, 0, 0);
+	QColor botAppColorBodyDay = QColor(0, 0, 0, 0);
+	QColor botAppColorBodyNight = QColor(0, 0, 0, 0);
+
 	QString startToken;
 	Dialogs::EntryState inlineReturnTo;
 
diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
index 17983983f..93fa4d389 100644
--- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
+++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
@@ -1423,7 +1423,23 @@ void WebViewInstance::started(uint64 queryId) {
 }
 
 Webview::ThemeParams WebViewInstance::botThemeParams() {
-	return Window::Theme::WebViewParams();
+	auto result = Window::Theme::WebViewParams();
+	if (const auto info = _bot->botInfo.get()) {
+		const auto night = Window::Theme::IsNightMode();
+		const auto &title = night
+			? info->botAppColorTitleNight
+			: info->botAppColorTitleDay;
+		const auto &body = night
+			? info->botAppColorBodyNight
+			: info->botAppColorBodyDay;
+		if (title.alpha() == 255) {
+			result.titleBg = title;
+		}
+		if (body.alpha() == 255) {
+			result.bodyBg = body;
+		}
+	}
+	return result;
 }
 
 bool WebViewInstance::botHandleLocalUri(QString uri, bool keepOpen) {
diff --git a/Telegram/SourceFiles/payments/ui/payments_panel.cpp b/Telegram/SourceFiles/payments/ui/payments_panel.cpp
index 2bc023e8c..b0e1bcc0c 100644
--- a/Telegram/SourceFiles/payments/ui/payments_panel.cpp
+++ b/Telegram/SourceFiles/payments/ui/payments_panel.cpp
@@ -550,7 +550,7 @@ bool Panel::createWebview(const Webview::ThemeParams &params) {
 	_webview = std::make_unique<WebviewWithLifetime>(
 		container,
 		Webview::WindowConfig{
-			.opaqueBg = params.opaqueBg,
+			.opaqueBg = params.bodyBg,
 			.storageId = _delegate->panelWebviewStorageId(),
 		});
 
@@ -919,7 +919,7 @@ void Panel::updateThemeParams(const Webview::ThemeParams &params) {
 		return;
 	}
 	_webview->window.updateTheme(
-		params.opaqueBg,
+		params.bodyBg,
 		params.scrollBg,
 		params.scrollBgOver,
 		params.scrollBarBg,
diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp
index 9da6c01eb..7158f1e9f 100644
--- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp
+++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp
@@ -366,23 +366,19 @@ Panel::Progress::Progress(QWidget *parent, Fn<QRect()> rect)
 	st::paymentsLoading) {
 }
 
-Panel::Panel(
-	const Webview::StorageId &storageId,
-	rpl::producer<QString> title,
-	object_ptr<Ui::RpWidget> titleBadge,
-	not_null<Delegate*> delegate,
-	MenuButtons menuButtons,
-	bool fullscreen,
-	bool allowClipboardRead)
-: _storageId(storageId)
-, _delegate(delegate)
-, _menuButtons(menuButtons)
+Panel::Panel(Args &&args)
+: _storageId(args.storageId)
+, _delegate(args.delegate)
+, _menuButtons(args.menuButtons)
 , _widget(std::make_unique<SeparatePanel>())
-, _fullscreen(fullscreen)
-, _allowClipboardRead(allowClipboardRead) {
+, _fullscreen(args.fullscreen)
+, _allowClipboardRead(args.allowClipboardRead) {
 	_widget->setWindowFlag(Qt::WindowStaysOnTopHint, false);
 	_widget->setInnerSize(st::botWebViewPanelSize, true);
 
+	const auto params = _delegate->botThemeParams();
+	updateColorOverrides(params);
+
 	_fullscreen.value(
 	) | rpl::start_with_next([=](bool fullscreen) {
 		_widget->toggleFullScreen(fullscreen);
@@ -426,8 +422,17 @@ Panel::Panel(
 		});
 	}, _widget->lifetime());
 
-	setTitle(std::move(title));
-	_widget->setTitleBadge(std::move(titleBadge));
+	setTitle(std::move(args.title));
+	_widget->setTitleBadge(std::move(args.titleBadge));
+
+	if (!showWebview(args.url, params, std::move(args.bottom))) {
+		const auto available = Webview::Availability();
+		if (available.error != Webview::Available::Error::None) {
+			showWebviewError(tr::lng_bot_no_webview(tr::now), available);
+		} else {
+			showCriticalError({ "Error: Could not initialize WebView." });
+		}
+	}
 }
 
 Panel::~Panel() {
@@ -679,13 +684,17 @@ bool Panel::createWebview(const Webview::ThemeParams &params) {
 	_widget->showInner(std::move(outer));
 	_webviewParent = container;
 
+	_headerColorReceived = false;
+	_bodyColorReceived = false;
+	_bottomColorReceived = false;
+	updateColorOverrides(params);
 	createWebviewBottom();
 
 	container->show();
 	_webview = std::make_unique<WebviewWithLifetime>(
 		container,
 		Webview::WindowConfig{
-			.opaqueBg = params.opaqueBg,
+			.opaqueBg = params.bodyBg,
 			.storageId = _storageId,
 		});
 	const auto raw = &_webview->window;
@@ -823,6 +832,8 @@ bool Panel::createWebview(const Webview::ThemeParams &params) {
 			requestClipboardText(arguments);
 		} else if (command == "web_app_set_header_color") {
 			processHeaderColor(arguments);
+		} else if (command == "web_app_set_background_color") {
+			processBackgroundColor(arguments);
 		} else if (command == "web_app_set_bottom_bar_color") {
 			processBottomBarColor(arguments);
 		} else if (command == "web_app_send_prepared_message") {
@@ -1437,6 +1448,7 @@ void Panel::processSettingsButtonMessage(const QJsonObject &args) {
 }
 
 void Panel::processHeaderColor(const QJsonObject &args) {
+	_headerColorReceived = true;
 	if (const auto color = ParseColor(args["color"].toString())) {
 		_widget->overrideTitleColor(color);
 		_headerColorLifetime.destroy();
@@ -1453,7 +1465,32 @@ void Panel::processHeaderColor(const QJsonObject &args) {
 	}
 }
 
+void Panel::processBackgroundColor(const QJsonObject &args) {
+	_bodyColorReceived = true;
+	if (const auto color = ParseColor(args["color"].toString())) {
+		_widget->overrideBodyColor(color);
+		_bodyColorLifetime.destroy();
+	} else if (const auto color = LookupNamedColor(
+			args["color_key"].toString())) {
+		_widget->overrideBodyColor((*color)->c);
+		_bodyColorLifetime = style::PaletteChanged(
+		) | rpl::start_with_next([=] {
+			_widget->overrideBodyColor((*color)->c);
+		});
+	} else {
+		_widget->overrideBodyColor(std::nullopt);
+		_bodyColorLifetime.destroy();
+	}
+	if (const auto raw = _bottomButtonsBg.get()) {
+		raw->update();
+	}
+	if (const auto raw = _webviewBottom.get()) {
+		raw->update();
+	}
+}
+
 void Panel::processBottomBarColor(const QJsonObject &args) {
+	_bottomColorReceived = true;
 	if (const auto color = ParseColor(args["color"].toString())) {
 		_widget->overrideBottomBarColor(color);
 		_bottomBarColor = color;
@@ -1462,7 +1499,7 @@ void Panel::processBottomBarColor(const QJsonObject &args) {
 			args["color_key"].toString())) {
 		_widget->overrideBottomBarColor((*color)->c);
 		_bottomBarColor = (*color)->c;
-		_headerColorLifetime = style::PaletteChanged(
+		_bottomBarColorLifetime = style::PaletteChanged(
 		) | rpl::start_with_next([=] {
 			_widget->overrideBottomBarColor((*color)->c);
 			_bottomBarColor = (*color)->c;
@@ -1470,7 +1507,7 @@ void Panel::processBottomBarColor(const QJsonObject &args) {
 	} else {
 		_widget->overrideBottomBarColor(std::nullopt);
 		_bottomBarColor = std::nullopt;
-		_headerColorLifetime.destroy();
+		_bottomBarColorLifetime.destroy();
 	}
 	if (const auto raw = _bottomButtonsBg.get()) {
 		raw->update();
@@ -1596,13 +1633,15 @@ void Panel::layoutButtons() {
 	} else if (_bottomButtonsBg) {
 		_bottomButtonsBg->hide();
 	}
-	_footerHeight = _layerShown
+	const auto footer = _layerShown
 		? 0
 		: any
 		? _bottomButtonsBg->height()
 		: _fullscreen.current()
 		? 0
 		: _webviewBottom->height();
+	_widget->setBottomBarHeight((!_layerShown && any) ? footer : 0);
+	_footerHeight = footer;
 }
 
 void Panel::showBox(object_ptr<BoxContent> box) {
@@ -1707,11 +1746,12 @@ void Panel::showCriticalError(const TextWithEntities &text) {
 }
 
 void Panel::updateThemeParams(const Webview::ThemeParams &params) {
+	updateColorOverrides(params);
 	if (!_webview || !_webview->window.widget()) {
 		return;
 	}
 	_webview->window.updateTheme(
-		params.opaqueBg,
+		params.bodyBg,
 		params.scrollBg,
 		params.scrollBgOver,
 		params.scrollBarBg,
@@ -1719,6 +1759,15 @@ void Panel::updateThemeParams(const Webview::ThemeParams &params) {
 	postEvent("theme_changed", "{\"theme_params\": " + params.json + "}");
 }
 
+void Panel::updateColorOverrides(const Webview::ThemeParams &params) {
+	if (!_headerColorReceived && params.titleBg.alpha() == 255) {
+		_widget->overrideTitleColor(params.titleBg);
+	}
+	if (!_bodyColorReceived && params.bodyBg.alpha() == 255) {
+		_widget->overrideBodyColor(params.bodyBg);
+	}
+}
+
 void Panel::invoiceClosed(const QString &slug, const QString &status) {
 	if (!_webview || !_webview->window.widget()) {
 		return;
@@ -1802,27 +1851,7 @@ rpl::lifetime &Panel::lifetime() {
 }
 
 std::unique_ptr<Panel> Show(Args &&args) {
-	auto result = std::make_unique<Panel>(
-		args.storageId,
-		std::move(args.title),
-		std::move(args.titleBadge),
-		args.delegate,
-		args.menuButtons,
-		args.fullscreen,
-		args.allowClipboardRead);
-	const auto params = args.delegate->botThemeParams();
-	if (!result->showWebview(args.url, params, std::move(args.bottom))) {
-		const auto available = Webview::Availability();
-		if (available.error != Webview::Available::Error::None) {
-			result->showWebviewError(
-				tr::lng_bot_no_webview(tr::now),
-				available);
-		} else {
-			result->showCriticalError({
-				"Error: Could not initialize WebView." });
-		}
-	}
-	return result;
+	return std::make_unique<Panel>(std::move(args));
 }
 
 } // namespace Ui::BotWebView
diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h
index 1f15dac3c..bff1175ce 100644
--- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h
+++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h
@@ -87,16 +87,21 @@ public:
 	virtual void botClose() = 0;
 };
 
+struct Args {
+	QString url;
+	Webview::StorageId storageId;
+	rpl::producer<QString> title;
+	object_ptr<Ui::RpWidget> titleBadge = { nullptr };
+	rpl::producer<QString> bottom;
+	not_null<Delegate*> delegate;
+	MenuButtons menuButtons;
+	bool fullscreen = false;
+	bool allowClipboardRead = false;
+};
+
 class Panel final : public base::has_weak_ptr {
 public:
-	Panel(
-		const Webview::StorageId &storageId,
-		rpl::producer<QString> title,
-		object_ptr<Ui::RpWidget> titleBadge,
-		not_null<Delegate*> delegate,
-		MenuButtons menuButtons,
-		bool fullscreen,
-		bool allowClipboardRead);
+	explicit Panel(Args &&args);
 	~Panel();
 
 	void requestActivate();
@@ -148,6 +153,7 @@ private:
 	void processBackButtonMessage(const QJsonObject &args);
 	void processSettingsButtonMessage(const QJsonObject &args);
 	void processHeaderColor(const QJsonObject &args);
+	void processBackgroundColor(const QJsonObject &args);
 	void processBottomBarColor(const QJsonObject &args);
 	void openTgLink(const QJsonObject &args);
 	void openExternalLink(const QJsonObject &args);
@@ -171,6 +177,8 @@ private:
 	void sendContentSafeArea();
 	void sendFullScreen();
 
+	void updateColorOverrides(const Webview::ThemeParams &params);
+
 	using EventData = std::variant<QString, QJsonObject>;
 	void postEvent(const QString &event);
 	void postEvent(const QString &event, EventData data);
@@ -201,29 +209,22 @@ private:
 	rpl::event_stream<> _themeUpdateForced;
 	std::optional<QColor> _bottomBarColor;
 	rpl::lifetime _headerColorLifetime;
+	rpl::lifetime _bodyColorLifetime;
 	rpl::lifetime _bottomBarColorLifetime;
 	rpl::variable<bool> _fullscreen = false;
-	bool _layerShown = false;
-	bool _webviewProgress = false;
-	bool _themeUpdateScheduled = false;
-	bool _hiddenForPayment = false;
-	bool _closeWithConfirmationScheduled = false;
-	bool _allowClipboardRead = false;
-	bool _inBlockingRequest = false;
+	bool _layerShown : 1 = false;
+	bool _webviewProgress : 1 = false;
+	bool _themeUpdateScheduled : 1 = false;
+	bool _hiddenForPayment : 1 = false;
+	bool _closeWithConfirmationScheduled : 1 = false;
+	bool _allowClipboardRead : 1 = false;
+	bool _inBlockingRequest : 1 = false;
+	bool _headerColorReceived : 1 = false;
+	bool _bodyColorReceived : 1 = false;
+	bool _bottomColorReceived : 1 = false;
 
 };
 
-struct Args {
-	QString url;
-	Webview::StorageId storageId;
-	rpl::producer<QString> title;
-	object_ptr<Ui::RpWidget> titleBadge = { nullptr };
-	rpl::producer<QString> bottom;
-	not_null<Delegate*> delegate;
-	MenuButtons menuButtons;
-	bool fullscreen = false;
-	bool allowClipboardRead = false;
-};
 [[nodiscard]] std::unique_ptr<Panel> Show(Args &&args);
 
 } // namespace Ui::BotWebView
diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp
index 899534d9e..dcdbfcccd 100644
--- a/Telegram/SourceFiles/window/themes/window_theme.cpp
+++ b/Telegram/SourceFiles/window/themes/window_theme.cpp
@@ -1539,7 +1539,8 @@ bool ReadPaletteValues(const QByteArray &content, Fn<bool(QLatin1String name, QL
 			mix(bg.blue(), shadow.blue()))));
 	}
 	return {
-		.opaqueBg = st::windowBg->c,
+		.bodyBg = st::windowBg->c,
+		.titleBg = QColor(0, 0, 0, 0),
 		.scrollBg = st::scrollBg->c,
 		.scrollBgOver = st::scrollBgOver->c,
 		.scrollBarBg = st::scrollBarBg->c,
diff --git a/Telegram/lib_webview b/Telegram/lib_webview
index efc48237b..095babf23 160000
--- a/Telegram/lib_webview
+++ b/Telegram/lib_webview
@@ -1 +1 @@
-Subproject commit efc48237bcaf269b57c8a37539e11e1887e1f3cf
+Subproject commit 095babf234736e053bdbbc3dc15bc042a40c45b4