Skip to main content

telegram_webapp_sdk/leptos/
back_button.rs

1// SPDX-FileCopyrightText: 2025-2026 RAprogramm <andrey.rozanov.vl@gmail.com>
2// SPDX-License-Identifier: MIT
3
4use std::cell::RefCell;
5
6use leptos::prelude::*;
7
8use crate::{
9    logger,
10    webapp::{EventHandle, TelegramWebApp}
11};
12
13thread_local! {
14    static BACK_BUTTON_HANDLE: RefCell<Option<EventHandle<dyn FnMut()>>> =
15        const { RefCell::new(None) };
16}
17
18/// Leptos component for the native back button.
19///
20/// Mirrors the React SDK's `BackButton` ergonomics. Drives `WebApp.BackButton`:
21/// shows/hides it based on the `visible` signal, registers the optional click
22/// callback, and cleans both up on unmount.
23///
24/// # Examples
25/// ```no_run
26/// use leptos::prelude::*;
27/// use telegram_webapp_sdk::leptos::BackButton;
28///
29/// #[component]
30/// fn App() -> impl IntoView {
31///     let visible = RwSignal::new(true);
32///     view! {
33///         <BackButton
34///             visible=visible
35///             on_click=move || { web_sys::console::log_1(&"back".into()); }
36///         />
37///     }
38/// }
39/// ```
40#[component]
41pub fn BackButton<F>(
42    /// Reactive flag controlling whether the back button is shown.
43    #[prop(into)]
44    visible: Signal<bool>,
45    /// Optional callback invoked when the back button is clicked.
46    #[prop(optional)]
47    on_click: Option<F>
48) -> impl IntoView
49where
50    F: Fn() + Clone + 'static
51{
52    Effect::new(move |_| {
53        if let Some(app) = TelegramWebApp::instance() {
54            let result = if visible.get() {
55                app.show_back_button()
56            } else {
57                app.hide_back_button()
58            };
59            if let Err(err) = result {
60                logger::error(&format!("BackButton visibility toggle failed: {err:?}"));
61            }
62        }
63    });
64
65    if let Some(cb) = on_click
66        && let Some(app) = TelegramWebApp::instance()
67    {
68        match app.set_back_button_callback(cb) {
69            Ok(handle) => BACK_BUTTON_HANDLE.with(|c| {
70                *c.borrow_mut() = Some(handle);
71            }),
72            Err(err) => logger::error(&format!("set_back_button_callback failed: {err:?}"))
73        }
74    }
75
76    on_cleanup(move || {
77        if let Some(app) = TelegramWebApp::instance() {
78            BACK_BUTTON_HANDLE.with(|c| {
79                if let Some(handle) = c.borrow_mut().take()
80                    && let Err(err) = app.remove_back_button_callback(handle)
81                {
82                    logger::error(&format!("remove_back_button_callback failed: {err:?}"));
83                }
84            });
85            if let Err(err) = app.hide_back_button() {
86                logger::error(&format!("hide_back_button failed: {err:?}"));
87            }
88        }
89    });
90
91    View::new(())
92}