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