telegram_webapp_sdk/webapp/
viewport.rs1use js_sys::Reflect;
5use wasm_bindgen::JsValue;
6
7use crate::webapp::{TelegramWebApp, types::SafeAreaInset};
8
9impl TelegramWebApp {
10 pub fn viewport_height(&self) -> Option<f64> {
19 Reflect::get(&self.inner, &"viewportHeight".into())
20 .ok()?
21 .as_f64()
22 }
23
24 pub fn viewport_width(&self) -> Option<f64> {
33 Reflect::get(&self.inner, &"viewportWidth".into())
34 .ok()?
35 .as_f64()
36 }
37
38 pub fn viewport_stable_height(&self) -> Option<f64> {
47 Reflect::get(&self.inner, &"viewportStableHeight".into())
48 .ok()?
49 .as_f64()
50 }
51
52 pub fn expand_viewport(&self) -> Result<(), JsValue> {
57 self.call0("expand")
58 }
59
60 pub(super) fn safe_area_from_property(&self, property: &str) -> Option<SafeAreaInset> {
61 let value = Reflect::get(&self.inner, &property.into()).ok()?;
62 SafeAreaInset::from_js(value)
63 }
64
65 pub fn safe_area_inset(&self) -> Option<SafeAreaInset> {
76 self.safe_area_from_property("safeAreaInset")
77 }
78
79 pub fn content_safe_area_inset(&self) -> Option<SafeAreaInset> {
90 self.safe_area_from_property("contentSafeAreaInset")
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use std::{cell::Cell, rc::Rc};
97
98 use js_sys::{Object, Reflect};
99 use wasm_bindgen::{JsCast, JsValue, prelude::Closure};
100 use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
101 use web_sys::window;
102
103 use crate::webapp::TelegramWebApp;
104
105 wasm_bindgen_test_configure!(run_in_browser);
106
107 fn setup_webapp() -> Object {
108 let win = window().expect("window");
109 let telegram = Object::new();
110 let webapp = Object::new();
111 let _ = Reflect::set(&win, &"Telegram".into(), &telegram);
112 let _ = Reflect::set(&telegram, &"WebApp".into(), &webapp);
113 webapp
114 }
115
116 #[wasm_bindgen_test]
117 #[allow(dead_code, clippy::unused_unit)]
118 fn viewport_height_returns_f64() {
119 let webapp = setup_webapp();
120 let _ = Reflect::set(&webapp, &"viewportHeight".into(), &JsValue::from_f64(640.0));
121 let app = TelegramWebApp::instance().expect("instance");
122 assert_eq!(app.viewport_height(), Some(640.0));
123 }
124
125 #[wasm_bindgen_test]
126 #[allow(dead_code, clippy::unused_unit)]
127 fn viewport_height_returns_none_when_property_missing() {
128 let _ = setup_webapp();
129 let app = TelegramWebApp::instance().expect("instance");
130 assert!(app.viewport_height().is_none());
131 }
132
133 #[wasm_bindgen_test]
134 #[allow(dead_code, clippy::unused_unit)]
135 fn viewport_width_reads_viewport_width_property() {
136 let webapp = setup_webapp();
139 let _ = Reflect::set(&webapp, &"viewportWidth".into(), &JsValue::from_f64(360.0));
140 let app = TelegramWebApp::instance().expect("instance");
141 assert_eq!(app.viewport_width(), Some(360.0));
142 }
143
144 #[wasm_bindgen_test]
145 #[allow(dead_code, clippy::unused_unit)]
146 fn viewport_stable_height_returns_f64() {
147 let webapp = setup_webapp();
148 let _ = Reflect::set(
149 &webapp,
150 &"viewportStableHeight".into(),
151 &JsValue::from_f64(600.0)
152 );
153 let app = TelegramWebApp::instance().expect("instance");
154 assert_eq!(app.viewport_stable_height(), Some(600.0));
155 }
156
157 #[wasm_bindgen_test]
158 #[allow(dead_code, clippy::unused_unit)]
159 fn expand_viewport_calls_js_expand() {
160 let webapp = setup_webapp();
161 let called = Rc::new(Cell::new(false));
162 let called_ref = called.clone();
163 let cb = Closure::<dyn FnMut()>::new(move || called_ref.set(true));
164 let _ = Reflect::set(&webapp, &"expand".into(), cb.as_ref().unchecked_ref());
165 cb.forget();
166
167 let app = TelegramWebApp::instance().expect("instance");
168 app.expand_viewport().expect("ok");
169 assert!(called.get());
170 }
171
172 #[wasm_bindgen_test]
173 #[allow(dead_code, clippy::unused_unit)]
174 fn expand_viewport_errors_when_method_missing() {
175 let _ = setup_webapp();
176 let app = TelegramWebApp::instance().expect("instance");
177 assert!(app.expand_viewport().is_err());
178 }
179}