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