telegram_webapp_sdk/webapp/
navigation.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, prelude::Closure};
7
8use crate::webapp::{TelegramWebApp, types::OpenLinkOptions};
9
10impl TelegramWebApp {
11    /// Call `WebApp.openLink(url)`.
12    ///
13    /// # Examples
14    /// ```no_run
15    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
16    /// # let app = TelegramWebApp::instance().unwrap();
17    /// app.open_link("https://example.com", None).unwrap();
18    /// ```
19    pub fn open_link(&self, url: &str, options: Option<&OpenLinkOptions>) -> Result<(), JsValue> {
20        let f = Reflect::get(&self.inner, &"openLink".into())?;
21        let func = f
22            .dyn_ref::<Function>()
23            .ok_or_else(|| JsValue::from_str("openLink is not a function"))?;
24        match options {
25            Some(opts) => {
26                let value = to_value(opts).map_err(|err| JsValue::from_str(&err.to_string()))?;
27                func.call2(&self.inner, &url.into(), &value)?;
28            }
29            None => {
30                func.call1(&self.inner, &url.into())?;
31            }
32        }
33        Ok(())
34    }
35
36    /// Call `WebApp.openTelegramLink(url)`.
37    ///
38    /// # Examples
39    /// ```no_run
40    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
41    /// # let app = TelegramWebApp::instance().unwrap();
42    /// app.open_telegram_link("https://t.me/telegram").unwrap();
43    /// ```
44    pub fn open_telegram_link(&self, url: &str) -> Result<(), JsValue> {
45        Reflect::get(&self.inner, &"openTelegramLink".into())?
46            .dyn_into::<Function>()?
47            .call1(&self.inner, &url.into())?;
48        Ok(())
49    }
50
51    /// Call `WebApp.switchInlineQuery(query, choose_chat_types)`.
52    ///
53    /// # Examples
54    /// ```no_run
55    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
56    /// # let app = TelegramWebApp::instance().unwrap();
57    /// app.switch_inline_query("query", None).unwrap();
58    /// ```
59    ///
60    /// # Errors
61    /// Returns [`JsValue`] if the underlying JS call fails.
62    pub fn switch_inline_query(
63        &self,
64        query: &str,
65        choose_chat_types: Option<&JsValue>
66    ) -> Result<(), JsValue> {
67        let f = Reflect::get(&self.inner, &"switchInlineQuery".into())?;
68        let func = f
69            .dyn_ref::<Function>()
70            .ok_or_else(|| JsValue::from_str("switchInlineQuery is not a function"))?;
71        match choose_chat_types {
72            Some(types) => func.call2(&self.inner, &query.into(), types)?,
73            None => func.call1(&self.inner, &query.into())?
74        };
75        Ok(())
76    }
77
78    /// Call `WebApp.shareMessage(msg_id, callback)`.
79    ///
80    /// # Examples
81    /// ```no_run
82    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
83    /// # let app = TelegramWebApp::instance().unwrap();
84    /// app.share_message("id123", |sent| {
85    ///     let _ = sent;
86    /// })
87    /// .unwrap();
88    /// ```
89    ///
90    /// # Errors
91    /// Returns [`JsValue`] if the underlying JS call fails.
92    pub fn share_message<F>(&self, msg_id: &str, callback: F) -> Result<(), JsValue>
93    where
94        F: 'static + Fn(bool)
95    {
96        let cb = Closure::<dyn FnMut(JsValue)>::new(move |v: JsValue| {
97            callback(v.as_bool().unwrap_or(false));
98        });
99        let f = Reflect::get(&self.inner, &"shareMessage".into())?;
100        let func = f
101            .dyn_ref::<Function>()
102            .ok_or_else(|| JsValue::from_str("shareMessage is not a function"))?;
103        func.call2(&self.inner, &msg_id.into(), cb.as_ref().unchecked_ref())?;
104        cb.forget();
105        Ok(())
106    }
107
108    /// Call `WebApp.shareToStory(media_url, params)`.
109    ///
110    /// # Examples
111    /// ```no_run
112    /// # use js_sys::Object;
113    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
114    /// # let app = TelegramWebApp::instance().unwrap();
115    /// let params = Object::new();
116    /// app.share_to_story("https://example.com/image.png", Some(&params.into()))
117    ///     .unwrap();
118    /// ```
119    ///
120    /// # Errors
121    /// Returns [`JsValue`] if the underlying JS call fails.
122    pub fn share_to_story(
123        &self,
124        media_url: &str,
125        params: Option<&JsValue>
126    ) -> Result<(), JsValue> {
127        let f = Reflect::get(&self.inner, &"shareToStory".into())?;
128        let func = f
129            .dyn_ref::<Function>()
130            .ok_or_else(|| JsValue::from_str("shareToStory is not a function"))?;
131        match params {
132            Some(p) => func.call2(&self.inner, &media_url.into(), p)?,
133            None => func.call1(&self.inner, &media_url.into())?
134        };
135        Ok(())
136    }
137
138    /// Call `WebApp.shareURL(url, text)`.
139    ///
140    /// # Examples
141    /// ```no_run
142    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
143    /// # let app = TelegramWebApp::instance().unwrap();
144    /// app.share_url("https://example.com", Some("Check this"))
145    ///     .unwrap();
146    /// ```
147    ///
148    /// # Errors
149    /// Returns [`JsValue`] if the underlying JS call fails.
150    pub fn share_url(&self, url: &str, text: Option<&str>) -> Result<(), JsValue> {
151        let f = Reflect::get(&self.inner, &"shareURL".into())?;
152        let func = f
153            .dyn_ref::<Function>()
154            .ok_or_else(|| JsValue::from_str("shareURL is not a function"))?;
155        match text {
156            Some(t) => func.call2(&self.inner, &url.into(), &t.into())?,
157            None => func.call1(&self.inner, &url.into())?
158        };
159        Ok(())
160    }
161
162    /// Call `WebApp.joinVoiceChat(chat_id, invite_hash)`.
163    ///
164    /// # Examples
165    /// ```no_run
166    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
167    /// # let app = TelegramWebApp::instance().unwrap();
168    /// app.join_voice_chat("chat", None).unwrap();
169    /// ```
170    ///
171    /// # Errors
172    /// Returns [`JsValue`] if the underlying JS call fails.
173    pub fn join_voice_chat(
174        &self,
175        chat_id: &str,
176        invite_hash: Option<&str>
177    ) -> Result<(), JsValue> {
178        let f = Reflect::get(&self.inner, &"joinVoiceChat".into())?;
179        let func = f
180            .dyn_ref::<Function>()
181            .ok_or_else(|| JsValue::from_str("joinVoiceChat is not a function"))?;
182        match invite_hash {
183            Some(hash) => func.call2(&self.inner, &chat_id.into(), &hash.into())?,
184            None => func.call1(&self.inner, &chat_id.into())?
185        };
186        Ok(())
187    }
188
189    /// Call `WebApp.addToHomeScreen()` and return whether the prompt was shown.
190    ///
191    /// # Examples
192    /// ```no_run
193    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
194    /// # let app = TelegramWebApp::instance().unwrap();
195    /// let _shown = app.add_to_home_screen().unwrap();
196    /// ```
197    pub fn add_to_home_screen(&self) -> Result<bool, JsValue> {
198        let f = Reflect::get(&self.inner, &"addToHomeScreen".into())?;
199        let func = f
200            .dyn_ref::<Function>()
201            .ok_or_else(|| JsValue::from_str("addToHomeScreen is not a function"))?;
202        let result = func.call0(&self.inner)?;
203        Ok(result.as_bool().unwrap_or(false))
204    }
205
206    /// Call `WebApp.checkHomeScreenStatus(callback)`.
207    ///
208    /// # Examples
209    /// ```no_run
210    /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
211    /// # let app = TelegramWebApp::instance().unwrap();
212    /// app.check_home_screen_status(|status| {
213    ///     let _ = status;
214    /// })
215    /// .unwrap();
216    /// ```
217    pub fn check_home_screen_status<F>(&self, callback: F) -> Result<(), JsValue>
218    where
219        F: 'static + Fn(String)
220    {
221        let cb = Closure::<dyn FnMut(JsValue)>::new(move |status: JsValue| {
222            callback(status.as_string().unwrap_or_default());
223        });
224        let f = Reflect::get(&self.inner, &"checkHomeScreenStatus".into())?;
225        let func = f
226            .dyn_ref::<Function>()
227            .ok_or_else(|| JsValue::from_str("checkHomeScreenStatus is not a function"))?;
228        func.call1(&self.inner, cb.as_ref().unchecked_ref())?;
229        cb.forget();
230        Ok(())
231    }
232}