telegram_webapp_sdk/api/
haptic.rs1use js_sys::{Function, Reflect};
2use wasm_bindgen::{JsCast, prelude::*};
3use web_sys::window;
4
5#[derive(Debug, Clone, Copy)]
7pub enum HapticImpactStyle {
8 Light,
10 Medium,
12 Heavy,
14 Rigid,
16 Soft
18}
19
20impl HapticImpactStyle {
21 const fn as_str(self) -> &'static str {
22 match self {
23 Self::Light => "light",
24 Self::Medium => "medium",
25 Self::Heavy => "heavy",
26 Self::Rigid => "rigid",
27 Self::Soft => "soft"
28 }
29 }
30}
31
32#[derive(Debug, Clone, Copy)]
34pub enum HapticNotificationType {
35 Error,
37 Success,
39 Warning
41}
42
43impl HapticNotificationType {
44 const fn as_str(self) -> &'static str {
45 match self {
46 Self::Error => "error",
47 Self::Success => "success",
48 Self::Warning => "warning"
49 }
50 }
51}
52
53pub fn impact_occurred(style: HapticImpactStyle) -> Result<(), JsValue> {
67 let haptic = haptic_object()?;
68 let func = Reflect::get(&haptic, &"impactOccurred".into())?.dyn_into::<Function>()?;
69 func.call1(&haptic, &JsValue::from_str(style.as_str()))?;
70 Ok(())
71}
72
73pub fn notification_occurred(ty: HapticNotificationType) -> Result<(), JsValue> {
87 let haptic = haptic_object()?;
88 let func = Reflect::get(&haptic, &"notificationOccurred".into())?.dyn_into::<Function>()?;
89 func.call1(&haptic, &JsValue::from_str(ty.as_str()))?;
90 Ok(())
91}
92
93pub fn selection_changed() -> Result<(), JsValue> {
107 let haptic = haptic_object()?;
108 let func = Reflect::get(&haptic, &"selectionChanged".into())?.dyn_into::<Function>()?;
109 func.call0(&haptic)?;
110 Ok(())
111}
112
113fn haptic_object() -> Result<JsValue, JsValue> {
115 let window = window().ok_or_else(|| JsValue::from_str("no window"))?;
116 let tg = Reflect::get(&window, &"Telegram".into())?;
117 let webapp = Reflect::get(&tg, &"WebApp".into())?;
118 Reflect::get(&webapp, &"HapticFeedback".into())
119}
120
121#[cfg(test)]
122mod tests {
123 use js_sys::{Object, Reflect};
124 use wasm_bindgen::prelude::*;
125 use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
126 use web_sys::window;
127
128 use super::*;
129
130 wasm_bindgen_test_configure!(run_in_browser);
131
132 #[allow(dead_code)]
133 fn setup_haptic() -> Object {
134 let win = window().unwrap();
135 let telegram = Object::new();
136 let webapp = Object::new();
137 let haptic = Object::new();
138 let _ = Reflect::set(&win, &"Telegram".into(), &telegram);
139 let _ = Reflect::set(&telegram, &"WebApp".into(), &webapp);
140 let _ = Reflect::set(&webapp, &"HapticFeedback".into(), &haptic);
141 haptic
142 }
143
144 #[wasm_bindgen_test]
145 #[allow(dead_code)]
146 fn impact_calls_js() {
147 let haptic = setup_haptic();
148 let _ = Reflect::set(&haptic, &"impact_called".into(), &JsValue::FALSE);
149 let haptic_clone = haptic.clone();
150 let closure = Closure::wrap(Box::new(move |_style: JsValue| {
151 let _ = Reflect::set(&haptic_clone, &"impact_called".into(), &JsValue::TRUE);
152 }) as Box<dyn FnMut(JsValue)>);
153 let _ = Reflect::set(&haptic, &"impactOccurred".into(), closure.as_ref());
154 closure.forget();
155 let _ = impact_occurred(HapticImpactStyle::Light);
156 let flag = Reflect::get(&haptic, &"impact_called".into()).unwrap();
157 assert!(flag.as_bool().unwrap());
158 }
159
160 #[wasm_bindgen_test]
161 #[allow(dead_code)]
162 fn notification_calls_js() {
163 let haptic = setup_haptic();
164 let _ = Reflect::set(&haptic, &"notification_called".into(), &JsValue::FALSE);
165 let haptic_clone = haptic.clone();
166 let closure = Closure::wrap(Box::new(move |_ty: JsValue| {
167 let _ = Reflect::set(&haptic_clone, &"notification_called".into(), &JsValue::TRUE);
168 }) as Box<dyn FnMut(JsValue)>);
169 let _ = Reflect::set(&haptic, &"notificationOccurred".into(), closure.as_ref());
170 closure.forget();
171 let _ = notification_occurred(HapticNotificationType::Error);
172 let flag = Reflect::get(&haptic, &"notification_called".into()).unwrap();
173 assert!(flag.as_bool().unwrap());
174 }
175
176 #[wasm_bindgen_test]
177 #[allow(dead_code)]
178 fn selection_calls_js() {
179 let haptic = setup_haptic();
180 let _ = Reflect::set(&haptic, &"selection_called".into(), &JsValue::FALSE);
181 let haptic_clone = haptic.clone();
182 let closure = Closure::wrap(Box::new(move || {
183 let _ = Reflect::set(&haptic_clone, &"selection_called".into(), &JsValue::TRUE);
184 }) as Box<dyn FnMut()>);
185 let _ = Reflect::set(&haptic, &"selectionChanged".into(), closure.as_ref());
186 closure.forget();
187 let _ = selection_changed();
188 let flag = Reflect::get(&haptic, &"selection_called".into()).unwrap();
189 assert!(flag.as_bool().unwrap());
190 }
191}