fix: replace leptos-shadcn-toast with custom implementation to fix WASM panic
Some checks failed
Build MIPS Binary / build (push) Failing after 1m23s

This commit is contained in:
spinline
2026-02-11 20:02:58 +03:00
parent 920704ee72
commit f35b119c0d
5 changed files with 125 additions and 10 deletions

View File

@@ -50,7 +50,7 @@ leptos-shadcn-scroll-area = "0.8"
leptos-shadcn-dialog = "0.8" leptos-shadcn-dialog = "0.8"
leptos-shadcn-label = "0.8" leptos-shadcn-label = "0.8"
leptos-shadcn-alert = "0.8" leptos-shadcn-alert = "0.8"
leptos-shadcn-toast = "0.8"
leptos-shadcn-dropdown-menu = "0.8" leptos-shadcn-dropdown-menu = "0.8"
leptos-shadcn-tooltip = "0.8" leptos-shadcn-tooltip = "0.8"
leptos-shadcn-skeleton = "0.8" leptos-shadcn-skeleton = "0.8"

View File

@@ -8,20 +8,20 @@ use leptos::task::spawn_local;
use leptos_router::components::{Router, Routes, Route}; use leptos_router::components::{Router, Routes, Route};
use leptos_router::hooks::use_navigate; use leptos_router::hooks::use_navigate;
use leptos_shadcn_skeleton::Skeleton; use leptos_shadcn_skeleton::Skeleton;
use leptos_shadcn_toast::SonnerProvider; use crate::components::toast::Toaster;
#[component] #[component]
pub fn App() -> impl IntoView { pub fn App() -> impl IntoView {
view! { view! {
<SonnerProvider> <InnerApp />
<InnerApp /> <Toaster />
</SonnerProvider>
} }
} }
#[component] #[component]
fn InnerApp() -> impl IntoView { fn InnerApp() -> impl IntoView {
crate::store::provide_torrent_store(); crate::store::provide_torrent_store();
crate::components::toast::provide_toast_context();
let store = use_context::<crate::store::TorrentStore>(); let store = use_context::<crate::store::TorrentStore>();
let is_loading = signal(true); let is_loading = signal(true);

View File

@@ -2,3 +2,4 @@ pub mod context_menu;
pub mod layout; pub mod layout;
pub mod torrent; pub mod torrent;
pub mod auth; pub mod auth;
pub mod toast;

View File

@@ -0,0 +1,111 @@
use leptos::prelude::*;
use std::collections::HashMap;
use uuid::Uuid;
use shared::NotificationLevel;
#[derive(Clone, Debug, PartialEq)]
pub struct Toast {
pub id: String,
pub message: String,
pub level: NotificationLevel,
pub visible: RwSignal<bool>,
}
#[derive(Clone, Copy)]
pub struct ToastContext {
pub toasts: RwSignal<HashMap<String, Toast>>,
}
impl ToastContext {
pub fn add(&self, message: impl Into<String>, level: NotificationLevel) {
let id = Uuid::new_v4().to_string();
let message = message.into();
let toast = Toast {
id: id.clone(),
message,
level,
visible: RwSignal::new(true),
};
self.toasts.update(|m| {
m.insert(id.clone(), toast);
});
// Auto remove after 5 seconds
let toasts = self.toasts;
let id_clone = id.clone();
leptos::task::spawn_local(async move {
gloo_timers::future::TimeoutFuture::new(5000).await;
toasts.update(|m| {
if let Some(t) = m.get(&id_clone) {
t.visible.set(false);
}
});
// Wait for animation
gloo_timers::future::TimeoutFuture::new(300).await;
toasts.update(|m| {
m.remove(&id_clone);
});
});
}
}
pub fn provide_toast_context() {
let toasts = RwSignal::new(HashMap::new());
provide_context(ToastContext { toasts });
}
#[component]
pub fn Toaster() -> impl IntoView {
let context = expect_context::<ToastContext>();
view! {
<div class="fixed top-4 right-4 z-[100] flex flex-col gap-2 w-full max-w-sm pointer-events-none">
{move || {
context.toasts.get().into_values().map(|toast| {
view! { <ToastItem toast=toast /> }
}).collect::<Vec<_>>()
}}
</div>
}
}
#[component]
fn ToastItem(toast: Toast) -> impl IntoView {
let (visible, set_visible) = (toast.visible, toast.visible.write_only());
let base_classes = "pointer-events-auto relative w-full rounded-lg border p-4 shadow-lg transition-all duration-300 ease-in-out";
let color_classes = match toast.level {
NotificationLevel::Success => "bg-green-50 text-green-900 border-green-200 dark:bg-green-900 dark:text-green-100 dark:border-green-800",
NotificationLevel::Error => "bg-red-50 text-red-900 border-red-200 dark:bg-red-900 dark:text-red-100 dark:border-red-800",
NotificationLevel::Warning => "bg-yellow-50 text-yellow-900 border-yellow-200 dark:bg-yellow-900 dark:text-yellow-100 dark:border-yellow-800",
NotificationLevel::Info => "bg-blue-50 text-blue-900 border-blue-200 dark:bg-blue-900 dark:text-blue-100 dark:border-blue-800",
};
view! {
<div
class=move || format!("{} {} {}",
base_classes,
color_classes,
if visible.get() { "opacity-100 translate-x-0" } else { "opacity-0 translate-x-full" }
)
role="alert"
>
<div class="flex items-start gap-4">
<div class="flex-1">
<p class="text-sm font-medium">{&toast.message}</p>
</div>
<button
class="inline-flex shrink-0 opacity-50 hover:opacity-100 focus:opacity-100 focus:outline-none"
on:click=move |_| set_visible.set(false)
>
<span class="sr-only">"Kapat"</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4">
<line x1="18" x2="6" y1="6" y2="18"></line>
<line x1="6" x2="18" y1="6" y2="18"></line>
</svg>
</button>
</div>
</div>
}
}

View File

@@ -7,15 +7,18 @@ use std::collections::HashMap;
use struct_patch::traits::Patch; use struct_patch::traits::Patch;
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64}; use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
use crate::components::toast::ToastContext;
pub fn show_toast(level: NotificationLevel, message: impl Into<String>) { pub fn show_toast(level: NotificationLevel, message: impl Into<String>) {
let msg = message.into(); let msg = message.into();
gloo_console::log!("TOAST CALL:", &msg, format!("{:?}", level)); gloo_console::log!("TOAST CALL:", &msg, format!("{:?}", level));
log::info!("Displaying toast: [{:?}] {}", level, msg); log::info!("Displaying toast: [{:?}] {}", level, msg);
match level {
NotificationLevel::Info => { leptos_shadcn_toast::toast::info(&msg).show(); }, if let Some(context) = use_context::<ToastContext>() {
NotificationLevel::Success => { leptos_shadcn_toast::toast::success(&msg).show(); }, context.add(msg, level);
NotificationLevel::Warning => { leptos_shadcn_toast::toast::warning(&msg).show(); }, } else {
NotificationLevel::Error => { leptos_shadcn_toast::toast::error(&msg).show(); }, log::error!("ToastContext not found!");
gloo_console::error!("ToastContext not found!");
} }
} }