Fix ~60s delay before accept buttons work on new requests

Root cause: ['connections', 'incoming'] query has no polling, so
pendingRequestIds stays empty until manually refetched. Accept buttons
on NotificationsPage are gated by this set.

- NotificationsPage: detect connection_request notifications not in
  pendingRequestIds and eagerly invalidate incoming requests query
- NotificationToaster: invalidate ['connections', 'incoming'] when
  connection_request notifications arrive, so accept buttons are
  ready on all surfaces immediately

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kyle 2026-03-04 10:02:50 +08:00
parent 14a77f0f11
commit f854987f53
2 changed files with 20 additions and 1 deletions

View File

@ -62,6 +62,12 @@ export default function NotificationToaster() {
maxSeenIdRef.current = maxCurrent; maxSeenIdRef.current = maxCurrent;
} }
// Eagerly refresh incoming requests when connection_request notifications arrive
// so accept buttons work immediately on NotificationsPage / PeoplePage
if (newNotifications.some((n) => n.type === 'connection_request')) {
queryClient.invalidateQueries({ queryKey: ['connections', 'incoming'] });
}
// Show toasts // Show toasts
newNotifications.forEach((notification) => { newNotifications.forEach((notification) => {
if (notification.type === 'connection_request' && notification.source_id) { if (notification.type === 'connection_request' && notification.source_id) {

View File

@ -1,5 +1,6 @@
import { useState, useMemo } from 'react'; import { useState, useMemo, useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query';
import { Bell, Check, CheckCheck, Trash2, UserPlus, Info, AlertCircle, X, Loader2 } from 'lucide-react'; import { Bell, Check, CheckCheck, Trash2, UserPlus, Info, AlertCircle, X, Loader2 } from 'lucide-react';
import { formatDistanceToNow } from 'date-fns'; import { formatDistanceToNow } from 'date-fns';
import { toast } from 'sonner'; import { toast } from 'sonner';
@ -31,6 +32,7 @@ export default function NotificationsPage() {
} = useNotifications(); } = useNotifications();
const { incomingRequests, respond, isResponding } = useConnections(); const { incomingRequests, respond, isResponding } = useConnections();
const queryClient = useQueryClient();
const navigate = useNavigate(); const navigate = useNavigate();
const [filter, setFilter] = useState<Filter>('all'); const [filter, setFilter] = useState<Filter>('all');
@ -40,6 +42,17 @@ export default function NotificationsPage() {
[incomingRequests], [incomingRequests],
); );
// Eagerly fetch incoming requests when notifications contain connection_request
// entries whose source_id isn't in pendingRequestIds yet (stale connections data)
useEffect(() => {
const hasMissing = notifications.some(
(n) => n.type === 'connection_request' && n.source_id && !n.is_read && !pendingRequestIds.has(n.source_id),
);
if (hasMissing) {
queryClient.invalidateQueries({ queryKey: ['connections', 'incoming'] });
}
}, [notifications, pendingRequestIds, queryClient]);
const filtered = useMemo(() => { const filtered = useMemo(() => {
if (filter === 'unread') return notifications.filter((n) => !n.is_read); if (filter === 'unread') return notifications.filter((n) => !n.is_read);
return notifications; return notifications;