Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,22 @@ export function useScheduleCalendar(
const [locationsLoading, setLocationsLoading] = useState(true);
const [occurrencesError, setOccurrencesError] = useState<string | null>(null);
const [locationsError, setLocationsError] = useState<string | null>(null);
// 首次加载成功后,后续重取(增删日程/语音写日程/轮询)不再把整个 agenda
// 卸载重挂——只在真的还没数据时才盖全屏转圈,之后都是后台悄悄换数据。必须等
// 两个请求都没报错才算数:首次加载失败时不能锁上,否则点重试会在空/旧数据
// 上直接显示 agenda,而不是原来的全屏首次加载态。渲染期间同步翻它,不放进
// useEffect(react-hooks 规则不允许在 effect 里直接 setState 触发级联渲染,
// 也不允许在渲染期间读 ref)。
const [initialLoadDone, setInitialLoadDone] = useState(false);
if (
!initialLoadDone &&
!occurrencesLoading &&
!locationsLoading &&
occurrencesError === null &&
locationsError === null
) {
setInitialLoadDone(true);
}
const [reloadToken, setReloadToken] = useState(0);

const focusKey = focusTarget
Expand Down Expand Up @@ -215,7 +231,7 @@ export function useScheduleCalendar(
occurrencesByDate,
selectedOccurrences,
locationSchedules,
loading: occurrencesLoading || locationsLoading,
loading: !initialLoadDone && (occurrencesLoading || locationsLoading),
Comment thread
LUPENGHAN marked this conversation as resolved.
error: occurrencesError ?? locationsError,
selectDate,
changeMonth,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,87 @@ describe('useScheduleCalendar', () => {
expect(result.current.locationSchedules).toHaveLength(1);
expect(getLocationSchedules).toHaveBeenCalledTimes(1);
});

it('keeps loading false on a background refetch after the first load completes', async () => {
let resolveSecondFetch: (value: readonly []) => void = () => {};
const getSchedulesByRange = jest
.fn<ScheduleCalendarReadService['getSchedulesByRange']>()
.mockResolvedValueOnce([])
.mockImplementationOnce(
() =>
new Promise((resolve) => {
resolveSecondFetch = resolve;
}),
);
const service = {
getSchedulesByRange,
getSchedulesByDay: jest.fn(),
getLocationSchedules: jest
.fn<ScheduleCalendarReadService['getLocationSchedules']>()
.mockResolvedValue([]),
} as ScheduleCalendarReadService;

const { result } = renderHook(() =>
useScheduleCalendar(service, 'account-a', 'Asia/Shanghai', new Date(2026, 7, 14)),
);

await waitFor(() => expect(result.current.loading).toBe(false));
expect(getSchedulesByRange).toHaveBeenCalledTimes(1);

act(() => {
result.current.retry();
});
await act(async () => {
await Promise.resolve();
});

expect(getSchedulesByRange).toHaveBeenCalledTimes(2);
expect(result.current.loading).toBe(false);

await act(async () => {
resolveSecondFetch([]);
});
});

it('keeps the full-screen loading state on retry after the initial load fails', async () => {
let resolveRetryFetch: (value: readonly []) => void = () => {};
const getSchedulesByRange = jest
.fn<ScheduleCalendarReadService['getSchedulesByRange']>()
.mockRejectedValueOnce(new Error('network error'))
.mockImplementationOnce(
() =>
new Promise((resolve) => {
resolveRetryFetch = resolve;
}),
);
const service = {
getSchedulesByRange,
getSchedulesByDay: jest.fn(),
getLocationSchedules: jest
.fn<ScheduleCalendarReadService['getLocationSchedules']>()
.mockResolvedValue([]),
} as ScheduleCalendarReadService;

const { result } = renderHook(() =>
useScheduleCalendar(service, 'account-a', 'Asia/Shanghai', new Date(2026, 7, 14)),
);

await waitFor(() => expect(result.current.error).not.toBeNull());
expect(result.current.loading).toBe(false);

act(() => {
result.current.retry();
});
await act(async () => {
await Promise.resolve();
});

expect(getSchedulesByRange).toHaveBeenCalledTimes(2);
expect(result.current.loading).toBe(true);

await act(async () => {
resolveRetryFetch([]);
});
await waitFor(() => expect(result.current.loading).toBe(false));
});
});
Loading