telegram_webapp_sdk/webapp/buttons.rs
1// SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
2// SPDX-License-Identifier: MIT
3
4use js_sys::{Function, Object, Reflect};
5use serde_wasm_bindgen::to_value;
6use wasm_bindgen::{JsCast, JsValue, prelude::Closure};
7
8use crate::{
9 logger,
10 webapp::{
11 TelegramWebApp,
12 types::{
13 BottomButton, BottomButtonParams, EventHandle, SecondaryButtonParams,
14 SecondaryButtonPosition
15 }
16 }
17};
18
19impl TelegramWebApp {
20 // === Internal bottom button helpers ===
21
22 pub(super) fn bottom_button_object(&self, button: BottomButton) -> Result<Object, JsValue> {
23 let name = button.js_name();
24 Reflect::get(&self.inner, &name.into())
25 .inspect_err(|_| logger::error(&format!("{name} not available")))?
26 .dyn_into::<Object>()
27 .inspect_err(|_| logger::error(&format!("{name} is not an object")))
28 }
29
30 pub(super) fn bottom_button_method(
31 &self,
32 button: BottomButton,
33 method: &str,
34 arg: Option<&JsValue>
35 ) -> Result<(), JsValue> {
36 let name = button.js_name();
37 let btn = self.bottom_button_object(button)?;
38 let f = Reflect::get(&btn, &method.into())
39 .inspect_err(|_| logger::error(&format!("{name}.{method} not available")))?;
40 let func = f.dyn_ref::<Function>().ok_or_else(|| {
41 logger::error(&format!("{name}.{method} is not a function"));
42 JsValue::from_str("not a function")
43 })?;
44 let result = match arg {
45 Some(v) => func.call1(&btn, v),
46 None => func.call0(&btn)
47 };
48 result.inspect_err(|_| logger::error(&format!("{name}.{method} call failed")))?;
49 Ok(())
50 }
51
52 pub(super) fn bottom_button_property(
53 &self,
54 button: BottomButton,
55 property: &str
56 ) -> Option<JsValue> {
57 self.bottom_button_object(button)
58 .ok()
59 .and_then(|object| Reflect::get(&object, &property.into()).ok())
60 }
61
62 // === Bottom button operations ===
63
64 /// Call `WebApp.MainButton.show()` or `WebApp.SecondaryButton.show()`.
65 ///
66 /// # Errors
67 /// Returns [`JsValue`] if the underlying JS call fails.
68 pub fn show_bottom_button(&self, button: BottomButton) -> Result<(), JsValue> {
69 self.bottom_button_method(button, "show", None)
70 }
71
72 /// Hide a bottom button.
73 ///
74 /// # Errors
75 /// Returns [`JsValue`] if the underlying JS call fails.
76 pub fn hide_bottom_button(&self, button: BottomButton) -> Result<(), JsValue> {
77 self.bottom_button_method(button, "hide", None)
78 }
79
80 /// Set bottom button text.
81 ///
82 /// # Errors
83 /// Returns [`JsValue`] if the underlying JS call fails.
84 pub fn set_bottom_button_text(&self, button: BottomButton, text: &str) -> Result<(), JsValue> {
85 self.bottom_button_method(button, "setText", Some(&text.into()))
86 }
87
88 /// Set bottom button color (`setColor(color)`).
89 ///
90 /// # Errors
91 /// Returns [`JsValue`] if the underlying JS call fails.
92 ///
93 /// # Examples
94 /// ```no_run
95 /// # use telegram_webapp_sdk::webapp::{TelegramWebApp, BottomButton};
96 /// # let app = TelegramWebApp::instance().unwrap();
97 /// let _ = app.set_bottom_button_color(BottomButton::Main, "#ff0000");
98 /// ```
99 pub fn set_bottom_button_color(
100 &self,
101 button: BottomButton,
102 color: &str
103 ) -> Result<(), JsValue> {
104 self.bottom_button_method(button, "setColor", Some(&color.into()))
105 }
106
107 /// Set bottom button text color (`setTextColor(color)`).
108 ///
109 /// # Errors
110 /// Returns [`JsValue`] if the underlying JS call fails.
111 ///
112 /// # Examples
113 /// ```no_run
114 /// # use telegram_webapp_sdk::webapp::{TelegramWebApp, BottomButton};
115 /// # let app = TelegramWebApp::instance().unwrap();
116 /// let _ = app.set_bottom_button_text_color(BottomButton::Main, "#ffffff");
117 /// ```
118 pub fn set_bottom_button_text_color(
119 &self,
120 button: BottomButton,
121 color: &str
122 ) -> Result<(), JsValue> {
123 self.bottom_button_method(button, "setTextColor", Some(&color.into()))
124 }
125
126 /// Enable a bottom button, allowing user interaction.
127 ///
128 /// # Examples
129 /// ```no_run
130 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
131 ///
132 /// if let Some(app) = TelegramWebApp::instance() {
133 /// let _ = app.enable_bottom_button(BottomButton::Main);
134 /// }
135 /// ```
136 pub fn enable_bottom_button(&self, button: BottomButton) -> Result<(), JsValue> {
137 self.bottom_button_method(button, "enable", None)
138 }
139
140 /// Disable a bottom button, preventing user interaction.
141 ///
142 /// # Examples
143 /// ```no_run
144 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
145 ///
146 /// if let Some(app) = TelegramWebApp::instance() {
147 /// let _ = app.disable_bottom_button(BottomButton::Main);
148 /// }
149 /// ```
150 pub fn disable_bottom_button(&self, button: BottomButton) -> Result<(), JsValue> {
151 self.bottom_button_method(button, "disable", None)
152 }
153
154 /// Show the circular loading indicator on a bottom button.
155 ///
156 /// # Examples
157 /// ```no_run
158 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
159 ///
160 /// if let Some(app) = TelegramWebApp::instance() {
161 /// let _ = app.show_bottom_button_progress(BottomButton::Main, false);
162 /// }
163 /// ```
164 pub fn show_bottom_button_progress(
165 &self,
166 button: BottomButton,
167 leave_active: bool
168 ) -> Result<(), JsValue> {
169 let leave_active = JsValue::from_bool(leave_active);
170 self.bottom_button_method(button, "showProgress", Some(&leave_active))
171 }
172
173 /// Hide the loading indicator on a bottom button.
174 ///
175 /// # Examples
176 /// ```no_run
177 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
178 ///
179 /// if let Some(app) = TelegramWebApp::instance() {
180 /// let _ = app.hide_bottom_button_progress(BottomButton::Main);
181 /// }
182 /// ```
183 pub fn hide_bottom_button_progress(&self, button: BottomButton) -> Result<(), JsValue> {
184 self.bottom_button_method(button, "hideProgress", None)
185 }
186
187 /// Returns whether the specified bottom button is currently visible.
188 ///
189 /// # Examples
190 /// ```no_run
191 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
192 ///
193 /// if let Some(app) = TelegramWebApp::instance() {
194 /// let _ = app.is_bottom_button_visible(BottomButton::Main);
195 /// }
196 /// ```
197 pub fn is_bottom_button_visible(&self, button: BottomButton) -> bool {
198 self.bottom_button_property(button, "isVisible")
199 .and_then(|v| v.as_bool())
200 .unwrap_or(false)
201 }
202
203 /// Returns whether the specified bottom button is active (enabled).
204 ///
205 /// # Examples
206 /// ```no_run
207 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
208 ///
209 /// if let Some(app) = TelegramWebApp::instance() {
210 /// let _ = app.is_bottom_button_active(BottomButton::Main);
211 /// }
212 /// ```
213 pub fn is_bottom_button_active(&self, button: BottomButton) -> bool {
214 self.bottom_button_property(button, "isActive")
215 .and_then(|v| v.as_bool())
216 .unwrap_or(false)
217 }
218
219 /// Returns whether the progress indicator is visible on the button.
220 ///
221 /// # Examples
222 /// ```no_run
223 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
224 ///
225 /// if let Some(app) = TelegramWebApp::instance() {
226 /// let _ = app.is_bottom_button_progress_visible(BottomButton::Main);
227 /// }
228 /// ```
229 pub fn is_bottom_button_progress_visible(&self, button: BottomButton) -> bool {
230 self.bottom_button_property(button, "isProgressVisible")
231 .and_then(|v| v.as_bool())
232 .unwrap_or(false)
233 }
234
235 /// Returns the current text displayed on the button.
236 ///
237 /// # Examples
238 /// ```no_run
239 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
240 ///
241 /// if let Some(app) = TelegramWebApp::instance() {
242 /// let _ = app.bottom_button_text(BottomButton::Main);
243 /// }
244 /// ```
245 pub fn bottom_button_text(&self, button: BottomButton) -> Option<String> {
246 self.bottom_button_property(button, "text")?.as_string()
247 }
248
249 /// Returns the current text color of the button.
250 ///
251 /// # Examples
252 /// ```no_run
253 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
254 ///
255 /// if let Some(app) = TelegramWebApp::instance() {
256 /// let _ = app.bottom_button_text_color(BottomButton::Main);
257 /// }
258 /// ```
259 pub fn bottom_button_text_color(&self, button: BottomButton) -> Option<String> {
260 self.bottom_button_property(button, "textColor")?
261 .as_string()
262 }
263
264 /// Returns the current background color of the button.
265 ///
266 /// # Examples
267 /// ```no_run
268 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
269 ///
270 /// if let Some(app) = TelegramWebApp::instance() {
271 /// let _ = app.bottom_button_color(BottomButton::Main);
272 /// }
273 /// ```
274 pub fn bottom_button_color(&self, button: BottomButton) -> Option<String> {
275 self.bottom_button_property(button, "color")?.as_string()
276 }
277
278 /// Returns whether the shine effect is enabled on the button.
279 ///
280 /// # Examples
281 /// ```no_run
282 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
283 ///
284 /// if let Some(app) = TelegramWebApp::instance() {
285 /// let _ = app.bottom_button_has_shine_effect(BottomButton::Main);
286 /// }
287 /// ```
288 pub fn bottom_button_has_shine_effect(&self, button: BottomButton) -> bool {
289 self.bottom_button_property(button, "hasShineEffect")
290 .and_then(|v| v.as_bool())
291 .unwrap_or(false)
292 }
293
294 /// Update bottom button state via `setParams`.
295 ///
296 /// # Examples
297 /// ```no_run
298 /// use telegram_webapp_sdk::webapp::{BottomButton, BottomButtonParams, TelegramWebApp};
299 ///
300 /// if let Some(app) = TelegramWebApp::instance() {
301 /// let params = BottomButtonParams {
302 /// text: Some("Send"),
303 /// ..Default::default()
304 /// };
305 /// let _ = app.set_bottom_button_params(BottomButton::Main, ¶ms);
306 /// }
307 /// ```
308 pub fn set_bottom_button_params(
309 &self,
310 button: BottomButton,
311 params: &BottomButtonParams<'_>
312 ) -> Result<(), JsValue> {
313 let value = to_value(params).map_err(|err| JsValue::from_str(&err.to_string()))?;
314 self.bottom_button_method(button, "setParams", Some(&value))
315 }
316
317 /// Update secondary button state via `setParams`, including position.
318 ///
319 /// # Examples
320 /// ```no_run
321 /// use telegram_webapp_sdk::webapp::{
322 /// SecondaryButtonParams, SecondaryButtonPosition, TelegramWebApp
323 /// };
324 ///
325 /// if let Some(app) = TelegramWebApp::instance() {
326 /// let params = SecondaryButtonParams {
327 /// position: Some(SecondaryButtonPosition::Left),
328 /// ..Default::default()
329 /// };
330 /// let _ = app.set_secondary_button_params(¶ms);
331 /// }
332 /// ```
333 pub fn set_secondary_button_params(
334 &self,
335 params: &SecondaryButtonParams<'_>
336 ) -> Result<(), JsValue> {
337 let value = to_value(params).map_err(|err| JsValue::from_str(&err.to_string()))?;
338 self.bottom_button_method(BottomButton::Secondary, "setParams", Some(&value))
339 }
340
341 /// Returns the configured position of the secondary button, if available.
342 ///
343 /// # Examples
344 /// ```no_run
345 /// use telegram_webapp_sdk::webapp::{SecondaryButtonPosition, TelegramWebApp};
346 ///
347 /// if let Some(app) = TelegramWebApp::instance() {
348 /// let _ = app.secondary_button_position();
349 /// }
350 /// ```
351 pub fn secondary_button_position(&self) -> Option<SecondaryButtonPosition> {
352 self.bottom_button_property(BottomButton::Secondary, "position")
353 .and_then(SecondaryButtonPosition::from_js_value)
354 }
355
356 /// Set callback for `onClick()` on a bottom button.
357 ///
358 /// Returns an [`EventHandle`] that can be used to remove the callback.
359 ///
360 /// # Errors
361 /// Returns [`JsValue`] if the underlying JS call fails.
362 pub fn set_bottom_button_callback<F>(
363 &self,
364 button: BottomButton,
365 callback: F
366 ) -> Result<EventHandle<dyn FnMut()>, JsValue>
367 where
368 F: 'static + Fn()
369 {
370 let btn_val = Reflect::get(&self.inner, &button.js_name().into())?;
371 let btn = btn_val.dyn_into::<Object>()?;
372 let cb = Closure::<dyn FnMut()>::new(callback);
373 let f = Reflect::get(&btn, &"onClick".into())?;
374 let func = f
375 .dyn_ref::<Function>()
376 .ok_or_else(|| JsValue::from_str("onClick is not a function"))?;
377 func.call1(&btn, cb.as_ref().unchecked_ref())?;
378 Ok(EventHandle::new(btn, "offClick", None, cb))
379 }
380
381 /// Remove previously set bottom button callback.
382 ///
383 /// # Errors
384 /// Returns [`JsValue`] if the underlying JS call fails.
385 pub fn remove_bottom_button_callback(
386 &self,
387 handle: EventHandle<dyn FnMut()>
388 ) -> Result<(), JsValue> {
389 handle.unregister()
390 }
391
392 // === Back button operations ===
393
394 /// Show back button.
395 ///
396 /// # Errors
397 /// Returns [`JsValue`] if the underlying JS call fails.
398 pub fn show_back_button(&self) -> Result<(), JsValue> {
399 self.call_nested0("BackButton", "show")
400 }
401
402 /// Hide back button.
403 ///
404 /// # Errors
405 /// Returns [`JsValue`] if the underlying JS call fails.
406 pub fn hide_back_button(&self) -> Result<(), JsValue> {
407 self.call_nested0("BackButton", "hide")
408 }
409
410 /// Registers a callback for the native back button.
411 ///
412 /// Returns an [`EventHandle`] that can be passed to
413 /// [`remove_back_button_callback`](Self::remove_back_button_callback).
414 ///
415 /// # Examples
416 /// ```no_run
417 /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
418 /// # let app = TelegramWebApp::instance().unwrap();
419 /// let handle = app.set_back_button_callback(|| {}).expect("callback");
420 /// app.remove_back_button_callback(handle).unwrap();
421 /// ```
422 ///
423 /// # Errors
424 /// Returns [`JsValue`] if the underlying JS call fails.
425 pub fn set_back_button_callback<F>(
426 &self,
427 callback: F
428 ) -> Result<EventHandle<dyn FnMut()>, JsValue>
429 where
430 F: 'static + Fn()
431 {
432 let back_button_val = Reflect::get(&self.inner, &"BackButton".into())?;
433 let back_button = back_button_val.dyn_into::<Object>()?;
434 let cb = Closure::<dyn FnMut()>::new(callback);
435 let f = Reflect::get(&back_button, &"onClick".into())?;
436 let func = f
437 .dyn_ref::<Function>()
438 .ok_or_else(|| JsValue::from_str("onClick is not a function"))?;
439 func.call1(&back_button, cb.as_ref().unchecked_ref())?;
440 Ok(EventHandle::new(back_button, "offClick", None, cb))
441 }
442
443 /// Remove previously set back button callback.
444 ///
445 /// # Errors
446 /// Returns [`JsValue`] if the underlying JS call fails.
447 pub fn remove_back_button_callback(
448 &self,
449 handle: EventHandle<dyn FnMut()>
450 ) -> Result<(), JsValue> {
451 handle.unregister()
452 }
453
454 /// Returns whether the native back button is visible.
455 ///
456 /// # Examples
457 /// ```no_run
458 /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
459 /// # let app = TelegramWebApp::instance().unwrap();
460 /// let _ = app.is_back_button_visible();
461 /// ```
462 pub fn is_back_button_visible(&self) -> bool {
463 Reflect::get(&self.inner, &"BackButton".into())
464 .ok()
465 .and_then(|bb| Reflect::get(&bb, &"isVisible".into()).ok())
466 .and_then(|v| v.as_bool())
467 .unwrap_or(false)
468 }
469
470 // === Legacy aliases for main button ===
471
472 /// Legacy alias for [`Self::show_bottom_button`] with
473 /// [`BottomButton::Main`].
474 pub fn show_main_button(&self) -> Result<(), JsValue> {
475 self.show_bottom_button(BottomButton::Main)
476 }
477
478 /// Legacy alias for [`Self::hide_bottom_button`] with
479 /// [`BottomButton::Main`].
480 pub fn hide_main_button(&self) -> Result<(), JsValue> {
481 self.hide_bottom_button(BottomButton::Main)
482 }
483
484 /// Legacy alias for [`Self::set_bottom_button_text`] with
485 /// [`BottomButton::Main`].
486 pub fn set_main_button_text(&self, text: &str) -> Result<(), JsValue> {
487 self.set_bottom_button_text(BottomButton::Main, text)
488 }
489
490 /// Legacy alias for [`Self::set_bottom_button_color`] with
491 /// [`BottomButton::Main`].
492 pub fn set_main_button_color(&self, color: &str) -> Result<(), JsValue> {
493 self.set_bottom_button_color(BottomButton::Main, color)
494 }
495
496 /// Legacy alias for [`Self::set_bottom_button_text_color`] with
497 /// [`BottomButton::Main`].
498 pub fn set_main_button_text_color(&self, color: &str) -> Result<(), JsValue> {
499 self.set_bottom_button_text_color(BottomButton::Main, color)
500 }
501
502 /// Enable the main bottom button.
503 ///
504 /// # Examples
505 /// ```no_run
506 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
507 ///
508 /// if let Some(app) = TelegramWebApp::instance() {
509 /// let _ = app.enable_main_button();
510 /// }
511 /// ```
512 pub fn enable_main_button(&self) -> Result<(), JsValue> {
513 self.enable_bottom_button(BottomButton::Main)
514 }
515
516 /// Disable the main bottom button.
517 ///
518 /// # Examples
519 /// ```no_run
520 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
521 ///
522 /// if let Some(app) = TelegramWebApp::instance() {
523 /// let _ = app.disable_main_button();
524 /// }
525 /// ```
526 pub fn disable_main_button(&self) -> Result<(), JsValue> {
527 self.disable_bottom_button(BottomButton::Main)
528 }
529
530 /// Show progress on the main bottom button.
531 ///
532 /// # Examples
533 /// ```no_run
534 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
535 ///
536 /// if let Some(app) = TelegramWebApp::instance() {
537 /// let _ = app.show_main_button_progress(false);
538 /// }
539 /// ```
540 pub fn show_main_button_progress(&self, leave_active: bool) -> Result<(), JsValue> {
541 self.show_bottom_button_progress(BottomButton::Main, leave_active)
542 }
543
544 /// Hide progress indicator from the main bottom button.
545 ///
546 /// # Examples
547 /// ```no_run
548 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
549 ///
550 /// if let Some(app) = TelegramWebApp::instance() {
551 /// let _ = app.hide_main_button_progress();
552 /// }
553 /// ```
554 pub fn hide_main_button_progress(&self) -> Result<(), JsValue> {
555 self.hide_bottom_button_progress(BottomButton::Main)
556 }
557
558 /// Update the main button state via
559 /// [`set_bottom_button_params`](Self::set_bottom_button_params).
560 pub fn set_main_button_params(&self, params: &BottomButtonParams<'_>) -> Result<(), JsValue> {
561 self.set_bottom_button_params(BottomButton::Main, params)
562 }
563
564 /// Legacy alias for [`Self::set_bottom_button_callback`] with
565 /// [`BottomButton::Main`].
566 pub fn set_main_button_callback<F>(
567 &self,
568 callback: F
569 ) -> Result<EventHandle<dyn FnMut()>, JsValue>
570 where
571 F: 'static + Fn()
572 {
573 self.set_bottom_button_callback(BottomButton::Main, callback)
574 }
575
576 /// Legacy alias for [`Self::remove_bottom_button_callback`].
577 pub fn remove_main_button_callback(
578 &self,
579 handle: EventHandle<dyn FnMut()>
580 ) -> Result<(), JsValue> {
581 self.remove_bottom_button_callback(handle)
582 }
583
584 // === Secondary button convenience methods ===
585
586 /// Show the secondary bottom button.
587 pub fn show_secondary_button(&self) -> Result<(), JsValue> {
588 self.show_bottom_button(BottomButton::Secondary)
589 }
590
591 /// Hide the secondary bottom button.
592 pub fn hide_secondary_button(&self) -> Result<(), JsValue> {
593 self.hide_bottom_button(BottomButton::Secondary)
594 }
595
596 /// Set text for the secondary bottom button.
597 pub fn set_secondary_button_text(&self, text: &str) -> Result<(), JsValue> {
598 self.set_bottom_button_text(BottomButton::Secondary, text)
599 }
600
601 /// Set color for the secondary bottom button.
602 pub fn set_secondary_button_color(&self, color: &str) -> Result<(), JsValue> {
603 self.set_bottom_button_color(BottomButton::Secondary, color)
604 }
605
606 /// Set text color for the secondary bottom button.
607 pub fn set_secondary_button_text_color(&self, color: &str) -> Result<(), JsValue> {
608 self.set_bottom_button_text_color(BottomButton::Secondary, color)
609 }
610
611 /// Enable the secondary bottom button.
612 ///
613 /// # Examples
614 /// ```no_run
615 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
616 ///
617 /// if let Some(app) = TelegramWebApp::instance() {
618 /// let _ = app.enable_secondary_button();
619 /// }
620 /// ```
621 pub fn enable_secondary_button(&self) -> Result<(), JsValue> {
622 self.enable_bottom_button(BottomButton::Secondary)
623 }
624
625 /// Disable the secondary bottom button.
626 ///
627 /// # Examples
628 /// ```no_run
629 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
630 ///
631 /// if let Some(app) = TelegramWebApp::instance() {
632 /// let _ = app.disable_secondary_button();
633 /// }
634 /// ```
635 pub fn disable_secondary_button(&self) -> Result<(), JsValue> {
636 self.disable_bottom_button(BottomButton::Secondary)
637 }
638
639 /// Show progress on the secondary bottom button.
640 ///
641 /// # Examples
642 /// ```no_run
643 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
644 ///
645 /// if let Some(app) = TelegramWebApp::instance() {
646 /// let _ = app.show_secondary_button_progress(false);
647 /// }
648 /// ```
649 pub fn show_secondary_button_progress(&self, leave_active: bool) -> Result<(), JsValue> {
650 self.show_bottom_button_progress(BottomButton::Secondary, leave_active)
651 }
652
653 /// Hide progress indicator from the secondary bottom button.
654 ///
655 /// # Examples
656 /// ```no_run
657 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
658 ///
659 /// if let Some(app) = TelegramWebApp::instance() {
660 /// let _ = app.hide_secondary_button_progress();
661 /// }
662 /// ```
663 pub fn hide_secondary_button_progress(&self) -> Result<(), JsValue> {
664 self.hide_bottom_button_progress(BottomButton::Secondary)
665 }
666
667 /// Set callback for the secondary bottom button.
668 pub fn set_secondary_button_callback<F>(
669 &self,
670 callback: F
671 ) -> Result<EventHandle<dyn FnMut()>, JsValue>
672 where
673 F: 'static + Fn()
674 {
675 self.set_bottom_button_callback(BottomButton::Secondary, callback)
676 }
677
678 /// Remove callback for the secondary bottom button.
679 pub fn remove_secondary_button_callback(
680 &self,
681 handle: EventHandle<dyn FnMut()>
682 ) -> Result<(), JsValue> {
683 self.remove_bottom_button_callback(handle)
684 }
685
686 /// Hide the on-screen keyboard.
687 /// Call `WebApp.hideKeyboard()`.
688 ///
689 /// # Examples
690 /// ```no_run
691 /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
692 /// # let app = TelegramWebApp::instance().unwrap();
693 /// app.hide_keyboard().unwrap();
694 /// ```
695 ///
696 /// # Errors
697 /// Returns [`JsValue`] if the underlying JS call fails.
698 pub fn hide_keyboard(&self) -> Result<(), JsValue> {
699 self.call0("hideKeyboard")
700 }
701}