Compare commits
3 Commits
release-20
...
release-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d46dbd437 | ||
|
|
5f107299e3 | ||
|
|
c34133ded1 |
@@ -54,3 +54,5 @@ tailwind_fuse = "0.3.2"
|
|||||||
js-sys = "0.3.85"
|
js-sys = "0.3.85"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
serde-wasm-bindgen = "0.6.5"
|
serde-wasm-bindgen = "0.6.5"
|
||||||
|
leptos-use = "0.13"
|
||||||
|
codee = "0.2"
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
use leptos_use::storage::use_local_storage;
|
||||||
|
use codee::string::FromToStringCodec;
|
||||||
use shared::GlobalLimitRequest;
|
use shared::GlobalLimitRequest;
|
||||||
|
|
||||||
fn format_bytes(bytes: i64) -> String {
|
fn format_bytes(bytes: i64) -> String {
|
||||||
@@ -26,34 +28,19 @@ pub fn StatusBar() -> impl IntoView {
|
|||||||
let store = use_context::<crate::store::TorrentStore>().expect("store not provided");
|
let store = use_context::<crate::store::TorrentStore>().expect("store not provided");
|
||||||
let stats = store.global_stats;
|
let stats = store.global_stats;
|
||||||
|
|
||||||
let initial_theme = if let Some(win) = web_sys::window() {
|
// Use leptos-use for reactive localStorage management
|
||||||
if let Some(doc) = win.document() {
|
let (current_theme, set_current_theme, _) = use_local_storage::<String, FromToStringCodec>("vibetorrent_theme");
|
||||||
doc.document_element()
|
|
||||||
.and_then(|el| el.get_attribute("data-theme"))
|
|
||||||
.unwrap_or_else(|| "dark".to_string())
|
|
||||||
} else {
|
|
||||||
"dark".to_string()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
"dark".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let (current_theme, set_current_theme) = create_signal(initial_theme);
|
// Initialize with default if empty
|
||||||
|
if current_theme.get_untracked().is_empty() {
|
||||||
|
set_current_theme.set("dark".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically sync theme to document attribute
|
||||||
create_effect(move |_| {
|
create_effect(move |_| {
|
||||||
if let Some(win) = web_sys::window() {
|
let theme = current_theme.get().to_lowercase();
|
||||||
if let Some(storage) = win.local_storage().ok().flatten() {
|
if let Some(doc) = document().document_element() {
|
||||||
if let Ok(Some(stored_theme)) = storage.get_item("vibetorrent_theme") {
|
let _ = doc.set_attribute("data-theme", &theme);
|
||||||
let theme = stored_theme.to_lowercase();
|
|
||||||
set_current_theme.set(theme.clone());
|
|
||||||
if let Some(doc) = win.document() {
|
|
||||||
let _ = doc
|
|
||||||
.document_element()
|
|
||||||
.unwrap()
|
|
||||||
.set_attribute("data-theme", &theme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -275,14 +262,6 @@ pub fn StatusBar() -> impl IntoView {
|
|||||||
on:pointerdown=move |e| {
|
on:pointerdown=move |e| {
|
||||||
e.stop_propagation();
|
e.stop_propagation();
|
||||||
set_current_theme.set(theme.to_string());
|
set_current_theme.set(theme.to_string());
|
||||||
if let Some(win) = web_sys::window() {
|
|
||||||
if let Some(doc) = win.document() {
|
|
||||||
let _ = doc.document_element().unwrap().set_attribute("data-theme", theme);
|
|
||||||
}
|
|
||||||
if let Some(storage) = win.local_storage().ok().flatten() {
|
|
||||||
let _ = storage.set_item("vibetorrent_theme", theme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close_all();
|
close_all();
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use leptos::*;
|
use leptos::*;
|
||||||
use wasm_bindgen::closure::Closure;
|
use leptos_use::use_timeout_fn;
|
||||||
use wasm_bindgen::JsCast;
|
|
||||||
use crate::store::{get_action_messages, show_toast_with_signal};
|
use crate::store::{get_action_messages, show_toast_with_signal};
|
||||||
use shared::NotificationLevel;
|
use shared::NotificationLevel;
|
||||||
|
|
||||||
@@ -423,58 +422,45 @@ pub fn TorrentTable() -> impl IntoView {
|
|||||||
let _t_hash = t.hash.clone();
|
let _t_hash = t.hash.clone();
|
||||||
let t_hash_click = t.hash.clone();
|
let t_hash_click = t.hash.clone();
|
||||||
|
|
||||||
let (timer_id, set_timer_id) = create_signal(Option::<i32>::None);
|
|
||||||
let t_hash_long = t.hash.clone();
|
let t_hash_long = t.hash.clone();
|
||||||
|
let leptos_use::UseTimeoutFnReturn { start, stop, .. } = use_timeout_fn(
|
||||||
|
move |pos: (i32, i32)| {
|
||||||
|
set_menu_position.set(pos);
|
||||||
|
set_selected_hash.set(Some(t_hash_long.clone()));
|
||||||
|
set_menu_visible.set(true);
|
||||||
|
|
||||||
let clear_timer = move || {
|
// Haptic feedback
|
||||||
if let Some(id) = timer_id.get_untracked() {
|
let navigator = window().navigator();
|
||||||
window().clear_timeout_with_handle(id);
|
if let Ok(vibrate) = js_sys::Reflect::get(&navigator, &"vibrate".into()) {
|
||||||
set_timer_id.set(None);
|
if vibrate.is_function() {
|
||||||
}
|
let _ = navigator.vibrate_with_duration(50);
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
600.0,
|
||||||
|
);
|
||||||
|
|
||||||
let handle_touchstart = {
|
let handle_touchstart = {
|
||||||
let t_hash = t_hash_long.clone();
|
let start = start.clone();
|
||||||
move |e: web_sys::TouchEvent| {
|
move |e: web_sys::TouchEvent| {
|
||||||
clear_timer();
|
|
||||||
if let Some(touch) = e.touches().get(0) {
|
if let Some(touch) = e.touches().get(0) {
|
||||||
let x = touch.client_x();
|
start((touch.client_x(), touch.client_y()));
|
||||||
let y = touch.client_y();
|
|
||||||
let hash = t_hash.clone();
|
|
||||||
|
|
||||||
let closure = Closure::wrap(Box::new(move || {
|
|
||||||
set_menu_position.set((x, y));
|
|
||||||
set_selected_hash.set(Some(hash.clone()));
|
|
||||||
set_menu_visible.set(true);
|
|
||||||
|
|
||||||
// Haptic feedback (iOS Safari doesn't support vibrate)
|
|
||||||
let navigator = window().navigator();
|
|
||||||
if js_sys::Reflect::has(&navigator, &wasm_bindgen::JsValue::from_str("vibrate")).unwrap_or(false) {
|
|
||||||
let _ = navigator.vibrate_with_duration(50);
|
|
||||||
}
|
|
||||||
}) as Box<dyn Fn()>);
|
|
||||||
|
|
||||||
let id = window()
|
|
||||||
.set_timeout_with_callback_and_timeout_and_arguments_0(
|
|
||||||
closure.as_ref().unchecked_ref(),
|
|
||||||
600
|
|
||||||
)
|
|
||||||
.unwrap_or(0);
|
|
||||||
|
|
||||||
closure.forget();
|
|
||||||
set_timer_id.set(Some(id));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let handle_touchmove = move |_| {
|
let handle_touchmove = {
|
||||||
clear_timer();
|
let stop = stop.clone();
|
||||||
|
move |_| stop()
|
||||||
};
|
};
|
||||||
|
|
||||||
let handle_touchend = move |_| {
|
let handle_touchend = {
|
||||||
clear_timer();
|
let stop = stop.clone();
|
||||||
|
move |_| stop()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let handle_touchcancel = move |_| stop();
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div
|
<div
|
||||||
class=move || {
|
class=move || {
|
||||||
@@ -492,7 +478,7 @@ pub fn TorrentTable() -> impl IntoView {
|
|||||||
on:touchstart=handle_touchstart
|
on:touchstart=handle_touchstart
|
||||||
on:touchmove=handle_touchmove
|
on:touchmove=handle_touchmove
|
||||||
on:touchend=handle_touchend
|
on:touchend=handle_touchend
|
||||||
on:touchcancel=handle_touchend
|
on:touchcancel=handle_touchcancel
|
||||||
>
|
>
|
||||||
<div class="card-body gap-3">
|
<div class="card-body gap-3">
|
||||||
<div class="flex justify-between items-start gap-2">
|
<div class="flex justify-between items-start gap-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user