Use initial bot web app header/body colors.

This commit is contained in:
John Preston 2024-11-11 19:08:06 +04:00
parent 2d1fb0562d
commit 7bf78b3317
8 changed files with 142 additions and 72 deletions

View file

@ -345,6 +345,24 @@ void UserData::setBotInfo(const MTPBotInfo &info) {
const auto privacyChanged = (botInfo->privacyPolicyUrl != privacy); const auto privacyChanged = (botInfo->privacyPolicyUrl != privacy);
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) { if (changedCommands || changedButton || privacyChanged) {
owner().botCommandsChanged(this); owner().botCommandsChanged(this);
} }

View file

@ -33,6 +33,11 @@ struct BotInfo {
QString botMenuButtonUrl; QString botMenuButtonUrl;
QString privacyPolicyUrl; 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; QString startToken;
Dialogs::EntryState inlineReturnTo; Dialogs::EntryState inlineReturnTo;

View file

@ -1423,7 +1423,23 @@ void WebViewInstance::started(uint64 queryId) {
} }
Webview::ThemeParams WebViewInstance::botThemeParams() { 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) { bool WebViewInstance::botHandleLocalUri(QString uri, bool keepOpen) {

View file

@ -550,7 +550,7 @@ bool Panel::createWebview(const Webview::ThemeParams &params) {
_webview = std::make_unique<WebviewWithLifetime>( _webview = std::make_unique<WebviewWithLifetime>(
container, container,
Webview::WindowConfig{ Webview::WindowConfig{
.opaqueBg = params.opaqueBg, .opaqueBg = params.bodyBg,
.storageId = _delegate->panelWebviewStorageId(), .storageId = _delegate->panelWebviewStorageId(),
}); });
@ -919,7 +919,7 @@ void Panel::updateThemeParams(const Webview::ThemeParams &params) {
return; return;
} }
_webview->window.updateTheme( _webview->window.updateTheme(
params.opaqueBg, params.bodyBg,
params.scrollBg, params.scrollBg,
params.scrollBgOver, params.scrollBgOver,
params.scrollBarBg, params.scrollBarBg,

View file

@ -366,23 +366,19 @@ Panel::Progress::Progress(QWidget *parent, Fn<QRect()> rect)
st::paymentsLoading) { st::paymentsLoading) {
} }
Panel::Panel( Panel::Panel(Args &&args)
const Webview::StorageId &storageId, : _storageId(args.storageId)
rpl::producer<QString> title, , _delegate(args.delegate)
object_ptr<Ui::RpWidget> titleBadge, , _menuButtons(args.menuButtons)
not_null<Delegate*> delegate,
MenuButtons menuButtons,
bool fullscreen,
bool allowClipboardRead)
: _storageId(storageId)
, _delegate(delegate)
, _menuButtons(menuButtons)
, _widget(std::make_unique<SeparatePanel>()) , _widget(std::make_unique<SeparatePanel>())
, _fullscreen(fullscreen) , _fullscreen(args.fullscreen)
, _allowClipboardRead(allowClipboardRead) { , _allowClipboardRead(args.allowClipboardRead) {
_widget->setWindowFlag(Qt::WindowStaysOnTopHint, false); _widget->setWindowFlag(Qt::WindowStaysOnTopHint, false);
_widget->setInnerSize(st::botWebViewPanelSize, true); _widget->setInnerSize(st::botWebViewPanelSize, true);
const auto params = _delegate->botThemeParams();
updateColorOverrides(params);
_fullscreen.value( _fullscreen.value(
) | rpl::start_with_next([=](bool fullscreen) { ) | rpl::start_with_next([=](bool fullscreen) {
_widget->toggleFullScreen(fullscreen); _widget->toggleFullScreen(fullscreen);
@ -426,8 +422,17 @@ Panel::Panel(
}); });
}, _widget->lifetime()); }, _widget->lifetime());
setTitle(std::move(title)); setTitle(std::move(args.title));
_widget->setTitleBadge(std::move(titleBadge)); _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() { Panel::~Panel() {
@ -679,13 +684,17 @@ bool Panel::createWebview(const Webview::ThemeParams &params) {
_widget->showInner(std::move(outer)); _widget->showInner(std::move(outer));
_webviewParent = container; _webviewParent = container;
_headerColorReceived = false;
_bodyColorReceived = false;
_bottomColorReceived = false;
updateColorOverrides(params);
createWebviewBottom(); createWebviewBottom();
container->show(); container->show();
_webview = std::make_unique<WebviewWithLifetime>( _webview = std::make_unique<WebviewWithLifetime>(
container, container,
Webview::WindowConfig{ Webview::WindowConfig{
.opaqueBg = params.opaqueBg, .opaqueBg = params.bodyBg,
.storageId = _storageId, .storageId = _storageId,
}); });
const auto raw = &_webview->window; const auto raw = &_webview->window;
@ -823,6 +832,8 @@ bool Panel::createWebview(const Webview::ThemeParams &params) {
requestClipboardText(arguments); requestClipboardText(arguments);
} else if (command == "web_app_set_header_color") { } else if (command == "web_app_set_header_color") {
processHeaderColor(arguments); processHeaderColor(arguments);
} else if (command == "web_app_set_background_color") {
processBackgroundColor(arguments);
} else if (command == "web_app_set_bottom_bar_color") { } else if (command == "web_app_set_bottom_bar_color") {
processBottomBarColor(arguments); processBottomBarColor(arguments);
} else if (command == "web_app_send_prepared_message") { } else if (command == "web_app_send_prepared_message") {
@ -1437,6 +1448,7 @@ void Panel::processSettingsButtonMessage(const QJsonObject &args) {
} }
void Panel::processHeaderColor(const QJsonObject &args) { void Panel::processHeaderColor(const QJsonObject &args) {
_headerColorReceived = true;
if (const auto color = ParseColor(args["color"].toString())) { if (const auto color = ParseColor(args["color"].toString())) {
_widget->overrideTitleColor(color); _widget->overrideTitleColor(color);
_headerColorLifetime.destroy(); _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) { void Panel::processBottomBarColor(const QJsonObject &args) {
_bottomColorReceived = true;
if (const auto color = ParseColor(args["color"].toString())) { if (const auto color = ParseColor(args["color"].toString())) {
_widget->overrideBottomBarColor(color); _widget->overrideBottomBarColor(color);
_bottomBarColor = color; _bottomBarColor = color;
@ -1462,7 +1499,7 @@ void Panel::processBottomBarColor(const QJsonObject &args) {
args["color_key"].toString())) { args["color_key"].toString())) {
_widget->overrideBottomBarColor((*color)->c); _widget->overrideBottomBarColor((*color)->c);
_bottomBarColor = (*color)->c; _bottomBarColor = (*color)->c;
_headerColorLifetime = style::PaletteChanged( _bottomBarColorLifetime = style::PaletteChanged(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
_widget->overrideBottomBarColor((*color)->c); _widget->overrideBottomBarColor((*color)->c);
_bottomBarColor = (*color)->c; _bottomBarColor = (*color)->c;
@ -1470,7 +1507,7 @@ void Panel::processBottomBarColor(const QJsonObject &args) {
} else { } else {
_widget->overrideBottomBarColor(std::nullopt); _widget->overrideBottomBarColor(std::nullopt);
_bottomBarColor = std::nullopt; _bottomBarColor = std::nullopt;
_headerColorLifetime.destroy(); _bottomBarColorLifetime.destroy();
} }
if (const auto raw = _bottomButtonsBg.get()) { if (const auto raw = _bottomButtonsBg.get()) {
raw->update(); raw->update();
@ -1596,13 +1633,15 @@ void Panel::layoutButtons() {
} else if (_bottomButtonsBg) { } else if (_bottomButtonsBg) {
_bottomButtonsBg->hide(); _bottomButtonsBg->hide();
} }
_footerHeight = _layerShown const auto footer = _layerShown
? 0 ? 0
: any : any
? _bottomButtonsBg->height() ? _bottomButtonsBg->height()
: _fullscreen.current() : _fullscreen.current()
? 0 ? 0
: _webviewBottom->height(); : _webviewBottom->height();
_widget->setBottomBarHeight((!_layerShown && any) ? footer : 0);
_footerHeight = footer;
} }
void Panel::showBox(object_ptr<BoxContent> box) { void Panel::showBox(object_ptr<BoxContent> box) {
@ -1707,11 +1746,12 @@ void Panel::showCriticalError(const TextWithEntities &text) {
} }
void Panel::updateThemeParams(const Webview::ThemeParams &params) { void Panel::updateThemeParams(const Webview::ThemeParams &params) {
updateColorOverrides(params);
if (!_webview || !_webview->window.widget()) { if (!_webview || !_webview->window.widget()) {
return; return;
} }
_webview->window.updateTheme( _webview->window.updateTheme(
params.opaqueBg, params.bodyBg,
params.scrollBg, params.scrollBg,
params.scrollBgOver, params.scrollBgOver,
params.scrollBarBg, params.scrollBarBg,
@ -1719,6 +1759,15 @@ void Panel::updateThemeParams(const Webview::ThemeParams &params) {
postEvent("theme_changed", "{\"theme_params\": " + params.json + "}"); 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) { void Panel::invoiceClosed(const QString &slug, const QString &status) {
if (!_webview || !_webview->window.widget()) { if (!_webview || !_webview->window.widget()) {
return; return;
@ -1802,27 +1851,7 @@ rpl::lifetime &Panel::lifetime() {
} }
std::unique_ptr<Panel> Show(Args &&args) { std::unique_ptr<Panel> Show(Args &&args) {
auto result = std::make_unique<Panel>( return std::make_unique<Panel>(std::move(args));
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;
} }
} // namespace Ui::BotWebView } // namespace Ui::BotWebView

View file

@ -87,16 +87,21 @@ public:
virtual void botClose() = 0; 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 { class Panel final : public base::has_weak_ptr {
public: public:
Panel( explicit Panel(Args &&args);
const Webview::StorageId &storageId,
rpl::producer<QString> title,
object_ptr<Ui::RpWidget> titleBadge,
not_null<Delegate*> delegate,
MenuButtons menuButtons,
bool fullscreen,
bool allowClipboardRead);
~Panel(); ~Panel();
void requestActivate(); void requestActivate();
@ -148,6 +153,7 @@ private:
void processBackButtonMessage(const QJsonObject &args); void processBackButtonMessage(const QJsonObject &args);
void processSettingsButtonMessage(const QJsonObject &args); void processSettingsButtonMessage(const QJsonObject &args);
void processHeaderColor(const QJsonObject &args); void processHeaderColor(const QJsonObject &args);
void processBackgroundColor(const QJsonObject &args);
void processBottomBarColor(const QJsonObject &args); void processBottomBarColor(const QJsonObject &args);
void openTgLink(const QJsonObject &args); void openTgLink(const QJsonObject &args);
void openExternalLink(const QJsonObject &args); void openExternalLink(const QJsonObject &args);
@ -171,6 +177,8 @@ private:
void sendContentSafeArea(); void sendContentSafeArea();
void sendFullScreen(); void sendFullScreen();
void updateColorOverrides(const Webview::ThemeParams &params);
using EventData = std::variant<QString, QJsonObject>; using EventData = std::variant<QString, QJsonObject>;
void postEvent(const QString &event); void postEvent(const QString &event);
void postEvent(const QString &event, EventData data); void postEvent(const QString &event, EventData data);
@ -201,29 +209,22 @@ private:
rpl::event_stream<> _themeUpdateForced; rpl::event_stream<> _themeUpdateForced;
std::optional<QColor> _bottomBarColor; std::optional<QColor> _bottomBarColor;
rpl::lifetime _headerColorLifetime; rpl::lifetime _headerColorLifetime;
rpl::lifetime _bodyColorLifetime;
rpl::lifetime _bottomBarColorLifetime; rpl::lifetime _bottomBarColorLifetime;
rpl::variable<bool> _fullscreen = false; rpl::variable<bool> _fullscreen = false;
bool _layerShown = false; bool _layerShown : 1 = false;
bool _webviewProgress = false; bool _webviewProgress : 1 = false;
bool _themeUpdateScheduled = false; bool _themeUpdateScheduled : 1 = false;
bool _hiddenForPayment = false; bool _hiddenForPayment : 1 = false;
bool _closeWithConfirmationScheduled = false; bool _closeWithConfirmationScheduled : 1 = false;
bool _allowClipboardRead = false; bool _allowClipboardRead : 1 = false;
bool _inBlockingRequest = 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); [[nodiscard]] std::unique_ptr<Panel> Show(Args &&args);
} // namespace Ui::BotWebView } // namespace Ui::BotWebView

View file

@ -1539,7 +1539,8 @@ bool ReadPaletteValues(const QByteArray &content, Fn<bool(QLatin1String name, QL
mix(bg.blue(), shadow.blue())))); mix(bg.blue(), shadow.blue()))));
} }
return { return {
.opaqueBg = st::windowBg->c, .bodyBg = st::windowBg->c,
.titleBg = QColor(0, 0, 0, 0),
.scrollBg = st::scrollBg->c, .scrollBg = st::scrollBg->c,
.scrollBgOver = st::scrollBgOver->c, .scrollBgOver = st::scrollBgOver->c,
.scrollBarBg = st::scrollBarBg->c, .scrollBarBg = st::scrollBarBg->c,

@ -1 +1 @@
Subproject commit efc48237bcaf269b57c8a37539e11e1887e1f3cf Subproject commit 095babf234736e053bdbbc3dc15bc042a40c45b4