Skip to main content

telegram_webapp_sdk/leptos/
theme.rs

1// SPDX-FileCopyrightText: 2025-2026 RAprogramm <andrey.rozanov.vl@gmail.com>
2// SPDX-License-Identifier: MIT
3
4use leptos::prelude::*;
5use send_wrapper::SendWrapper;
6
7use crate::{
8    api::theme::get_theme_params, core::types::theme_params::TelegramThemeParams,
9    webapp::TelegramWebApp
10};
11
12/// Snapshot of `Telegram.WebApp` theme state.
13#[derive(Clone, Debug, PartialEq, Default)]
14pub struct ThemeState {
15    /// `"light"` or `"dark"`.
16    pub color_scheme: Option<String>,
17    /// Parsed theme palette.
18    pub params:       TelegramThemeParams
19}
20
21impl ThemeState {
22    fn snapshot(app: Option<&TelegramWebApp>) -> Self {
23        let color_scheme = app.and_then(|a| a.color_scheme());
24        let params = get_theme_params().unwrap_or_default();
25        Self {
26            color_scheme,
27            params
28        }
29    }
30}
31
32/// Leptos reactive hook over `Telegram.WebApp` theme state.
33///
34/// Updates on `themeChanged`. The subscription is removed on scope disposal.
35///
36/// # Examples
37/// ```no_run
38/// use leptos::prelude::*;
39/// use telegram_webapp_sdk::leptos::use_theme;
40///
41/// #[component]
42/// fn ThemeBadge() -> impl IntoView {
43///     let theme = use_theme();
44///     view! { <span>{ move || theme.get().color_scheme.unwrap_or_default() }</span> }
45/// }
46/// ```
47pub fn use_theme() -> ReadSignal<ThemeState> {
48    let app = TelegramWebApp::instance();
49    let signal = RwSignal::new(ThemeState::snapshot(app.as_ref()));
50
51    if let Some(app) = app {
52        let app_for_handler = app.clone();
53        let writer = signal;
54        if let Ok(handle) = app.on_theme_changed(move || {
55            writer.set(ThemeState::snapshot(Some(&app_for_handler)));
56        }) {
57            let wrapped = SendWrapper::new(handle);
58            on_cleanup(move || {
59                drop(wrapped);
60            });
61        }
62    }
63
64    signal.read_only()
65}