Skip to main content

telegram_webapp_sdk/yew/
theme.rs

1// SPDX-FileCopyrightText: 2025-2026 RAprogramm <andrey.rozanov.vl@gmail.com>
2// SPDX-License-Identifier: MIT
3
4use std::{cell::RefCell, rc::Rc};
5
6use yew::prelude::{hook, use_effect_with, use_state};
7
8use crate::{
9    api::theme::get_theme_params,
10    core::types::theme_params::TelegramThemeParams,
11    webapp::{EventHandle, TelegramWebApp}
12};
13
14type HandleSlot = Rc<RefCell<Option<EventHandle<dyn FnMut()>>>>;
15
16/// Snapshot of `Telegram.WebApp` theme state.
17#[derive(Clone, Debug, PartialEq, Default)]
18pub struct ThemeState {
19    /// `"light"` or `"dark"`.
20    pub color_scheme: Option<String>,
21    /// Parsed theme palette.
22    pub params:       TelegramThemeParams
23}
24
25impl ThemeState {
26    fn snapshot(app: Option<&TelegramWebApp>) -> Self {
27        let color_scheme = app.and_then(|a| a.color_scheme());
28        let params = get_theme_params().unwrap_or_default();
29        Self {
30            color_scheme,
31            params
32        }
33    }
34}
35
36/// Yew reactive hook over `Telegram.WebApp` theme state.
37///
38/// Updates on `themeChanged`. The subscription is removed on unmount.
39///
40/// # Examples
41/// ```no_run
42/// use telegram_webapp_sdk::yew::use_theme;
43/// use yew::prelude::*;
44///
45/// #[component]
46/// fn ThemeBadge() -> Html {
47///     let theme = use_theme();
48///     html! { <span>{ theme.color_scheme.clone().unwrap_or_default() }</span> }
49/// }
50/// ```
51#[hook]
52pub fn use_theme() -> ThemeState {
53    let state = use_state(|| ThemeState::snapshot(TelegramWebApp::instance().as_ref()));
54
55    {
56        let state = state.clone();
57        use_effect_with((), move |_| {
58            let stash: HandleSlot = Rc::new(RefCell::new(None));
59            if let Some(app) = TelegramWebApp::instance() {
60                let app_for_handler = app.clone();
61                let state_for_handler = state.clone();
62                if let Ok(handle) = app.on_theme_changed(move || {
63                    state_for_handler.set(ThemeState::snapshot(Some(&app_for_handler)));
64                }) {
65                    *stash.borrow_mut() = Some(handle);
66                }
67            }
68            move || {
69                stash.borrow_mut().take();
70            }
71        });
72    }
73
74    (*state).clone()
75}