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 +}