telegram_webapp_sdk/api/
device_storage.rs1use js_sys::{Function, Promise, Reflect};
2use wasm_bindgen::{JsCast, JsValue};
3use wasm_bindgen_futures::JsFuture;
4use web_sys::window;
5
6pub async fn set(key: &str, value: &str) -> Result<(), JsValue> {
20 let storage = device_storage_object()?;
21 let func = Reflect::get(&storage, &JsValue::from_str("set"))?.dyn_into::<Function>()?;
22 let promise = func
23 .call2(&storage, &JsValue::from_str(key), &JsValue::from_str(value))?
24 .dyn_into::<Promise>()?;
25 JsFuture::from(promise).await?;
26 Ok(())
27}
28
29pub async fn get(key: &str) -> Result<Option<String>, JsValue> {
45 let storage = device_storage_object()?;
46 let func = Reflect::get(&storage, &JsValue::from_str("get"))?.dyn_into::<Function>()?;
47 let promise = func
48 .call1(&storage, &JsValue::from_str(key))?
49 .dyn_into::<Promise>()?;
50 let value = JsFuture::from(promise).await?;
51 Ok(value.as_string())
52}
53
54pub async fn remove(key: &str) -> Result<(), JsValue> {
69 let storage = device_storage_object()?;
70 let func = Reflect::get(&storage, &JsValue::from_str("remove"))?.dyn_into::<Function>()?;
71 let promise = func
72 .call1(&storage, &JsValue::from_str(key))?
73 .dyn_into::<Promise>()?;
74 JsFuture::from(promise).await?;
75 Ok(())
76}
77
78pub async fn clear() -> Result<(), JsValue> {
93 let storage = device_storage_object()?;
94 let func = Reflect::get(&storage, &JsValue::from_str("clear"))?.dyn_into::<Function>()?;
95 let promise = func.call0(&storage)?.dyn_into::<Promise>()?;
96 JsFuture::from(promise).await?;
97 Ok(())
98}
99
100fn device_storage_object() -> Result<JsValue, JsValue> {
101 let window = window().ok_or_else(|| JsValue::from_str("no window"))?;
102 let tg = Reflect::get(&window, &JsValue::from_str("Telegram"))?;
103 let webapp = Reflect::get(&tg, &JsValue::from_str("WebApp"))?;
104 Reflect::get(&webapp, &JsValue::from_str("deviceStorage"))
105}
106
107#[cfg(test)]
108mod tests {
109 use js_sys::{Function, Object, Reflect};
110 use wasm_bindgen::prelude::*;
111 use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
112 use web_sys::window;
113
114 use super::*;
115
116 wasm_bindgen_test_configure!(run_in_browser);
117
118 #[allow(dead_code)]
119 fn setup_device_storage() -> Object {
120 let win = window().unwrap();
121 let telegram = Object::new();
122 let webapp = Object::new();
123 let storage = Object::new();
124 let _ = Reflect::set(&win, &"Telegram".into(), &telegram);
125 let _ = Reflect::set(&telegram, &"WebApp".into(), &webapp);
126 let _ = Reflect::set(&webapp, &"deviceStorage".into(), &storage);
127 storage
128 }
129
130 #[wasm_bindgen_test(async)]
131 #[allow(dead_code)]
132 async fn set_calls_js() {
133 let storage = setup_device_storage();
134 let func = Function::new_with_args("k,v", "this[k] = v; return Promise.resolve();");
135 let _ = Reflect::set(&storage, &"set".into(), &func);
136 assert!(set("a", "b").await.is_ok());
137 let val = Reflect::get(&storage, &"a".into()).unwrap();
138 assert_eq!(val.as_string().as_deref(), Some("b"));
139 }
140
141 #[wasm_bindgen_test(async)]
142 #[allow(dead_code)]
143 async fn set_err() {
144 assert!(set("a", "b").await.is_err());
145 }
146
147 #[wasm_bindgen_test(async)]
148 #[allow(dead_code)]
149 async fn get_calls_js() {
150 let storage = setup_device_storage();
151 let func = Function::new_with_args("k", "return this[k];");
152 let _ = Reflect::set(&storage, &"get".into(), &func);
153 let _ = Reflect::set(&storage, &"a".into(), &JsValue::from_str("b"));
154 let value = get("a").await.unwrap();
155 assert_eq!(value.as_deref(), Some("b"));
156 }
157
158 #[wasm_bindgen_test(async)]
159 #[allow(dead_code)]
160 async fn get_err() {
161 assert!(get("a").await.is_err());
162 }
163
164 #[wasm_bindgen_test(async)]
165 #[allow(dead_code)]
166 async fn remove_calls_js() {
167 let storage = setup_device_storage();
168 let func = Function::new_with_args("k", "delete this[k]; return Promise.resolve();");
169 let _ = Reflect::set(&storage, &"remove".into(), &func);
170 let _ = Reflect::set(&storage, &"a".into(), &JsValue::from_str("b"));
171 assert!(remove("a").await.is_ok());
172 let has = Reflect::has(&storage, &"a".into()).unwrap();
173 assert!(!has);
174 }
175
176 #[wasm_bindgen_test(async)]
177 #[allow(dead_code)]
178 async fn remove_err() {
179 assert!(remove("a").await.is_err());
180 }
181
182 #[wasm_bindgen_test(async)]
183 #[allow(dead_code)]
184 async fn clear_calls_js() {
185 let storage = setup_device_storage();
186 let func = Function::new_no_args(
187 "Object.keys(this).forEach(k => delete this[k]); return Promise.resolve();"
188 );
189 let _ = Reflect::set(&storage, &"clear".into(), &func);
190 let _ = Reflect::set(&storage, &"a".into(), &JsValue::from_str("b"));
191 assert!(clear().await.is_ok());
192 let has = Reflect::has(&storage, &"a".into()).unwrap();
193 assert!(!has);
194 }
195
196 #[wasm_bindgen_test(async)]
197 #[allow(dead_code)]
198 async fn clear_err() {
199 assert!(clear().await.is_err());
200 }
201}