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}