fix notification badge initialization
continuous-integration/drone/push Build is passing

This commit is contained in:
AmirReza Jamali
2026-07-11 14:56:05 +03:30
parent 4051cb156c
commit 37aa916ad5
2 changed files with 25 additions and 28 deletions
@@ -8,19 +8,21 @@ import {
import { useUser } from "@/contexts"; import { useUser } from "@/contexts";
import Modal from "@/components/ui/modal"; import Modal from "@/components/ui/modal";
import Status from "@/components/ui/Status"; import Status from "@/components/ui/Status";
import { useMarkNotificationAsSeen } from "@/hooks/queries/useNotificationQueries"; import {
useGetMyNotifications,
useMarkNotificationAsSeen,
} from "@/hooks/queries/useNotificationQueries";
import { useIsMobile } from "@/hooks/use-mobile"; import { useIsMobile } from "@/hooks/use-mobile";
import { TNotificationDetailsResponse } from "@/lib/api/types/NotificationHistory"; import { TNotificationDetailsResponse } from "@/lib/api/types/NotificationHistory";
import { cn, sanitizeHtmlWithStyles } from "@/lib/utils"; import { cn, sanitizeHtmlWithStyles } from "@/lib/utils";
import { capitalize, unixToDate } from "@/utils/shared"; import { capitalize, unixToDate } from "@/utils/shared";
import Link from "next/link"; import Link from "next/link";
import { useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { BellIcon } from "./icons"; import { BellIcon } from "./icons";
import NotificationList from "./notification-list"; import NotificationList from "./notification-list";
export function Notification() { export function Notification() {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [isDotVisible, setIsDotVisible] = useState(true);
const [unreadNotificationCount, setUnreadNotificationCount] = const [unreadNotificationCount, setUnreadNotificationCount] =
useState<number>(0); useState<number>(0);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
@@ -28,8 +30,15 @@ export function Notification() {
useState<TNotificationDetailsResponse | null>(null); useState<TNotificationDetailsResponse | null>(null);
const { mutate } = useMarkNotificationAsSeen(); const { mutate } = useMarkNotificationAsSeen();
const { data: unreadNotifications, isLoading: isLoadingNotifications } =
useGetMyNotifications({ seen: false });
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { user } = useUser(); const { user } = useUser();
useEffect(() => {
setUnreadNotificationCount(unreadNotifications?.data.length ?? 0);
}, [unreadNotifications]);
const notificationHistoryHref = useMemo(() => { const notificationHistoryHref = useMemo(() => {
if (!user?.id) return "/notification-history"; if (!user?.id) return "/notification-history";
@@ -47,20 +56,14 @@ export function Notification() {
return ( return (
<> <>
<Dropdown <Dropdown isOpen={isOpen} setIsOpen={setIsOpen}>
isOpen={isOpen}
setIsOpen={(open) => {
setIsOpen(open);
if (setIsDotVisible) setIsDotVisible(false);
}}
>
<DropdownTrigger <DropdownTrigger
className="shadow-theme-xs grid size-12 place-items-center rounded-full border border-stroke/70 bg-white/75 text-dark outline-none backdrop-blur-md transition-colors hover:text-primary focus-visible:border-primary focus-visible:text-primary dark:border-dark-3 dark:bg-dark-2/75 dark:text-white dark:focus-visible:border-primary" className="shadow-theme-xs grid size-12 place-items-center rounded-full border border-stroke/70 bg-white/75 text-dark outline-none backdrop-blur-md transition-colors hover:text-primary focus-visible:border-primary focus-visible:text-primary dark:border-dark-3 dark:bg-dark-2/75 dark:text-white dark:focus-visible:border-primary"
aria-label="View Notifications" aria-label="View Notifications"
> >
<span className="relative"> <span className="relative">
<BellIcon /> <BellIcon />
{isDotVisible && unreadNotificationCount > 0 && ( {unreadNotificationCount > 0 && (
<span <span
className={cn( className={cn(
"absolute -right-1.5 -top-1.5 z-1 grid min-w-[18px] place-items-center rounded-full bg-gradient-to-br from-rose-500 to-red-600 px-1 text-[10px] font-bold leading-none text-white shadow-[0_2px_8px_rgba(239,68,68,0.55)] ring-2 ring-white dark:ring-dark-2", "absolute -right-1.5 -top-1.5 z-1 grid min-w-[18px] place-items-center rounded-full bg-gradient-to-br from-rose-500 to-red-600 px-1 text-[10px] font-bold leading-none text-white shadow-[0_2px_8px_rgba(239,68,68,0.55)] ring-2 ring-white dark:ring-dark-2",
@@ -88,7 +91,8 @@ export function Notification() {
</div> </div>
<NotificationList <NotificationList
setUnreadNotificationCount={setUnreadNotificationCount} notifications={unreadNotifications?.data}
isLoading={isLoadingNotifications}
onNotificationClick={handleNotificationClick} onNotificationClick={handleNotificationClick}
/> />
@@ -118,7 +122,7 @@ export function Notification() {
<div className="pointer-events-none absolute -bottom-12 -left-6 size-28 rounded-full bg-white/10 blur-md" /> <div className="pointer-events-none absolute -bottom-12 -left-6 size-28 rounded-full bg-white/10 blur-md" />
<div className="relative flex items-start gap-4"> <div className="relative flex items-start gap-4">
<span className="grid size-12 shrink-0 place-items-center rounded-2xl bg-white/20 text-white shadow-theme-sm ring-1 ring-white/30 backdrop-blur-sm"> <span className="shadow-theme-sm grid size-12 shrink-0 place-items-center rounded-2xl bg-white/20 text-white ring-1 ring-white/30 backdrop-blur-sm">
<BellIcon /> <BellIcon />
</span> </span>
@@ -183,7 +187,7 @@ export function Notification() {
<button <button
type="button" type="button"
onClick={() => setIsModalOpen(false)} onClick={() => setIsModalOpen(false)}
className="rounded-xl bg-gradient-to-br from-primary to-blue-700 px-5 py-2 text-sm font-semibold text-white shadow-theme-sm transition-opacity hover:opacity-90" className="shadow-theme-sm rounded-xl bg-gradient-to-br from-primary to-blue-700 px-5 py-2 text-sm font-semibold text-white transition-opacity hover:opacity-90"
> >
Got it Got it
</button> </button>
@@ -2,36 +2,29 @@
import Loading from "@/components/loading"; import Loading from "@/components/loading";
import IsVisible from "@/components/ui/IsVisible"; import IsVisible from "@/components/ui/IsVisible";
import { useGetMyNotifications } from "@/hooks/queries/useNotificationQueries";
import { TNotificationDetailsResponse } from "@/lib/api/types/NotificationHistory"; import { TNotificationDetailsResponse } from "@/lib/api/types/NotificationHistory";
import { truncateString } from "@/utils/shared"; import { truncateString } from "@/utils/shared";
import { Dispatch, SetStateAction, useEffect } from "react";
interface IProps { interface IProps {
setUnreadNotificationCount: Dispatch<SetStateAction<number>>; notifications?: TNotificationDetailsResponse[];
isLoading: boolean;
onNotificationClick: (notification: TNotificationDetailsResponse) => void; onNotificationClick: (notification: TNotificationDetailsResponse) => void;
} }
const NotificationList = ({ const NotificationList = ({
setUnreadNotificationCount, notifications,
isLoading,
onNotificationClick, onNotificationClick,
}: IProps) => { }: IProps) => {
const { data, isLoading } = useGetMyNotifications({ seen: false });
useEffect(() => {
setUnreadNotificationCount(data?.data.length ?? 0);
}, [data, isLoading]);
if (isLoading) if (isLoading)
return ( return <Loading className="min-h-48 flex-none justify-center p-6" />;
<Loading className="min-h-48 flex-none justify-center p-6" />
);
return ( return (
<ul className="mb-3 max-h-[23rem] space-y-1.5 overflow-y-auto"> <ul className="mb-3 max-h-[23rem] space-y-1.5 overflow-y-auto">
<IsVisible condition={data?.data.length === 0}> <IsVisible condition={notifications?.length === 0}>
<h2 className="cursor-default ps-2">No new notification</h2> <h2 className="cursor-default ps-2">No new notification</h2>
</IsVisible> </IsVisible>
{data?.data.map((notification) => ( {notifications?.map((notification) => (
<li <li
role="menuitem" role="menuitem"
key={notification.id} key={notification.id}