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 /// Set custom emoji icon for a bottom button (Bot API 9.5+).
127 ///
128 /// Sets the custom emoji ID to be displayed as an icon on the button.
129 /// Use an empty string to remove the icon.
130 ///
131 /// # Arguments
132 /// * `button` - The button to update (Main or Secondary)
133 /// * `icon_id` - The custom emoji ID (e.g., "123456789")
134 ///
135 /// # Errors
136 /// Returns [`JsValue`] if the underlying JS call fails.
137 ///
138 /// # Examples
139 /// ```no_run
140 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
141 ///
142 /// if let Some(app) = TelegramWebApp::instance() {
143 /// let _ = app.set_bottom_button_icon_custom_emoji_id(BottomButton::Main, "123456789");
144 /// }
145 /// ```
146 pub fn set_bottom_button_icon_custom_emoji_id(
147 &self,
148 button: BottomButton,
149 icon_id: &str
150 ) -> Result<(), JsValue> {
151 self.bottom_button_method(button, "setIconCustomEmojiId", Some(&icon_id.into()))
152 }
153
154 /// Enable a bottom button, allowing user interaction.
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.enable_bottom_button(BottomButton::Main);
162 /// }
163 /// ```
164 pub fn enable_bottom_button(&self, button: BottomButton) -> Result<(), JsValue> {
165 self.bottom_button_method(button, "enable", None)
166 }
167
168 /// Disable a bottom button, preventing user interaction.
169 ///
170 /// # Examples
171 /// ```no_run
172 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
173 ///
174 /// if let Some(app) = TelegramWebApp::instance() {
175 /// let _ = app.disable_bottom_button(BottomButton::Main);
176 /// }
177 /// ```
178 pub fn disable_bottom_button(&self, button: BottomButton) -> Result<(), JsValue> {
179 self.bottom_button_method(button, "disable", None)
180 }
181
182 /// Show the circular loading indicator on a bottom button.
183 ///
184 /// # Examples
185 /// ```no_run
186 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
187 ///
188 /// if let Some(app) = TelegramWebApp::instance() {
189 /// let _ = app.show_bottom_button_progress(BottomButton::Main, false);
190 /// }
191 /// ```
192 pub fn show_bottom_button_progress(
193 &self,
194 button: BottomButton,
195 leave_active: bool
196 ) -> Result<(), JsValue> {
197 let leave_active = JsValue::from_bool(leave_active);
198 self.bottom_button_method(button, "showProgress", Some(&leave_active))
199 }
200
201 /// Hide the loading indicator on a bottom button.
202 ///
203 /// # Examples
204 /// ```no_run
205 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
206 ///
207 /// if let Some(app) = TelegramWebApp::instance() {
208 /// let _ = app.hide_bottom_button_progress(BottomButton::Main);
209 /// }
210 /// ```
211 pub fn hide_bottom_button_progress(&self, button: BottomButton) -> Result<(), JsValue> {
212 self.bottom_button_method(button, "hideProgress", None)
213 }
214
215 /// Returns whether the specified bottom button is currently visible.
216 ///
217 /// # Examples
218 /// ```no_run
219 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
220 ///
221 /// if let Some(app) = TelegramWebApp::instance() {
222 /// let _ = app.is_bottom_button_visible(BottomButton::Main);
223 /// }
224 /// ```
225 pub fn is_bottom_button_visible(&self, button: BottomButton) -> bool {
226 self.bottom_button_property(button, "isVisible")
227 .and_then(|v| v.as_bool())
228 .unwrap_or(false)
229 }
230
231 /// Returns whether the specified bottom button is active (enabled).
232 ///
233 /// # Examples
234 /// ```no_run
235 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
236 ///
237 /// if let Some(app) = TelegramWebApp::instance() {
238 /// let _ = app.is_bottom_button_active(BottomButton::Main);
239 /// }
240 /// ```
241 pub fn is_bottom_button_active(&self, button: BottomButton) -> bool {
242 self.bottom_button_property(button, "isActive")
243 .and_then(|v| v.as_bool())
244 .unwrap_or(false)
245 }
246
247 /// Returns whether the progress indicator is visible on the button.
248 ///
249 /// # Examples
250 /// ```no_run
251 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
252 ///
253 /// if let Some(app) = TelegramWebApp::instance() {
254 /// let _ = app.is_bottom_button_progress_visible(BottomButton::Main);
255 /// }
256 /// ```
257 pub fn is_bottom_button_progress_visible(&self, button: BottomButton) -> bool {
258 self.bottom_button_property(button, "isProgressVisible")
259 .and_then(|v| v.as_bool())
260 .unwrap_or(false)
261 }
262
263 /// Returns the current text displayed on the button.
264 ///
265 /// # Examples
266 /// ```no_run
267 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
268 ///
269 /// if let Some(app) = TelegramWebApp::instance() {
270 /// let _ = app.bottom_button_text(BottomButton::Main);
271 /// }
272 /// ```
273 pub fn bottom_button_text(&self, button: BottomButton) -> Option<String> {
274 self.bottom_button_property(button, "text")?.as_string()
275 }
276
277 /// Returns the current custom emoji icon ID of the button (Bot API 9.5+).
278 ///
279 /// # Arguments
280 /// * `button` - The button to query (Main or Secondary)
281 ///
282 /// # Examples
283 /// ```no_run
284 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
285 ///
286 /// if let Some(app) = TelegramWebApp::instance() {
287 /// if let Some(icon_id) = app.bottom_button_icon_custom_emoji_id(BottomButton::Main) {
288 /// println!("Icon ID: {}", icon_id);
289 /// }
290 /// }
291 /// ```
292 pub fn bottom_button_icon_custom_emoji_id(&self, button: BottomButton) -> Option<String> {
293 self.bottom_button_property(button, "iconCustomEmojiId")?
294 .as_string()
295 }
296
297 /// Returns the current text color of the button.
298 ///
299 /// # Examples
300 /// ```no_run
301 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
302 ///
303 /// if let Some(app) = TelegramWebApp::instance() {
304 /// let _ = app.bottom_button_text_color(BottomButton::Main);
305 /// }
306 /// ```
307 pub fn bottom_button_text_color(&self, button: BottomButton) -> Option<String> {
308 self.bottom_button_property(button, "textColor")?
309 .as_string()
310 }
311
312 /// Returns the current background color of the button.
313 ///
314 /// # Examples
315 /// ```no_run
316 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
317 ///
318 /// if let Some(app) = TelegramWebApp::instance() {
319 /// let _ = app.bottom_button_color(BottomButton::Main);
320 /// }
321 /// ```
322 pub fn bottom_button_color(&self, button: BottomButton) -> Option<String> {
323 self.bottom_button_property(button, "color")?.as_string()
324 }
325
326 /// Returns whether the shine effect is enabled on the button.
327 ///
328 /// # Examples
329 /// ```no_run
330 /// use telegram_webapp_sdk::webapp::{BottomButton, TelegramWebApp};
331 ///
332 /// if let Some(app) = TelegramWebApp::instance() {
333 /// let _ = app.bottom_button_has_shine_effect(BottomButton::Main);
334 /// }
335 /// ```
336 pub fn bottom_button_has_shine_effect(&self, button: BottomButton) -> bool {
337 self.bottom_button_property(button, "hasShineEffect")
338 .and_then(|v| v.as_bool())
339 .unwrap_or(false)
340 }
341
342 /// Update bottom button state via `setParams`.
343 ///
344 /// # Examples
345 /// ```no_run
346 /// use telegram_webapp_sdk::webapp::{BottomButton, BottomButtonParams, TelegramWebApp};
347 ///
348 /// if let Some(app) = TelegramWebApp::instance() {
349 /// let params = BottomButtonParams {
350 /// text: Some("Send"),
351 /// ..Default::default()
352 /// };
353 /// let _ = app.set_bottom_button_params(BottomButton::Main, ¶ms);
354 /// }
355 /// ```
356 pub fn set_bottom_button_params(
357 &self,
358 button: BottomButton,
359 params: &BottomButtonParams<'_>
360 ) -> Result<(), JsValue> {
361 let value = to_value(params).map_err(|err| JsValue::from_str(&err.to_string()))?;
362 self.bottom_button_method(button, "setParams", Some(&value))
363 }
364
365 /// Update secondary button state via `setParams`, including position.
366 ///
367 /// # Examples
368 /// ```no_run
369 /// use telegram_webapp_sdk::webapp::{
370 /// SecondaryButtonParams, SecondaryButtonPosition, TelegramWebApp
371 /// };
372 ///
373 /// if let Some(app) = TelegramWebApp::instance() {
374 /// let params = SecondaryButtonParams {
375 /// position: Some(SecondaryButtonPosition::Left),
376 /// ..Default::default()
377 /// };
378 /// let _ = app.set_secondary_button_params(¶ms);
379 /// }
380 /// ```
381 pub fn set_secondary_button_params(
382 &self,
383 params: &SecondaryButtonParams<'_>
384 ) -> Result<(), JsValue> {
385 let value = to_value(params).map_err(|err| JsValue::from_str(&err.to_string()))?;
386 self.bottom_button_method(BottomButton::Secondary, "setParams", Some(&value))
387 }
388
389 /// Returns the configured position of the secondary button, if available.
390 ///
391 /// # Examples
392 /// ```no_run
393 /// use telegram_webapp_sdk::webapp::{SecondaryButtonPosition, TelegramWebApp};
394 ///
395 /// if let Some(app) = TelegramWebApp::instance() {
396 /// let _ = app.secondary_button_position();
397 /// }
398 /// ```
399 pub fn secondary_button_position(&self) -> Option<SecondaryButtonPosition> {
400 self.bottom_button_property(BottomButton::Secondary, "position")
401 .and_then(SecondaryButtonPosition::from_js_value)
402 }
403
404 /// Set callback for `onClick()` on a bottom button.
405 ///
406 /// Returns an [`EventHandle`] that can be used to remove the callback.
407 ///
408 /// # Errors
409 /// Returns [`JsValue`] if the underlying JS call fails.
410 pub fn set_bottom_button_callback<F>(
411 &self,
412 button: BottomButton,
413 callback: F
414 ) -> Result<EventHandle<dyn FnMut()>, JsValue>
415 where
416 F: 'static + Fn()
417 {
418 let btn_val = Reflect::get(&self.inner, &button.js_name().into())?;
419 let btn = btn_val.dyn_into::<Object>()?;
420 let cb = Closure::<dyn FnMut()>::new(callback);
421 let f = Reflect::get(&btn, &"onClick".into())?;
422 let func = f
423 .dyn_ref::<Function>()
424 .ok_or_else(|| JsValue::from_str("onClick is not a function"))?;
425 func.call1(&btn, cb.as_ref().unchecked_ref())?;
426 Ok(EventHandle::new(btn, "offClick", None, cb))
427 }
428
429 /// Remove previously set bottom button callback.
430 ///
431 /// # Errors
432 /// Returns [`JsValue`] if the underlying JS call fails.
433 pub fn remove_bottom_button_callback(
434 &self,
435 handle: EventHandle<dyn FnMut()>
436 ) -> Result<(), JsValue> {
437 handle.unregister()
438 }
439
440 // === Back button operations ===
441
442 /// Show back button.
443 ///
444 /// # Errors
445 /// Returns [`JsValue`] if the underlying JS call fails.
446 pub fn show_back_button(&self) -> Result<(), JsValue> {
447 self.call_nested0("BackButton", "show")
448 }
449
450 /// Hide back button.
451 ///
452 /// # Errors
453 /// Returns [`JsValue`] if the underlying JS call fails.
454 pub fn hide_back_button(&self) -> Result<(), JsValue> {
455 self.call_nested0("BackButton", "hide")
456 }
457
458 /// Registers a callback for the native back button.
459 ///
460 /// Returns an [`EventHandle`] that can be passed to
461 /// [`remove_back_button_callback`](Self::remove_back_button_callback).
462 ///
463 /// # Examples
464 /// ```no_run
465 /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
466 /// # let app = TelegramWebApp::instance().unwrap();
467 /// let handle = app.set_back_button_callback(|| {}).expect("callback");
468 /// app.remove_back_button_callback(handle).unwrap();
469 /// ```
470 ///
471 /// # Errors
472 /// Returns [`JsValue`] if the underlying JS call fails.
473 pub fn set_back_button_callback<F>(
474 &self,
475 callback: F
476 ) -> Result<EventHandle<dyn FnMut()>, JsValue>
477 where
478 F: 'static + Fn()
479 {
480 let back_button_val = Reflect::get(&self.inner, &"BackButton".into())?;
481 let back_button = back_button_val.dyn_into::<Object>()?;
482 let cb = Closure::<dyn FnMut()>::new(callback);
483 let f = Reflect::get(&back_button, &"onClick".into())?;
484 let func = f
485 .dyn_ref::<Function>()
486 .ok_or_else(|| JsValue::from_str("onClick is not a function"))?;
487 func.call1(&back_button, cb.as_ref().unchecked_ref())?;
488 Ok(EventHandle::new(back_button, "offClick", None, cb))
489 }
490
491 /// Remove previously set back button callback.
492 ///
493 /// # Errors
494 /// Returns [`JsValue`] if the underlying JS call fails.
495 pub fn remove_back_button_callback(
496 &self,
497 handle: EventHandle<dyn FnMut()>
498 ) -> Result<(), JsValue> {
499 handle.unregister()
500 }
501
502 /// Returns whether the native back button is visible.
503 ///
504 /// # Examples
505 /// ```no_run
506 /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
507 /// # let app = TelegramWebApp::instance().unwrap();
508 /// let _ = app.is_back_button_visible();
509 /// ```
510 pub fn is_back_button_visible(&self) -> bool {
511 Reflect::get(&self.inner, &"BackButton".into())
512 .ok()
513 .and_then(|bb| Reflect::get(&bb, &"isVisible".into()).ok())
514 .and_then(|v| v.as_bool())
515 .unwrap_or(false)
516 }
517
518 // === Legacy aliases for main button ===
519
520 /// Legacy alias for [`Self::show_bottom_button`] with
521 /// [`BottomButton::Main`].
522 pub fn show_main_button(&self) -> Result<(), JsValue> {
523 self.show_bottom_button(BottomButton::Main)
524 }
525
526 /// Legacy alias for [`Self::hide_bottom_button`] with
527 /// [`BottomButton::Main`].
528 pub fn hide_main_button(&self) -> Result<(), JsValue> {
529 self.hide_bottom_button(BottomButton::Main)
530 }
531
532 /// Legacy alias for [`Self::set_bottom_button_text`] with
533 /// [`BottomButton::Main`].
534 pub fn set_main_button_text(&self, text: &str) -> Result<(), JsValue> {
535 self.set_bottom_button_text(BottomButton::Main, text)
536 }
537
538 /// Legacy alias for [`Self::set_bottom_button_color`] with
539 /// [`BottomButton::Main`].
540 pub fn set_main_button_color(&self, color: &str) -> Result<(), JsValue> {
541 self.set_bottom_button_color(BottomButton::Main, color)
542 }
543
544 /// Legacy alias for [`Self::set_bottom_button_text_color`] with
545 /// [`BottomButton::Main`].
546 pub fn set_main_button_text_color(&self, color: &str) -> Result<(), JsValue> {
547 self.set_bottom_button_text_color(BottomButton::Main, color)
548 }
549
550 /// Set custom emoji icon for the main button (Bot API 9.5+).
551 ///
552 /// Legacy alias for [`Self::set_bottom_button_icon_custom_emoji_id`] with
553 /// [`BottomButton::Main`].
554 ///
555 /// # Examples
556 /// ```no_run
557 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
558 ///
559 /// if let Some(app) = TelegramWebApp::instance() {
560 /// let _ = app.set_main_button_icon_custom_emoji_id("123456789");
561 /// }
562 /// ```
563 pub fn set_main_button_icon_custom_emoji_id(&self, icon_id: &str) -> Result<(), JsValue> {
564 self.set_bottom_button_icon_custom_emoji_id(BottomButton::Main, icon_id)
565 }
566
567 /// Enable the main bottom button.
568 ///
569 /// # Examples
570 /// ```no_run
571 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
572 ///
573 /// if let Some(app) = TelegramWebApp::instance() {
574 /// let _ = app.enable_main_button();
575 /// }
576 /// ```
577 pub fn enable_main_button(&self) -> Result<(), JsValue> {
578 self.enable_bottom_button(BottomButton::Main)
579 }
580
581 /// Disable the main bottom button.
582 ///
583 /// # Examples
584 /// ```no_run
585 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
586 ///
587 /// if let Some(app) = TelegramWebApp::instance() {
588 /// let _ = app.disable_main_button();
589 /// }
590 /// ```
591 pub fn disable_main_button(&self) -> Result<(), JsValue> {
592 self.disable_bottom_button(BottomButton::Main)
593 }
594
595 /// Show progress on the main bottom button.
596 ///
597 /// # Examples
598 /// ```no_run
599 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
600 ///
601 /// if let Some(app) = TelegramWebApp::instance() {
602 /// let _ = app.show_main_button_progress(false);
603 /// }
604 /// ```
605 pub fn show_main_button_progress(&self, leave_active: bool) -> Result<(), JsValue> {
606 self.show_bottom_button_progress(BottomButton::Main, leave_active)
607 }
608
609 /// Hide progress indicator from the main bottom button.
610 ///
611 /// # Examples
612 /// ```no_run
613 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
614 ///
615 /// if let Some(app) = TelegramWebApp::instance() {
616 /// let _ = app.hide_main_button_progress();
617 /// }
618 /// ```
619 pub fn hide_main_button_progress(&self) -> Result<(), JsValue> {
620 self.hide_bottom_button_progress(BottomButton::Main)
621 }
622
623 /// Update the main button state via
624 /// [`set_bottom_button_params`](Self::set_bottom_button_params).
625 pub fn set_main_button_params(&self, params: &BottomButtonParams<'_>) -> Result<(), JsValue> {
626 self.set_bottom_button_params(BottomButton::Main, params)
627 }
628
629 /// Legacy alias for [`Self::set_bottom_button_callback`] with
630 /// [`BottomButton::Main`].
631 pub fn set_main_button_callback<F>(
632 &self,
633 callback: F
634 ) -> Result<EventHandle<dyn FnMut()>, JsValue>
635 where
636 F: 'static + Fn()
637 {
638 self.set_bottom_button_callback(BottomButton::Main, callback)
639 }
640
641 /// Remove callback for the main button.
642 ///
643 /// Legacy alias for [`Self::remove_bottom_button_callback`].
644 pub fn remove_main_button_callback(
645 &self,
646 handle: EventHandle<dyn FnMut()>
647 ) -> Result<(), JsValue> {
648 self.remove_bottom_button_callback(handle)
649 }
650
651 // === Secondary button convenience methods ===
652
653 /// Show the secondary bottom button.
654 pub fn show_secondary_button(&self) -> Result<(), JsValue> {
655 self.show_bottom_button(BottomButton::Secondary)
656 }
657
658 /// Hide the secondary bottom button.
659 pub fn hide_secondary_button(&self) -> Result<(), JsValue> {
660 self.hide_bottom_button(BottomButton::Secondary)
661 }
662
663 /// Set text for the secondary bottom button.
664 pub fn set_secondary_button_text(&self, text: &str) -> Result<(), JsValue> {
665 self.set_bottom_button_text(BottomButton::Secondary, text)
666 }
667
668 /// Set color for the secondary bottom button.
669 pub fn set_secondary_button_color(&self, color: &str) -> Result<(), JsValue> {
670 self.set_bottom_button_color(BottomButton::Secondary, color)
671 }
672
673 /// Set text color for the secondary bottom button.
674 pub fn set_secondary_button_text_color(&self, color: &str) -> Result<(), JsValue> {
675 self.set_bottom_button_text_color(BottomButton::Secondary, color)
676 }
677
678 /// Set custom emoji icon for the secondary button (Bot API 9.5+).
679 ///
680 /// Convenience alias for [`Self::set_bottom_button_icon_custom_emoji_id`]
681 /// with [`BottomButton::Secondary`].
682 ///
683 /// # Examples
684 /// ```no_run
685 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
686 ///
687 /// if let Some(app) = TelegramWebApp::instance() {
688 /// let _ = app.set_secondary_button_icon_custom_emoji_id("123456789");
689 /// }
690 /// ```
691 pub fn set_secondary_button_icon_custom_emoji_id(&self, icon_id: &str) -> Result<(), JsValue> {
692 self.set_bottom_button_icon_custom_emoji_id(BottomButton::Secondary, icon_id)
693 }
694
695 /// Enable the secondary bottom button.
696 ///
697 /// # Examples
698 /// ```no_run
699 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
700 ///
701 /// if let Some(app) = TelegramWebApp::instance() {
702 /// let _ = app.enable_secondary_button();
703 /// }
704 /// ```
705 pub fn enable_secondary_button(&self) -> Result<(), JsValue> {
706 self.enable_bottom_button(BottomButton::Secondary)
707 }
708
709 /// Disable the secondary bottom button.
710 ///
711 /// # Examples
712 /// ```no_run
713 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
714 ///
715 /// if let Some(app) = TelegramWebApp::instance() {
716 /// let _ = app.disable_secondary_button();
717 /// }
718 /// ```
719 pub fn disable_secondary_button(&self) -> Result<(), JsValue> {
720 self.disable_bottom_button(BottomButton::Secondary)
721 }
722
723 /// Show progress on the secondary bottom button.
724 ///
725 /// # Examples
726 /// ```no_run
727 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
728 ///
729 /// if let Some(app) = TelegramWebApp::instance() {
730 /// let _ = app.show_secondary_button_progress(false);
731 /// }
732 /// ```
733 pub fn show_secondary_button_progress(&self, leave_active: bool) -> Result<(), JsValue> {
734 self.show_bottom_button_progress(BottomButton::Secondary, leave_active)
735 }
736
737 /// Hide progress indicator from the secondary bottom button.
738 ///
739 /// # Examples
740 /// ```no_run
741 /// use telegram_webapp_sdk::webapp::TelegramWebApp;
742 ///
743 /// if let Some(app) = TelegramWebApp::instance() {
744 /// let _ = app.hide_secondary_button_progress();
745 /// }
746 /// ```
747 pub fn hide_secondary_button_progress(&self) -> Result<(), JsValue> {
748 self.hide_bottom_button_progress(BottomButton::Secondary)
749 }
750
751 /// Set callback for the secondary bottom button.
752 pub fn set_secondary_button_callback<F>(
753 &self,
754 callback: F
755 ) -> Result<EventHandle<dyn FnMut()>, JsValue>
756 where
757 F: 'static + Fn()
758 {
759 self.set_bottom_button_callback(BottomButton::Secondary, callback)
760 }
761
762 /// Remove callback for the secondary bottom button.
763 pub fn remove_secondary_button_callback(
764 &self,
765 handle: EventHandle<dyn FnMut()>
766 ) -> Result<(), JsValue> {
767 self.remove_bottom_button_callback(handle)
768 }
769
770 /// Hide the on-screen keyboard.
771 /// Call `WebApp.hideKeyboard()`.
772 ///
773 /// # Examples
774 /// ```no_run
775 /// # use telegram_webapp_sdk::webapp::TelegramWebApp;
776 /// # let app = TelegramWebApp::instance().unwrap();
777 /// app.hide_keyboard().unwrap();
778 /// ```
779 ///
780 /// # Errors
781 /// Returns [`JsValue`] if the underlying JS call fails.
782 pub fn hide_keyboard(&self) -> Result<(), JsValue> {
783 self.call0("hideKeyboard")
784 }
785}