telegram_webapp_sdk/api/
accelerometer.rs

1use js_sys::{Function, Reflect};
2use wasm_bindgen::{JsCast, prelude::*};
3use web_sys::window;
4
5use super::events;
6
7/// Three-dimensional acceleration in meters per second squared.
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct Acceleration {
10    /// Acceleration along the X axis.
11    pub x: f64,
12    /// Acceleration along the Y axis.
13    pub y: f64,
14    /// Acceleration along the Z axis.
15    pub z: f64
16}
17
18impl Acceleration {
19    /// Creates a new [`Acceleration`] instance.
20    #[allow(dead_code)]
21    const fn new(x: f64, y: f64, z: f64) -> Self {
22        Self {
23            x,
24            y,
25            z
26        }
27    }
28}
29
30/// Starts the accelerometer.
31///
32/// # Errors
33/// Returns [`JsValue`] if the underlying JavaScript call fails or the sensor is
34/// unavailable.
35///
36/// # Examples
37/// ```no_run
38/// # use telegram_webapp_sdk::api::accelerometer::start;
39/// start()?;
40/// # Ok::<(), wasm_bindgen::JsValue>(())
41/// ```
42pub fn start() -> Result<(), JsValue> {
43    let accel = accelerometer_object()?;
44    let func = Reflect::get(&accel, &"start".into())?.dyn_into::<Function>()?;
45    func.call0(&accel)?;
46    Ok(())
47}
48
49/// Stops the accelerometer.
50///
51/// # Errors
52/// Returns [`JsValue`] if the underlying JavaScript call fails or the sensor is
53/// unavailable.
54///
55/// # Examples
56/// ```no_run
57/// # use telegram_webapp_sdk::api::accelerometer::stop;
58/// stop()?;
59/// # Ok::<(), wasm_bindgen::JsValue>(())
60/// ```
61pub fn stop() -> Result<(), JsValue> {
62    let accel = accelerometer_object()?;
63    let func = Reflect::get(&accel, &"stop".into())?.dyn_into::<Function>()?;
64    func.call0(&accel)?;
65    Ok(())
66}
67
68/// Reads the current acceleration values.
69///
70/// # Examples
71/// ```no_run
72/// # use telegram_webapp_sdk::api::accelerometer::get_acceleration;
73/// let _ = get_acceleration();
74/// ```
75pub fn get_acceleration() -> Option<Acceleration> {
76    let accel = accelerometer_object().ok()?;
77    let x = Reflect::get(&accel, &"x".into()).ok()?.as_f64()?;
78    let y = Reflect::get(&accel, &"y".into()).ok()?.as_f64()?;
79    let z = Reflect::get(&accel, &"z".into()).ok()?.as_f64()?;
80    Some(Acceleration {
81        x,
82        y,
83        z
84    })
85}
86
87/// Registers a callback for `accelerometerStarted` event.
88///
89/// ⚠️ The closure must be kept alive for as long as it is needed.
90pub fn on_started(callback: &Closure<dyn Fn()>) -> Result<(), JsValue> {
91    events::on_event("accelerometerStarted", callback)
92}
93
94/// Registers a callback for `accelerometerChanged` event.
95///
96/// ⚠️ The closure must be kept alive for as long as it is needed.
97pub fn on_changed(callback: &Closure<dyn Fn()>) -> Result<(), JsValue> {
98    events::on_event("accelerometerChanged", callback)
99}
100
101/// Registers a callback for `accelerometerStopped` event.
102///
103/// ⚠️ The closure must be kept alive for as long as it is needed.
104pub fn on_stopped(callback: &Closure<dyn Fn()>) -> Result<(), JsValue> {
105    events::on_event("accelerometerStopped", callback)
106}
107
108/// Registers a callback for `accelerometerFailed` event.
109///
110/// ⚠️ The closure must be kept alive for as long as it is needed.
111pub fn on_failed(callback: &Closure<dyn Fn()>) -> Result<(), JsValue> {
112    events::on_event("accelerometerFailed", callback)
113}
114
115fn accelerometer_object() -> Result<JsValue, JsValue> {
116    let win = window().ok_or_else(|| JsValue::from_str("no window"))?;
117    let tg = Reflect::get(&win, &"Telegram".into())?;
118    let webapp = Reflect::get(&tg, &"WebApp".into())?;
119    Reflect::get(&webapp, &"Accelerometer".into())
120}
121
122#[cfg(test)]
123#[allow(dead_code)]
124mod tests {
125    use js_sys::{Function, Object, Reflect};
126    use wasm_bindgen::{JsValue, closure::Closure};
127    use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
128    use web_sys::window;
129
130    use super::*;
131
132    wasm_bindgen_test_configure!(run_in_browser);
133
134    #[allow(dead_code)]
135    fn setup_accelerometer() -> (Object, Object) {
136        let win = window().unwrap();
137        let telegram = Object::new();
138        let webapp = Object::new();
139        let accel = Object::new();
140        let _ = Reflect::set(&win, &"Telegram".into(), &telegram);
141        let _ = Reflect::set(&telegram, &"WebApp".into(), &webapp);
142        let _ = Reflect::set(&webapp, &"Accelerometer".into(), &accel);
143        (webapp, accel)
144    }
145
146    #[wasm_bindgen_test]
147    #[allow(clippy::unused_unit)]
148    fn start_ok() {
149        let (_webapp, accel) = setup_accelerometer();
150        let func = Function::new_no_args("this.called = true;");
151        let _ = Reflect::set(&accel, &"start".into(), &func);
152        assert!(start().is_ok());
153        let called = Reflect::get(&accel, &"called".into()).unwrap();
154        assert_eq!(called.as_bool(), Some(true));
155    }
156
157    #[wasm_bindgen_test]
158    #[allow(clippy::unused_unit)]
159    fn start_err() {
160        let (_webapp, accel) = setup_accelerometer();
161        let _ = Reflect::set(&accel, &"start".into(), &JsValue::from_f64(1.0));
162        assert!(start().is_err());
163    }
164
165    #[wasm_bindgen_test]
166    #[allow(clippy::unused_unit)]
167    fn stop_ok() {
168        let (_webapp, accel) = setup_accelerometer();
169        let func = Function::new_no_args("this.stopped = true;");
170        let _ = Reflect::set(&accel, &"stop".into(), &func);
171        assert!(stop().is_ok());
172        let stopped = Reflect::get(&accel, &"stopped".into()).unwrap();
173        assert_eq!(stopped.as_bool(), Some(true));
174    }
175
176    #[wasm_bindgen_test]
177    fn get_acceleration_ok() {
178        let (_webapp, accel) = setup_accelerometer();
179        let _ = Reflect::set(&accel, &"x".into(), &JsValue::from_f64(1.0));
180        let _ = Reflect::set(&accel, &"y".into(), &JsValue::from_f64(2.0));
181        let _ = Reflect::set(&accel, &"z".into(), &JsValue::from_f64(3.0));
182        let result = get_acceleration().unwrap();
183        assert_eq!(
184            result,
185            Acceleration {
186                x: 1.0,
187                y: 2.0,
188                z: 3.0
189            }
190        );
191    }
192
193    #[wasm_bindgen_test]
194    fn registers_callbacks() {
195        let (webapp, _accel) = setup_accelerometer();
196        let on_event = Function::new_with_args("name, cb", "this[name] = cb;");
197        let _ = Reflect::set(&webapp, &"onEvent".into(), &on_event);
198        let cb = Closure::wrap(Box::new(|| {}) as Box<dyn Fn()>);
199        on_started(&cb).expect("on_started");
200        on_changed(&cb).expect("on_changed");
201        on_stopped(&cb).expect("on_stopped");
202        on_failed(&cb).expect("on_failed");
203        assert!(Reflect::has(&webapp, &"accelerometerStarted".into()).unwrap());
204        assert!(Reflect::has(&webapp, &"accelerometerChanged".into()).unwrap());
205        assert!(Reflect::has(&webapp, &"accelerometerStopped".into()).unwrap());
206        assert!(Reflect::has(&webapp, &"accelerometerFailed".into()).unwrap());
207        cb.forget();
208    }
209}