From 642f1ba376c52651d581c184e71c3402602cab02 Mon Sep 17 00:00:00 2001 From: Omar Emad Date: Thu, 9 Oct 2025 13:01:00 +0300 Subject: [PATCH] Refactoring the notification to: check if this message is sent to the user before Switch to use "sonner" to handle notification logic efficiently Use gradient instead of images to improve loading time Move all the logic to "NotificationToast.tsx" to keep "App.tsx" clean and minimal --- frontend/.env.development | 1 + frontend/package-lock.json | 11 ++++ frontend/package.json | 1 + frontend/src/App.tsx | 23 +++----- frontend/src/components/NotificationToast.tsx | 52 +++++++++++++++++++ 5 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 frontend/src/components/NotificationToast.tsx diff --git a/frontend/.env.development b/frontend/.env.development index 7b6f3434f..2a1175593 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -2,5 +2,6 @@ VITE_BASE_URL=http://localhost:5173 VITE_API_HOST=http://127.0.0.1:7091 VITE_API_STREAMING=true + VITE_NOTIFICATION_TEXT="What's new in 0.14.0 — Changelog" VITE_NOTIFICATION_LINK="#" \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cf5a4fd05..fb6ecfe2a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -30,6 +30,7 @@ "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", + "sonner": "^2.0.7", "tailwind-merge": "^3.3.1" }, "devDependencies": { @@ -10191,6 +10192,16 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, + "node_modules/sonner": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", + "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index c689baaf1..aee68a292 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -41,6 +41,7 @@ "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", + "sonner": "^2.0.7", "tailwind-merge": "^3.3.1" }, "devDependencies": { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 0c3384d1d..108d17ae2 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -15,7 +15,10 @@ import useTokenAuth from './hooks/useTokenAuth'; import Navigation from './Navigation'; import PageNotFound from './PageNotFound'; import Setting from './settings'; -import Notification from './components/Notification'; +// import Notification from './components/Notification'; + +import { Toaster } from 'sonner'; +import { NotificationToast } from './components/NotificationToast'; function AuthWrapper({ children }: { children: React.ReactNode }) { const { isAuthLoading } = useTokenAuth(); @@ -48,32 +51,18 @@ function MainLayout() { + + ); } export default function App() { const [, , componentMounted] = useDarkTheme(); - const [showNotification, setShowNotification] = useState(() => { - const saved = localStorage.getItem('showNotification'); - return saved ? JSON.parse(saved) : true; - }); - const notificationText = import.meta.env.VITE_NOTIFICATION_TEXT; - const notificationLink = import.meta.env.VITE_NOTIFICATION_LINK; if (!componentMounted) { return
; } return (
- {notificationLink && notificationText && showNotification && ( - { - setShowNotification(false); - localStorage.setItem('showNotification', 'false'); - }} - /> - )} { + const message = import.meta.env.VITE_NOTIFICATION_TEXT; + const link = import.meta.env.VITE_NOTIFICATION_LINK; + + console.log('Omar Emad , ', message, link); + if (!message) return; + + const lastSeenNotification = localStorage.getItem('lastseennotification'); + if (lastSeenNotification === `${message}/${link}`) return; + localStorage.setItem('lastseennotification', `${message}/${link}`); + + toast.custom( + (t) => ( +
toast.dismiss(t)} + className="flex cursor-pointer items-center justify-between gap-3 rounded-lg bg-[linear-gradient(90deg,rgba(57,0,134,0)_16.83%,#6222B7_53.02%,rgba(57,0,134,0)_87.5%),linear-gradient(90deg,#390086_0%,#6222B7_100%)] px-5 py-4 text-sm text-white transition select-none hover:opacity-90" + > + {link ? ( + e.stopPropagation()} + className="underline hover:no-underline" + > + {message} + + ) : ( + {message} + )} + + +
+ ), + { duration: 10000, position: 'bottom-right' }, // 10 seconds , measured in ms + ); + }, []); + + return null; // this component doesn’t render anything itself +}