Toast notifications for memory mutations, slide-in animation

This commit is contained in:
Hermes
2026-04-09 21:33:47 -05:00
parent 6e7fae8031
commit 0b0881074a
4 changed files with 89 additions and 4 deletions
+6 -1
View File
@@ -2,6 +2,7 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState } from "react";
import { ToastProvider } from "@/components/Toast";
export function Providers({ children }: { children: React.ReactNode }) {
const [client] = useState(
@@ -12,5 +13,9 @@ export function Providers({ children }: { children: React.ReactNode }) {
},
})
);
return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
return (
<QueryClientProvider client={client}>
<ToastProvider>{children}</ToastProvider>
</QueryClientProvider>
);
}
+56
View File
@@ -0,0 +1,56 @@
"use client";
import { createContext, useContext, useState, useCallback, type ReactNode } from "react";
import { Check, X, AlertTriangle } from "lucide-react";
interface Toast {
id: number;
message: string;
type: "success" | "error";
}
const ToastContext = createContext<{
toast: (message: string, type?: "success" | "error") => void;
}>({ toast: () => {} });
export function useToast() {
return useContext(ToastContext);
}
export function ToastProvider({ children }: { children: ReactNode }) {
const [toasts, setToasts] = useState<Toast[]>([]);
let nextId = 0;
const toast = useCallback((message: string, type: "success" | "error" = "success") => {
const id = ++nextId;
setToasts((t) => [...t, { id, message, type }]);
setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 2500);
}, []);
return (
<ToastContext.Provider value={{ toast }}>
{children}
<div className="fixed top-4 left-4 right-4 z-[100] flex flex-col items-center gap-2 pointer-events-none max-w-lg mx-auto">
{toasts.map((t) => (
<div
key={t.id}
className="card px-4 py-2.5 flex items-center gap-2 pointer-events-auto animate-slide-in"
style={{
background: t.type === "error" ? "rgba(255,59,48,0.15)" : "rgba(52,199,89,0.15)",
border: `1px solid ${t.type === "error" ? "rgba(255,59,48,0.3)" : "rgba(52,199,89,0.3)"}`,
}}
>
{t.type === "error" ? (
<AlertTriangle size={14} style={{ color: "#ff3b30" }} />
) : (
<Check size={14} style={{ color: "#34c759" }} />
)}
<span className="text-[13px]" style={{ color: t.type === "error" ? "#ff3b30" : "#34c759" }}>
{t.message}
</span>
</div>
))}
</div>
</ToastContext.Provider>
);
}