Skip to main content

telegram_webapp_sdk/webapp/
lifecycle.rs

1// SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
2// SPDX-License-Identifier: MIT
3
4use js_sys::{Function, Reflect};
5use serde_wasm_bindgen::to_value;
6use wasm_bindgen::{JsCast, JsValue};
7
8use crate::webapp::{TelegramWebApp, types::CloseOptions};
9
10impl TelegramWebApp {
11    /// Call `WebApp.expand()`.
12    ///
13    /// # Errors
14    /// Returns [`JsValue`] if the underlying JS call fails.
15    pub fn expand(&self) -> Result<(), JsValue> {
16        self.call0("expand")
17    }
18
19    /// Call `WebApp.close()`.
20    ///
21    /// # Errors
22    /// Returns [`JsValue`] if the underlying JS call fails.
23    pub fn close(&self) -> Result<(), JsValue> {
24        self.call0("close")
25    }
26
27    /// Call `WebApp.close(options)` (Bot API 7.6+ for `return_back`).
28    ///
29    /// On older Telegram clients the option is silently ignored on the JS side.
30    ///
31    /// # Examples
32    /// ```no_run
33    /// # use telegram_webapp_sdk::webapp::{CloseOptions, TelegramWebApp};
34    /// # let app = TelegramWebApp::instance().unwrap();
35    /// app.close_with_options(&CloseOptions {
36    ///     return_back: Some(true)
37    /// })
38    /// .unwrap();
39    /// ```
40    ///
41    /// # Errors
42    /// Returns [`JsValue`] if the underlying JS call fails or the options fail
43    /// to serialize.
44    pub fn close_with_options(&self, options: &CloseOptions) -> Result<(), JsValue> {
45        let payload = to_value(options).map_err(|err| JsValue::from_str(&err.to_string()))?;
46        let f = Reflect::get(&self.inner, &"close".into())?;
47        let func = f
48            .dyn_ref::<Function>()
49            .ok_or_else(|| JsValue::from_str("close is not a function"))?;
50        func.call1(&self.inner, &payload)?;
51        Ok(())
52    }
53
54    /// Call `WebApp.enableClosingConfirmation()`.
55    ///
56    /// # Examples
57    /// ```no_run
58    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
59    /// # let app = TelegramWebApp::instance().unwrap();
60    /// app.enable_closing_confirmation().unwrap();
61    /// ```
62    ///
63    /// # Errors
64    /// Returns [`JsValue`] if the underlying JS call fails.
65    pub fn enable_closing_confirmation(&self) -> Result<(), JsValue> {
66        self.call0("enableClosingConfirmation")
67    }
68
69    /// Call `WebApp.disableClosingConfirmation()`.
70    ///
71    /// # Examples
72    /// ```no_run
73    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
74    /// # let app = TelegramWebApp::instance().unwrap();
75    /// app.disable_closing_confirmation().unwrap();
76    /// ```
77    ///
78    /// # Errors
79    /// Returns [`JsValue`] if the underlying JS call fails.
80    pub fn disable_closing_confirmation(&self) -> Result<(), JsValue> {
81        self.call0("disableClosingConfirmation")
82    }
83
84    /// Returns whether closing confirmation is currently enabled.
85    ///
86    /// # Examples
87    /// ```no_run
88    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
89    /// # let app = TelegramWebApp::instance().unwrap();
90    /// let _ = app.is_closing_confirmation_enabled();
91    /// ```
92    pub fn is_closing_confirmation_enabled(&self) -> bool {
93        Reflect::get(&self.inner, &"isClosingConfirmationEnabled".into())
94            .ok()
95            .and_then(|v| v.as_bool())
96            .unwrap_or(false)
97    }
98
99    /// Call `WebApp.requestFullscreen()`.
100    ///
101    /// # Examples
102    /// ```no_run
103    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
104    /// # let app = TelegramWebApp::instance().unwrap();
105    /// app.request_fullscreen().unwrap();
106    /// ```
107    ///
108    /// # Errors
109    /// Returns [`JsValue`] if the underlying JS call fails.
110    pub fn request_fullscreen(&self) -> Result<(), JsValue> {
111        self.call0("requestFullscreen")
112    }
113
114    /// Call `WebApp.exitFullscreen()`.
115    ///
116    /// # Examples
117    /// ```no_run
118    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
119    /// # let app = TelegramWebApp::instance().unwrap();
120    /// app.exit_fullscreen().unwrap();
121    /// ```
122    ///
123    /// # Errors
124    /// Returns [`JsValue`] if the underlying JS call fails.
125    pub fn exit_fullscreen(&self) -> Result<(), JsValue> {
126        self.call0("exitFullscreen")
127    }
128
129    /// Returns whether the app is displayed in fullscreen mode.
130    ///
131    /// # Examples
132    /// ```no_run
133    /// use telegram_webapp_sdk::webapp::TelegramWebApp;
134    ///
135    /// if let Some(app) = TelegramWebApp::instance() {
136    ///     let _ = app.is_fullscreen();
137    /// }
138    /// ```
139    pub fn is_fullscreen(&self) -> bool {
140        Reflect::get(&self.inner, &"isFullscreen".into())
141            .ok()
142            .and_then(|v| v.as_bool())
143            .unwrap_or(false)
144    }
145
146    /// Call `WebApp.lockOrientation(orientation)`.
147    ///
148    /// # Examples
149    /// ```no_run
150    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
151    /// # let app = TelegramWebApp::instance().unwrap();
152    /// app.lock_orientation("portrait").unwrap();
153    /// ```
154    ///
155    /// # Errors
156    /// Returns [`JsValue`] if the underlying JS call fails.
157    pub fn lock_orientation(&self, orientation: &str) -> Result<(), JsValue> {
158        self.call1("lockOrientation", &orientation.into())
159    }
160
161    /// Call `WebApp.unlockOrientation()`.
162    ///
163    /// # Examples
164    /// ```no_run
165    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
166    /// # let app = TelegramWebApp::instance().unwrap();
167    /// app.unlock_orientation().unwrap();
168    /// ```
169    ///
170    /// # Errors
171    /// Returns [`JsValue`] if the underlying JS call fails.
172    pub fn unlock_orientation(&self) -> Result<(), JsValue> {
173        self.call0("unlockOrientation")
174    }
175
176    /// Returns whether the orientation is locked.
177    ///
178    /// # Examples
179    /// ```no_run
180    /// use telegram_webapp_sdk::webapp::TelegramWebApp;
181    ///
182    /// if let Some(app) = TelegramWebApp::instance() {
183    ///     let _ = app.is_orientation_locked();
184    /// }
185    /// ```
186    pub fn is_orientation_locked(&self) -> bool {
187        Reflect::get(&self.inner, &"isOrientationLocked".into())
188            .ok()
189            .and_then(|v| v.as_bool())
190            .unwrap_or(false)
191    }
192
193    /// Call `WebApp.enableVerticalSwipes()`.
194    ///
195    /// # Examples
196    /// ```no_run
197    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
198    /// # let app = TelegramWebApp::instance().unwrap();
199    /// app.enable_vertical_swipes().unwrap();
200    /// ```
201    ///
202    /// # Errors
203    /// Returns [`JsValue`] if the underlying JS call fails.
204    pub fn enable_vertical_swipes(&self) -> Result<(), JsValue> {
205        self.call0("enableVerticalSwipes")
206    }
207
208    /// Call `WebApp.disableVerticalSwipes()`.
209    ///
210    /// # Examples
211    /// ```no_run
212    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
213    /// # let app = TelegramWebApp::instance().unwrap();
214    /// app.disable_vertical_swipes().unwrap();
215    /// ```
216    ///
217    /// # Errors
218    /// Returns [`JsValue`] if the underlying JS call fails.
219    pub fn disable_vertical_swipes(&self) -> Result<(), JsValue> {
220        self.call0("disableVerticalSwipes")
221    }
222
223    /// Returns whether vertical swipes are currently enabled.
224    ///
225    /// # Examples
226    /// ```no_run
227    /// use telegram_webapp_sdk::webapp::TelegramWebApp;
228    ///
229    /// if let Some(app) = TelegramWebApp::instance() {
230    ///     let _ = app.is_vertical_swipes_enabled();
231    /// }
232    /// ```
233    pub fn is_vertical_swipes_enabled(&self) -> bool {
234        Reflect::get(&self.inner, &"isVerticalSwipesEnabled".into())
235            .ok()
236            .and_then(|v| v.as_bool())
237            .unwrap_or(false)
238    }
239
240    /// Returns whether the mini app is currently active (visible to the user).
241    ///
242    /// # Examples
243    /// ```no_run
244    /// use telegram_webapp_sdk::webapp::TelegramWebApp;
245    ///
246    /// if let Some(app) = TelegramWebApp::instance() {
247    ///     let _ = app.is_active();
248    /// }
249    /// ```
250    pub fn is_active(&self) -> bool {
251        Reflect::get(&self.inner, &"isActive".into())
252            .ok()
253            .and_then(|v| v.as_bool())
254            .unwrap_or(false)
255    }
256
257    pub fn is_expanded(&self) -> bool {
258        Reflect::get(&self.inner, &"isExpanded".into())
259            .ok()
260            .and_then(|v| v.as_bool())
261            .unwrap_or(false)
262    }
263}
264
265#[cfg(test)]
266mod tests {
267    use js_sys::{Function, Object, Reflect};
268    use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
269    use web_sys::window;
270
271    use crate::webapp::{TelegramWebApp, types::CloseOptions};
272
273    wasm_bindgen_test_configure!(run_in_browser);
274
275    fn setup_webapp() -> Object {
276        let win = window().expect("window");
277        let telegram = Object::new();
278        let webapp = Object::new();
279        let _ = Reflect::set(&win, &"Telegram".into(), &telegram);
280        let _ = Reflect::set(&telegram, &"WebApp".into(), &webapp);
281        webapp
282    }
283
284    #[wasm_bindgen_test]
285    #[allow(dead_code, clippy::unused_unit)]
286    fn close_with_options_passes_return_back() {
287        let webapp = setup_webapp();
288        let capture = Function::new_with_args("opts", "this.captured_close = opts;");
289        let _ = Reflect::set(&webapp, &"close".into(), &capture);
290
291        let app = TelegramWebApp::instance().expect("instance");
292        app.close_with_options(&CloseOptions {
293            return_back: Some(true)
294        })
295        .expect("ok");
296
297        let opts = Reflect::get(&webapp, &"captured_close".into()).expect("captured");
298        let val = Reflect::get(&opts, &"return_back".into()).expect("field");
299        assert_eq!(val.as_bool(), Some(true));
300    }
301
302    #[wasm_bindgen_test]
303    #[allow(dead_code, clippy::unused_unit)]
304    fn close_with_options_omits_unset_fields() {
305        let webapp = setup_webapp();
306        let capture = Function::new_with_args("opts", "this.captured_close = opts;");
307        let _ = Reflect::set(&webapp, &"close".into(), &capture);
308
309        let app = TelegramWebApp::instance().expect("instance");
310        app.close_with_options(&CloseOptions::default())
311            .expect("ok");
312
313        let opts = Reflect::get(&webapp, &"captured_close".into()).expect("captured");
314        let val = Reflect::get(&opts, &"return_back".into()).expect("field");
315        assert!(val.is_undefined());
316    }
317}