euv_ui/component/browser/hook/
impl.rs1use super::*;
2
3impl UseEuvBrowser {
5 pub fn use_browser_state() -> UseEuvBrowser {
11 UseEuvBrowser::default()
12 }
13
14 pub fn local_storage_get<K>(key: K) -> Option<String>
24 where
25 K: AsRef<str>,
26 {
27 let window: Window = window()?;
28 let storage: Storage = window.local_storage().ok()??;
29 storage.get_item(key.as_ref()).ok()?
30 }
31
32 pub fn local_storage_set<K, V>(key: K, value: V)
39 where
40 K: AsRef<str>,
41 V: AsRef<str>,
42 {
43 let Some(window) = window() else {
44 return;
45 };
46 let storage: Storage = match window.local_storage() {
47 Ok(Some(local_storage)) => local_storage,
48 _ => return,
49 };
50 let _: Result<(), JsValue> = storage.set_item(key.as_ref(), value.as_ref());
51 }
52
53 pub(crate) fn local_storage_remove<K>(key: K)
59 where
60 K: AsRef<str>,
61 {
62 let Some(window) = window() else {
63 return;
64 };
65 let storage: Storage = match window.local_storage() {
66 Ok(Some(local_storage)) => local_storage,
67 _ => return,
68 };
69 let _: Result<(), JsValue> = storage.remove_item(key.as_ref());
70 }
71
72 pub(crate) fn session_storage_get<K>(key: K) -> Option<String>
82 where
83 K: AsRef<str>,
84 {
85 let window: Window = window()?;
86 let storage: Storage = window.session_storage().ok()??;
87 storage.get_item(key.as_ref()).ok()?
88 }
89
90 pub(crate) fn session_storage_set<K, V>(key: K, value: V)
97 where
98 K: AsRef<str>,
99 V: AsRef<str>,
100 {
101 let Some(window) = window() else {
102 return;
103 };
104 let storage: Storage = match window.session_storage() {
105 Ok(Some(session_storage)) => session_storage,
106 _ => return,
107 };
108 let _: Result<(), JsValue> = storage.set_item(key.as_ref(), value.as_ref());
109 }
110
111 pub(crate) fn session_storage_remove<K>(key: K)
117 where
118 K: AsRef<str>,
119 {
120 let Some(window) = window() else {
121 return;
122 };
123 let storage: Storage = match window.session_storage() {
124 Ok(Some(session_storage)) => session_storage,
125 _ => return,
126 };
127 let _: Result<(), JsValue> = storage.remove_item(key.as_ref());
128 }
129
130 pub(crate) async fn clipboard_read_text() -> String {
136 let Some(window) = window() else {
137 return String::new();
138 };
139 let navigator: Navigator = window.navigator();
140 match Reflect::get(&navigator, &JsValue::from_str("clipboard")) {
141 Ok(clipboard_obj) if !clipboard_obj.is_undefined() => {
142 let clipboard: Clipboard = navigator.clipboard();
143 let promise: Promise = clipboard.read_text();
144 let future: JsFuture = JsFuture::from(promise);
145 match future.await {
146 Ok(value) => value
147 .as_string()
148 .unwrap_or_else(|| "No text content".to_string()),
149 Err(_) => "Failed to read clipboard".to_string(),
150 }
151 }
152 _ => "Clipboard API not available (requires secure context)".to_string(),
153 }
154 }
155
156 pub(crate) async fn clipboard_write_text<T>(text: T) -> bool
166 where
167 T: AsRef<str>,
168 {
169 let Some(window) = window() else {
170 return false;
171 };
172 let navigator: Navigator = window.navigator();
173 match js_sys::Reflect::get(&navigator, &JsValue::from_str("clipboard")) {
174 Ok(clipboard_obj) if !clipboard_obj.is_undefined() => {
175 let clipboard: Clipboard = navigator.clipboard();
176 let promise: Promise = clipboard.write_text(text.as_ref());
177 let future: JsFuture = JsFuture::from(promise);
178 future.await.is_ok()
179 }
180 _ => false,
181 }
182 }
183
184 pub(crate) fn window_inner_size() -> (i32, i32) {
190 let Some(window) = window() else {
191 return (0, 0);
192 };
193 let width: i32 = window
194 .inner_width()
195 .ok()
196 .map(|value: JsValue| Number::from(value).value_of() as i32)
197 .unwrap_or_default();
198 let height: i32 = window
199 .inner_height()
200 .ok()
201 .map(|value: JsValue| Number::from(value).value_of() as i32)
202 .unwrap_or_default();
203 (width, height)
204 }
205
206 pub(crate) fn navigator_user_agent() -> String {
212 let Some(window) = window() else {
213 return String::new();
214 };
215 window
216 .navigator()
217 .user_agent()
218 .unwrap_or_else(|_: JsValue| "Unknown".to_string())
219 }
220
221 pub(crate) fn navigator_language() -> String {
227 let Some(window) = window() else {
228 return String::new();
229 };
230 window
231 .navigator()
232 .language()
233 .unwrap_or_else(|| "Unknown".to_string())
234 }
235
236 pub(crate) fn location_href() -> String {
242 let Some(window) = window() else {
243 return String::new();
244 };
245 window
246 .location()
247 .href()
248 .unwrap_or_else(|_error: JsValue| "Unknown".to_string())
249 }
250
251 pub(crate) fn location_origin() -> String {
257 let Some(window) = window() else {
258 return String::new();
259 };
260 window
261 .location()
262 .origin()
263 .unwrap_or_else(|_error: JsValue| "Unknown".to_string())
264 }
265
266 pub(crate) fn location_pathname() -> String {
272 let Some(window) = window() else {
273 return String::new();
274 };
275 window
276 .location()
277 .pathname()
278 .unwrap_or_else(|_error: JsValue| "Unknown".to_string())
279 }
280
281 pub fn on_local_storage_set(self) -> Option<Rc<dyn Fn(Event)>> {
291 Some(Rc::new(move |_: Event| {
292 let key: String = self.get_local_key().get();
293 let value: String = self.get_local_value().get();
294 if !key.is_empty() {
295 Self::local_storage_set(&key, &value);
296 self.get_local_result().set(format!("Set: {key} = {value}"));
297 }
298 }))
299 }
300
301 pub fn on_local_storage_get(self) -> Option<Rc<dyn Fn(Event)>> {
311 Some(Rc::new(move |_: Event| {
312 let key: String = self.get_local_key().get();
313 let value: Option<String> = Self::local_storage_get(&key);
314 match value {
315 Some(v) => self.get_local_result().set(format!("Get: {key} = {v}")),
316 None => self
317 .get_local_result()
318 .set(format!("Key '{key}' not found")),
319 }
320 }))
321 }
322
323 pub fn on_local_storage_remove(self) -> Option<Rc<dyn Fn(Event)>> {
333 Some(Rc::new(move |_: Event| {
334 let key: String = self.get_local_key().get();
335 Self::local_storage_remove(&key);
336 self.get_local_result().set(format!("Removed key: {key}"));
337 }))
338 }
339
340 pub fn on_session_storage_set(self) -> Option<Rc<dyn Fn(Event)>> {
350 Some(Rc::new(move |_: Event| {
351 let key: String = self.get_session_key().get();
352 let value: String = self.get_session_value().get();
353 if !key.is_empty() {
354 Self::session_storage_set(&key, &value);
355 self.get_session_result()
356 .set(format!("Set: {key} = {value}"));
357 }
358 }))
359 }
360
361 pub fn on_session_storage_get(self) -> Option<Rc<dyn Fn(Event)>> {
371 Some(Rc::new(move |_: Event| {
372 let key: String = self.get_session_key().get();
373 let value: Option<String> = Self::session_storage_get(&key);
374 match value {
375 Some(v) => self.get_session_result().set(format!("Get: {key} = {v}")),
376 None => self
377 .get_session_result()
378 .set(format!("Key '{key}' not found")),
379 }
380 }))
381 }
382
383 pub fn on_session_storage_remove(self) -> Option<Rc<dyn Fn(Event)>> {
393 Some(Rc::new(move |_: Event| {
394 let key: String = self.get_session_key().get();
395 Self::session_storage_remove(&key);
396 self.get_session_result().set(format!("Removed key: {key}"));
397 }))
398 }
399
400 pub fn on_clipboard_copy(self) -> Option<Rc<dyn Fn(Event)>> {
410 Some(Rc::new(move |_: Event| {
411 let text: String = self.get_clipboard_text().get();
412 let text_clone: String = text.clone();
413 let result: Signal<String> = self.get_clipboard_result();
414 if text.is_empty() {
415 result.set("Please enter text to copy".to_string());
416 return;
417 }
418 let Some(window) = window() else {
419 return;
420 };
421 let navigator: Navigator = window.navigator();
422 match js_sys::Reflect::get(&navigator, &JsValue::from_str("clipboard")) {
423 Ok(clipboard_obj) if !clipboard_obj.is_undefined() => {
424 spawn_local(async move {
425 let success: bool = Self::clipboard_write_text(&text_clone).await;
426 if success {
427 result.set("Copied to clipboard!".to_string());
428 } else {
429 result.set("Failed to copy".to_string());
430 }
431 });
432 }
433 _ => {
434 result.set("Clipboard API not available (requires secure context)".to_string());
435 }
436 }
437 }))
438 }
439
440 pub fn on_clipboard_paste(self) -> Option<Rc<dyn Fn(Event)>> {
450 Some(Rc::new(move |_: Event| {
451 let result: Signal<String> = self.get_clipboard_result();
452 let Some(window) = window() else {
453 return;
454 };
455 let navigator: Navigator = window.navigator();
456 match js_sys::Reflect::get(&navigator, &JsValue::from_str("clipboard")) {
457 Ok(clipboard_obj) if !clipboard_obj.is_undefined() => {
458 spawn_local(async move {
459 let text: String = Self::clipboard_read_text().await;
460 result.set(format!("Pasted: {text}"));
461 });
462 }
463 _ => {
464 result.set("Clipboard API not available (requires secure context)".to_string());
465 }
466 }
467 }))
468 }
469
470 pub fn on_window_refresh_size(self) -> Option<Rc<dyn Fn(Event)>> {
480 Some(Rc::new(move |_: Event| {
481 let (width, height): (i32, i32) = Self::window_inner_size();
482 self.get_window_size().set(format!("{width} x {height}"));
483 }))
484 }
485
486 pub fn on_console_log(console_input: Signal<String>) -> Option<Rc<dyn Fn(Event)>> {
496 Some(Rc::new(move |_: Event| {
497 let raw: String = console_input.get();
498 let message: &str = if raw.is_empty() {
499 CONSOLE_LOG_DEFAULT_MESSAGE
500 } else {
501 &raw
502 };
503 Console::log(message);
504 }))
505 }
506
507 pub fn on_console_warn(console_input: Signal<String>) -> Option<Rc<dyn Fn(Event)>> {
517 Some(Rc::new(move |_: Event| {
518 let raw: String = console_input.get();
519 let message: &str = if raw.is_empty() {
520 CONSOLE_WARN_DEFAULT_MESSAGE
521 } else {
522 &raw
523 };
524 Console::warn(message);
525 }))
526 }
527
528 pub fn on_console_error(console_input: Signal<String>) -> Option<Rc<dyn Fn(Event)>> {
538 Some(Rc::new(move |_: Event| {
539 let raw: String = console_input.get();
540 let message: &str = if raw.is_empty() {
541 CONSOLE_ERROR_DEFAULT_MESSAGE
542 } else {
543 &raw
544 };
545 Console::error(message);
546 }))
547 }
548}
549
550impl Default for UseEuvBrowser {
552 fn default() -> Self {
554 let window_size_val: String = {
555 let (width, height): (i32, i32) = UseEuvBrowser::window_inner_size();
556 format!("{width} x {height}")
557 };
558 UseEuvBrowser {
559 local_key: App::use_signal(String::new),
560 local_value: App::use_signal(String::new),
561 local_result: App::use_signal(String::new),
562 session_key: App::use_signal(String::new),
563 session_value: App::use_signal(String::new),
564 session_result: App::use_signal(String::new),
565 clipboard_text: App::use_signal(String::new),
566 clipboard_result: App::use_signal(String::new),
567 window_size: App::use_signal(move || window_size_val.clone()),
568 user_agent: App::use_signal(UseEuvBrowser::navigator_user_agent),
569 language: App::use_signal(UseEuvBrowser::navigator_language),
570 location_url: App::use_signal(UseEuvBrowser::location_href),
571 location_origin_val: App::use_signal(UseEuvBrowser::location_origin),
572 location_pathname_val: App::use_signal(UseEuvBrowser::location_pathname),
573 console_input: App::use_signal(String::new),
574 }
575 }
576}