Fix team review findings: reactive shared-calendar gate + ReorderItem hardening
- Convert hasSharingRef from useRef to useState in useCalendars so refetchInterval reacts immediately when sharing is detected (P-01) - Add extra="forbid" to ReorderItem schema to prevent mass-assignment (S-03) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a94485b138
commit
e270a2f63d
@ -4,7 +4,7 @@ from sqlalchemy import select
|
|||||||
from sqlalchemy.orm import selectinload
|
from sqlalchemy.orm import selectinload
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, ConfigDict
|
||||||
|
|
||||||
from app.database import get_db
|
from app.database import get_db
|
||||||
from app.models.project import Project
|
from app.models.project import Project
|
||||||
@ -20,6 +20,7 @@ router = APIRouter()
|
|||||||
|
|
||||||
|
|
||||||
class ReorderItem(BaseModel):
|
class ReorderItem(BaseModel):
|
||||||
|
model_config = ConfigDict(extra="forbid")
|
||||||
id: int
|
id: int
|
||||||
sort_order: int
|
sort_order: int
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useMemo, useRef } from 'react';
|
import { useState, useMemo } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import api from '@/lib/api';
|
import api from '@/lib/api';
|
||||||
import type { Calendar, SharedCalendarMembership } from '@/types';
|
import type { Calendar, SharedCalendarMembership } from '@/types';
|
||||||
@ -18,10 +18,11 @@ export function useCalendars({ pollingEnabled = false }: UseCalendarsOptions = {
|
|||||||
|
|
||||||
// AS-4: Gate shared-calendar polling on whether user participates in sharing.
|
// AS-4: Gate shared-calendar polling on whether user participates in sharing.
|
||||||
// Saves ~12 API calls/min for personal-only users.
|
// Saves ~12 API calls/min for personal-only users.
|
||||||
// Use a ref to latch "has sharing" once discovered, so polling doesn't flicker.
|
// Uses useState (not useRef) so changes trigger a re-render and the
|
||||||
const hasSharingRef = useRef(false);
|
// refetchInterval picks up the new value immediately.
|
||||||
|
const [hasSharing, setHasSharing] = useState(false);
|
||||||
const ownsShared = (ownedQuery.data ?? []).some((c) => c.is_shared);
|
const ownsShared = (ownedQuery.data ?? []).some((c) => c.is_shared);
|
||||||
if (ownsShared) hasSharingRef.current = true;
|
if (ownsShared && !hasSharing) setHasSharing(true);
|
||||||
|
|
||||||
const sharedQuery = useQuery({
|
const sharedQuery = useQuery({
|
||||||
queryKey: ['calendars', 'shared'],
|
queryKey: ['calendars', 'shared'],
|
||||||
@ -29,12 +30,12 @@ export function useCalendars({ pollingEnabled = false }: UseCalendarsOptions = {
|
|||||||
const { data } = await api.get<SharedCalendarMembership[]>('/shared-calendars');
|
const { data } = await api.get<SharedCalendarMembership[]>('/shared-calendars');
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
refetchInterval: pollingEnabled && hasSharingRef.current ? 5_000 : false,
|
refetchInterval: pollingEnabled && hasSharing ? 5_000 : false,
|
||||||
staleTime: 3_000,
|
staleTime: 3_000,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Also latch if user is a member of others' shared calendars
|
// Also latch if user is a member of others' shared calendars
|
||||||
if ((sharedQuery.data ?? []).length > 0) hasSharingRef.current = true;
|
if ((sharedQuery.data ?? []).length > 0 && !hasSharing) setHasSharing(true);
|
||||||
|
|
||||||
const allCalendarIds = useMemo(() => {
|
const allCalendarIds = useMemo(() => {
|
||||||
const owned = (ownedQuery.data ?? []).map((c) => c.id);
|
const owned = (ownedQuery.data ?? []).map((c) => c.id);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user