Centralize issues refresh handling
- add a shared issues query invalidation helper - invalidate from the page and domain host directly - remove the internal window refresh event listener - keep the legacy bridge refresh method wired to the shared helper
This commit is contained in:
parent
892334007d
commit
ed2b8d0e3d
4 changed files with 28 additions and 50 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { queryOptions } from '@tanstack/react-query';
|
||||
import { queryOptions, type QueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { apiClient, readJson } from '@/app/api-client';
|
||||
|
||||
|
|
@ -13,6 +13,7 @@ import type {
|
|||
} from './-issues.types';
|
||||
|
||||
const DEFAULT_LIMIT = 100;
|
||||
export const ISSUES_QUERY_KEY = ['issues'] as const;
|
||||
|
||||
function createIssueHeaders(profileId: number, extra?: HeadersInit): Headers {
|
||||
const headers = new Headers(extra);
|
||||
|
|
@ -125,7 +126,7 @@ export async function deleteIssue(profileId: number, issueId: number): Promise<v
|
|||
|
||||
export function issueCountsQueryOptions(profileId: number) {
|
||||
return queryOptions({
|
||||
queryKey: ['issues', 'counts', profileId],
|
||||
queryKey: [...ISSUES_QUERY_KEY, 'counts', profileId],
|
||||
queryFn: () => fetchIssueCounts(profileId),
|
||||
});
|
||||
}
|
||||
|
|
@ -135,15 +136,19 @@ export function issueListQueryOptions(
|
|||
search: Pick<IssuesSearch, 'status' | 'category'>,
|
||||
) {
|
||||
return queryOptions({
|
||||
queryKey: ['issues', 'list', profileId, search.status, search.category],
|
||||
queryKey: [...ISSUES_QUERY_KEY, 'list', profileId, search.status, search.category],
|
||||
queryFn: () => fetchIssueList(profileId, search),
|
||||
});
|
||||
}
|
||||
|
||||
export function issueDetailQueryOptions(profileId: number, issueId: number) {
|
||||
return queryOptions({
|
||||
queryKey: ['issues', 'detail', profileId, issueId],
|
||||
queryKey: [...ISSUES_QUERY_KEY, 'detail', profileId, issueId],
|
||||
queryFn: () => fetchIssue(profileId, issueId),
|
||||
enabled: issueId > 0,
|
||||
});
|
||||
}
|
||||
|
||||
export function invalidateIssuesQueries(queryClient: QueryClient) {
|
||||
return queryClient.invalidateQueries({ queryKey: ISSUES_QUERY_KEY });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ import {
|
|||
type IssueStatus,
|
||||
} from './-issues.types';
|
||||
|
||||
export const REFRESH_EVENT = 'ss:issues-refresh';
|
||||
|
||||
export const ISSUE_CATEGORY_META: Record<
|
||||
IssueCategory,
|
||||
{ label: string; icon: string; description: string; applies: Array<'track' | 'album' | 'artist'> }
|
||||
|
|
@ -100,10 +98,6 @@ export function getIssueStatusMeta(status: string) {
|
|||
return ISSUE_STATUS_META[status as IssueStatus];
|
||||
}
|
||||
|
||||
export function dispatchIssuesRefreshEvent() {
|
||||
window.dispatchEvent(new CustomEvent(REFRESH_EVENT));
|
||||
}
|
||||
|
||||
export function parseSnapshot(snapshot: IssueRecord['snapshot_data']): IssueSnapshot {
|
||||
if (!snapshot) {
|
||||
return {};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useForm } from '@tanstack/react-form';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { DialogBody, DialogFooter, DialogFrame, DialogHeader } from '@/components/dialog';
|
||||
|
|
@ -20,9 +20,8 @@ import { useProfile } from '@/platform/shell/route-controllers';
|
|||
|
||||
import type { IssueReportPayload } from '../-issues.types';
|
||||
|
||||
import { createIssue, issueCountsQueryOptions } from '../-issues.api';
|
||||
import { createIssue, issueCountsQueryOptions, invalidateIssuesQueries } from '../-issues.api';
|
||||
import {
|
||||
REFRESH_EVENT,
|
||||
createDefaultIssueTitle,
|
||||
getIssueCategoriesForEntity,
|
||||
getEntityLabel,
|
||||
|
|
@ -35,6 +34,9 @@ export function IssueDomainHost() {
|
|||
const profile = useProfile();
|
||||
const [reportPayload, setReportPayload] = useState<IssueReportPayload | null>(null);
|
||||
const profileId = profile.profileId;
|
||||
const refreshIssues = useCallback(() => {
|
||||
void invalidateIssuesQueries(queryClient);
|
||||
}, [queryClient]);
|
||||
|
||||
const countsQuery = useQuery({
|
||||
...issueCountsQueryOptions(profileId),
|
||||
|
|
@ -46,17 +48,6 @@ export function IssueDomainHost() {
|
|||
}
|
||||
}, [countsQuery.data]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleRefresh = () => {
|
||||
void queryClient.invalidateQueries({ queryKey: ISSUE_DOMAIN_QUERY_KEY });
|
||||
};
|
||||
|
||||
window.addEventListener(REFRESH_EVENT, handleRefresh);
|
||||
return () => {
|
||||
window.removeEventListener(REFRESH_EVENT, handleRefresh);
|
||||
};
|
||||
}, [queryClient]);
|
||||
|
||||
useEffect(() => {
|
||||
window.SoulSyncIssueDomain = {
|
||||
openReportIssue(payload) {
|
||||
|
|
@ -65,9 +56,7 @@ export function IssueDomainHost() {
|
|||
closeReportIssue() {
|
||||
setReportPayload(null);
|
||||
},
|
||||
refresh() {
|
||||
void queryClient.invalidateQueries({ queryKey: ISSUE_DOMAIN_QUERY_KEY });
|
||||
},
|
||||
refresh: refreshIssues,
|
||||
};
|
||||
|
||||
return () => {
|
||||
|
|
@ -87,7 +76,7 @@ export function IssueDomainHost() {
|
|||
onClose={() => setReportPayload(null)}
|
||||
onSubmitted={() => {
|
||||
setReportPayload(null);
|
||||
void queryClient.invalidateQueries({ queryKey: ISSUE_DOMAIN_QUERY_KEY });
|
||||
refreshIssues();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -330,8 +319,6 @@ function ReportIssueModal({
|
|||
);
|
||||
}
|
||||
|
||||
const ISSUE_DOMAIN_QUERY_KEY = ['issues'] as const;
|
||||
|
||||
const DEFAULT_REPORT_ISSUE_VALUES: ReportIssueFormValues = {
|
||||
category: '',
|
||||
description: '',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { Select } from '@/components/form';
|
||||
import { Show } from '@/components/primitives';
|
||||
|
|
@ -8,10 +7,12 @@ import { useProfile, useReactPageShell } from '@/platform/shell/route-controller
|
|||
|
||||
import type { IssueCounts, IssuePriority, IssueRecord, IssuesSearch } from '../-issues.types';
|
||||
|
||||
import { issueCountsQueryOptions, issueListQueryOptions } from '../-issues.api';
|
||||
import {
|
||||
REFRESH_EVENT,
|
||||
dispatchIssuesRefreshEvent,
|
||||
issueCountsQueryOptions,
|
||||
issueListQueryOptions,
|
||||
invalidateIssuesQueries,
|
||||
} from '../-issues.api';
|
||||
import {
|
||||
formatIssueDate,
|
||||
getEntityDetails,
|
||||
getEntityLabel,
|
||||
|
|
@ -31,6 +32,7 @@ import styles from './issues-page.module.css';
|
|||
|
||||
export function IssuesPage() {
|
||||
useReactPageShell('issues');
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate({ from: Route.fullPath });
|
||||
const params = Route.useSearch();
|
||||
|
||||
|
|
@ -42,16 +44,18 @@ export function IssuesPage() {
|
|||
});
|
||||
};
|
||||
|
||||
const handleMutationSuccess = () => {
|
||||
clearIssueSelection();
|
||||
void invalidateIssuesQueries(queryClient);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IssueBoard />
|
||||
<IssueDetailModal
|
||||
issueId={params.issueId}
|
||||
onClose={clearIssueSelection}
|
||||
onMutationSuccess={() => {
|
||||
clearIssueSelection();
|
||||
dispatchIssuesRefreshEvent();
|
||||
}}
|
||||
onMutationSuccess={handleMutationSuccess}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
@ -59,21 +63,9 @@ export function IssuesPage() {
|
|||
|
||||
function IssueBoard() {
|
||||
const { isAdmin, profileId } = useProfile();
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate({ from: Route.fullPath });
|
||||
const params = Route.useSearch();
|
||||
|
||||
useEffect(() => {
|
||||
const handleRefresh = () => {
|
||||
void queryClient.invalidateQueries({ queryKey: ['issues'] });
|
||||
};
|
||||
|
||||
window.addEventListener(REFRESH_EVENT, handleRefresh);
|
||||
return () => {
|
||||
window.removeEventListener(REFRESH_EVENT, handleRefresh);
|
||||
};
|
||||
}, [queryClient]);
|
||||
|
||||
const countsQuery = useQuery({
|
||||
...issueCountsQueryOptions(profileId),
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue