diff --git a/frontend/src/components/calendar/EventDetailPanel.tsx b/frontend/src/components/calendar/EventDetailPanel.tsx index b656fbe..8362a1f 100644 --- a/frontend/src/components/calendar/EventDetailPanel.tsx +++ b/frontend/src/components/calendar/EventDetailPanel.tsx @@ -264,6 +264,7 @@ export default function EventDetailPanel({ const [locationSearch, setLocationSearch] = useState(''); // Poll lock status in view mode for shared events (Stream A: real-time lock awareness) + // lockInfo is only set from the 423 error path; poll data (viewLockQuery.data) is used directly. const viewLockQuery = useQuery({ queryKey: ['event-lock', event?.id], queryFn: async () => { @@ -274,18 +275,22 @@ export default function EventDetailPanel({ }, enabled: !!isSharedEvent && !!event && typeof event.id === 'number' && !isEditing && !isCreating, refetchInterval: 5_000, + refetchIntervalInBackground: true, + refetchOnMount: 'always', }); - // Show/hide lock banner proactively in view mode (poll data always authoritative) + // Clear 423-error lockInfo when poll confirms lock is gone useEffect(() => { - if (!viewLockQuery.data) return; - if (viewLockQuery.data.locked) { - setLockInfo(viewLockQuery.data); - } else { + if (viewLockQuery.data && !viewLockQuery.data.locked) { setLockInfo(null); } }, [viewLockQuery.data]); + // Derived: authoritative lock state — poll data wins, 423 error lockInfo as fallback + const activeLockInfo: EventLockInfo | null = + (viewLockQuery.data?.locked ? viewLockQuery.data : null) ?? + (lockInfo?.locked ? lockInfo : null); + const isRecurring = !!(event?.is_recurring || event?.parent_event_id); // Permission helpers @@ -580,8 +585,8 @@ export default function EventDetailPanel({ size="icon" className="h-7 w-7" onClick={handleEditStart} - disabled={isAcquiringLock || !!(lockInfo && lockInfo.locked)} - title={lockInfo && lockInfo.locked ? `Locked by ${lockInfo.locked_by_name || 'another user'}` : 'Edit event'} + disabled={isAcquiringLock || !!activeLockInfo} + title={activeLockInfo ? `Locked by ${activeLockInfo.locked_by_name || 'another user'}` : 'Edit event'} > {isAcquiringLock ? : } @@ -629,12 +634,12 @@ export default function EventDetailPanel({ {/* Body */}
- {/* Lock banner */} - {lockInfo && lockInfo.locked && ( + {/* Lock banner — shown when activeLockInfo reports a lock (poll-authoritative) */} + {activeLockInfo && ( )}