refactor Notifications
This commit is contained in:
parent
dcb3e1fe9a
commit
05ec62595d
56
app/api/notifications/mark-all-read/route.ts
Normal file
56
app/api/notifications/mark-all-read/route.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getServerSession } from 'next-auth';
|
||||
import { authOptions } from "@/app/api/auth/options";
|
||||
import { NotificationRegistry } from '@/lib/services/notifications/notification-registry';
|
||||
import { logger } from '@/lib/logger';
|
||||
|
||||
// POST /api/notifications/mark-all-read
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
|
||||
}
|
||||
|
||||
const registry = NotificationRegistry.getInstance();
|
||||
|
||||
// Reset all counts to 0 for this user
|
||||
try {
|
||||
const redis = await import('@/lib/redis').then(m => m.getRedisClient());
|
||||
const countKey = `notifications:count:${session.user.id}`;
|
||||
|
||||
// Set all counts to 0
|
||||
const emptyCount = {
|
||||
total: 0,
|
||||
unread: 0,
|
||||
sources: {
|
||||
email: { total: 0, unread: 0 },
|
||||
rocketchat: { total: 0, unread: 0 },
|
||||
leantime: { total: 0, unread: 0 },
|
||||
calendar: { total: 0, unread: 0 },
|
||||
},
|
||||
};
|
||||
|
||||
await redis.set(countKey, JSON.stringify(emptyCount), 'EX', 30);
|
||||
|
||||
logger.debug('[NOTIFICATIONS_MARK_ALL_READ] All notifications marked as read', {
|
||||
userId: session.user.id,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[NOTIFICATIONS_MARK_ALL_READ] Error marking all as read', {
|
||||
userId: session.user.id,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error: any) {
|
||||
logger.error('[NOTIFICATIONS_MARK_ALL_READ] Error', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
return NextResponse.json(
|
||||
{ error: "Internal server error", message: error.message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -30,19 +30,28 @@ type FilterOption = 'all' | 'email' | 'rocketchat' | 'leantime' | 'calendar';
|
||||
// Use React.memo to prevent unnecessary re-renders
|
||||
export const NotificationBadge = memo(function NotificationBadge({ className }: NotificationBadgeProps) {
|
||||
const { data: session, status } = useSession();
|
||||
const { notifications, notificationCount, fetchNotifications, markAsRead, loading, error } = useNotifications();
|
||||
const { notifications, notificationCount, fetchNotifications, markAsRead, markAllAsRead, loading, error } = useNotifications();
|
||||
const hasUnread = notificationCount.unread > 0;
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [sortBy, setSortBy] = useState<SortOption>('newest');
|
||||
const [filterBy, setFilterBy] = useState<FilterOption>('all');
|
||||
const [displayLimit, setDisplayLimit] = useState(10);
|
||||
const [hasMarkedAsRead, setHasMarkedAsRead] = useState(false);
|
||||
|
||||
// Fetch notifications when dropdown opens
|
||||
// When dropdown opens, mark all as read and fetch notifications
|
||||
useEffect(() => {
|
||||
if (isOpen && status === 'authenticated') {
|
||||
fetchNotifications(1, 50, filterBy === 'all' ? undefined : filterBy);
|
||||
// Mark all as read when opening (only once per open)
|
||||
if (!hasMarkedAsRead) {
|
||||
markAllAsRead();
|
||||
setHasMarkedAsRead(true);
|
||||
}
|
||||
}, [isOpen, status, filterBy, fetchNotifications]);
|
||||
fetchNotifications(1, 50, filterBy === 'all' ? undefined : filterBy);
|
||||
} else if (!isOpen) {
|
||||
// Reset flag when dropdown closes
|
||||
setHasMarkedAsRead(false);
|
||||
}
|
||||
}, [isOpen, status, filterBy, fetchNotifications, markAllAsRead, hasMarkedAsRead]);
|
||||
|
||||
// Sort and filter notifications
|
||||
const sortedAndFilteredNotifications = useMemo(() => {
|
||||
|
||||
@ -69,6 +69,40 @@ export function useNotifications() {
|
||||
}
|
||||
}, [session?.user]);
|
||||
|
||||
// Mark all notifications as read (when dropdown is opened)
|
||||
const markAllAsRead = useCallback(async () => {
|
||||
if (!session?.user || !isMountedRef.current) return false;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/notifications/mark-all-read', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to mark all notifications as read');
|
||||
}
|
||||
|
||||
// Update local state - reset count to 0
|
||||
setNotificationCount({
|
||||
total: 0,
|
||||
unread: 0,
|
||||
sources: {
|
||||
email: { total: 0, unread: 0 },
|
||||
rocketchat: { total: 0, unread: 0 },
|
||||
leantime: { total: 0, unread: 0 },
|
||||
calendar: { total: 0, unread: 0 },
|
||||
},
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (err: any) {
|
||||
console.error('Error marking all notifications as read:', err);
|
||||
setError(err.message || 'Failed to mark all notifications as read');
|
||||
return false;
|
||||
}
|
||||
}, [session?.user]);
|
||||
|
||||
// Mark notification as read
|
||||
const markAsRead = useCallback(async (notificationId: string) => {
|
||||
if (!session?.user || !isMountedRef.current) return false;
|
||||
@ -208,5 +242,6 @@ export function useNotifications() {
|
||||
fetchNotifications,
|
||||
fetchNotificationCount: () => fetchNotificationCount(true),
|
||||
markAsRead,
|
||||
markAllAsRead,
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user