Fix connection accept regression: revert disabled gates, add 409 handling
Previous fix (2139ea8) caused regression: staleTime:0 nuked cache on every mount, isLoadingIncoming disabled buttons during cold-cache fetch. Changes: - Remove staleTime:0 (keep refetchOnMount:'always' for background refresh) - Revert button gate to pendingRequestIds.has() only (no is_read gate) - Remove isLoadingIncoming from disabled prop and spinner condition - Add 409-as-success handling to ConnectionRequestCard (People tab) - Use axios.isAxiosError() instead of err:any in all 3 catch blocks - Add markRead() call to 409 branch so notifications clear properly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2139ea8077
commit
1e736eb333
@ -4,6 +4,7 @@ import { toast } from 'sonner';
|
|||||||
import { formatDistanceToNow } from 'date-fns';
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { useConnections } from '@/hooks/useConnections';
|
import { useConnections } from '@/hooks/useConnections';
|
||||||
|
import axios from 'axios';
|
||||||
import { getErrorMessage } from '@/lib/api';
|
import { getErrorMessage } from '@/lib/api';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { ConnectionRequest } from '@/types';
|
import type { ConnectionRequest } from '@/types';
|
||||||
@ -33,8 +34,14 @@ export default function ConnectionRequestCard({ request, direction }: Connection
|
|||||||
setResolved(true);
|
setResolved(true);
|
||||||
toast.success(action === 'accept' ? 'Connection accepted' : 'Request declined');
|
toast.success(action === 'accept' ? 'Connection accepted' : 'Request declined');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// 409 means the request was already resolved (e.g. accepted via toast or notification center)
|
||||||
|
if (axios.isAxiosError(err) && err.response?.status === 409) {
|
||||||
|
setResolved(true);
|
||||||
|
toast.success(action === 'accept' ? 'Connection already accepted' : 'Request already resolved');
|
||||||
|
} else {
|
||||||
toast.error(getErrorMessage(err, 'Failed to respond'));
|
toast.error(getErrorMessage(err, 'Failed to respond'));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = async () => {
|
const handleCancel = async () => {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Check, X, Bell, UserPlus } from 'lucide-react';
|
|||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { useNotifications } from '@/hooks/useNotifications';
|
import { useNotifications } from '@/hooks/useNotifications';
|
||||||
import { useConnections } from '@/hooks/useConnections';
|
import { useConnections } from '@/hooks/useConnections';
|
||||||
|
import axios from 'axios';
|
||||||
import { getErrorMessage } from '@/lib/api';
|
import { getErrorMessage } from '@/lib/api';
|
||||||
import type { AppNotification } from '@/types';
|
import type { AppNotification } from '@/types';
|
||||||
|
|
||||||
@ -36,11 +37,10 @@ export default function NotificationToaster() {
|
|||||||
await respondRef.current({ requestId, action });
|
await respondRef.current({ requestId, action });
|
||||||
toast.dismiss(loadingId);
|
toast.dismiss(loadingId);
|
||||||
toast.success(action === 'accept' ? 'Connection accepted' : 'Request declined');
|
toast.success(action === 'accept' ? 'Connection accepted' : 'Request declined');
|
||||||
} catch (err: any) {
|
} catch (err) {
|
||||||
toast.dismiss(loadingId);
|
toast.dismiss(loadingId);
|
||||||
// 409 means the request was already resolved (e.g. accepted via notification center)
|
// 409 means the request was already resolved (e.g. accepted via notification center)
|
||||||
const status = err?.response?.status;
|
if (axios.isAxiosError(err) && err.response?.status === 409) {
|
||||||
if (status === 409) {
|
|
||||||
toast.success(action === 'accept' ? 'Connection already accepted' : 'Request already resolved');
|
toast.success(action === 'accept' ? 'Connection already accepted' : 'Request already resolved');
|
||||||
} else {
|
} else {
|
||||||
toast.error(getErrorMessage(err, 'Failed to respond to request'));
|
toast.error(getErrorMessage(err, 'Failed to respond to request'));
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { useNotifications } from '@/hooks/useNotifications';
|
|||||||
import { useConnections } from '@/hooks/useConnections';
|
import { useConnections } from '@/hooks/useConnections';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
import axios from 'axios';
|
||||||
import { getErrorMessage } from '@/lib/api';
|
import { getErrorMessage } from '@/lib/api';
|
||||||
import { ListSkeleton } from '@/components/ui/skeleton';
|
import { ListSkeleton } from '@/components/ui/skeleton';
|
||||||
import type { AppNotification } from '@/types';
|
import type { AppNotification } from '@/types';
|
||||||
@ -31,7 +32,7 @@ export default function NotificationsPage() {
|
|||||||
deleteNotification,
|
deleteNotification,
|
||||||
} = useNotifications();
|
} = useNotifications();
|
||||||
|
|
||||||
const { incomingRequests, respond, isResponding, isLoadingIncoming } = useConnections();
|
const { incomingRequests, respond, isResponding } = useConnections();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [filter, setFilter] = useState<Filter>('all');
|
const [filter, setFilter] = useState<Filter>('all');
|
||||||
@ -94,8 +95,10 @@ export default function NotificationsPage() {
|
|||||||
toast.success(action === 'accept' ? 'Connection accepted' : 'Request declined');
|
toast.success(action === 'accept' ? 'Connection accepted' : 'Request declined');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// 409 means the request was already resolved (e.g. accepted via toast)
|
// 409 means the request was already resolved (e.g. accepted via toast)
|
||||||
const status = (err as any)?.response?.status;
|
if (axios.isAxiosError(err) && err.response?.status === 409) {
|
||||||
if (status === 409) {
|
if (!notification.is_read) {
|
||||||
|
await markRead([notification.id]).catch(() => {});
|
||||||
|
}
|
||||||
toast.success(action === 'accept' ? 'Connection already accepted' : 'Request already resolved');
|
toast.success(action === 'accept' ? 'Connection already accepted' : 'Request already resolved');
|
||||||
} else {
|
} else {
|
||||||
toast.error(getErrorMessage(err, 'Failed to respond'));
|
toast.error(getErrorMessage(err, 'Failed to respond'));
|
||||||
@ -224,23 +227,22 @@ export default function NotificationsPage() {
|
|||||||
{/* Connection request actions (inline) */}
|
{/* Connection request actions (inline) */}
|
||||||
{notification.type === 'connection_request' &&
|
{notification.type === 'connection_request' &&
|
||||||
notification.source_id &&
|
notification.source_id &&
|
||||||
!notification.is_read &&
|
pendingRequestIds.has(notification.source_id) && (
|
||||||
(isLoadingIncoming || pendingRequestIds.has(notification.source_id)) && (
|
|
||||||
<div className="flex items-center gap-1.5 shrink-0">
|
<div className="flex items-center gap-1.5 shrink-0">
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={(e) => { e.stopPropagation(); handleConnectionRespond(notification, 'accept'); }}
|
onClick={(e) => { e.stopPropagation(); handleConnectionRespond(notification, 'accept'); }}
|
||||||
disabled={isResponding || isLoadingIncoming}
|
disabled={isResponding}
|
||||||
className="gap-1 h-7 text-xs"
|
className="gap-1 h-7 text-xs"
|
||||||
>
|
>
|
||||||
{(isResponding || isLoadingIncoming) ? <Loader2 className="h-3 w-3 animate-spin" /> : <Check className="h-3 w-3" />}
|
{isResponding ? <Loader2 className="h-3 w-3 animate-spin" /> : <Check className="h-3 w-3" />}
|
||||||
Accept
|
Accept
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={(e) => { e.stopPropagation(); handleConnectionRespond(notification, 'reject'); }}
|
onClick={(e) => { e.stopPropagation(); handleConnectionRespond(notification, 'reject'); }}
|
||||||
disabled={isResponding || isLoadingIncoming}
|
disabled={isResponding}
|
||||||
className="h-7 text-xs"
|
className="h-7 text-xs"
|
||||||
>
|
>
|
||||||
<X className="h-3 w-3" />
|
<X className="h-3 w-3" />
|
||||||
|
|||||||
@ -20,7 +20,6 @@ export function useConnections() {
|
|||||||
const { data } = await api.get<ConnectionRequest[]>('/connections/requests/incoming');
|
const { data } = await api.get<ConnectionRequest[]>('/connections/requests/incoming');
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
staleTime: 0,
|
|
||||||
refetchOnMount: 'always',
|
refetchOnMount: 'always',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user