refactor(frontend): trim unused mutation and busy state APIs

This commit is contained in:
Tunglies
2026-10-06 14:33:00 +08:00
parent c9a54ecd38
commit ae679cad3f
3 changed files with 0 additions and 52 deletions
-28
View File
@@ -1,6 +1,5 @@
import { showNotice } from '@/services/notice-service'
import { revalidateQueries } from '@/services/query-client'
import type { AppStoreAction } from '@/store/app-state'
/** Anything a notice call accepts: translation key, ReactNode, or failure payload. */
type MutateNoticeInput = Parameters<typeof showNotice.error>[0]
@@ -8,18 +7,12 @@ type MutateNoticeInput = Parameters<typeof showNotice.error>[0]
export interface MutateOptions<T> {
/** Commands with the same identity execute in request order. */
id: string
/** Applied before the command runs; undone by `rollback` on failure. */
optimistic?: () => void
/** Restores pre-optimistic state when the command rejects. */
rollback?: () => void | Promise<void>
/** Runs after a fulfilled (non-busy) result, e.g. cache merges. */
onFulfilled?: (value: T) => void
/** SWR keys revalidated after a fulfilled result. */
revalidate?: readonly (readonly unknown[] | string)[]
/** Shown after a fulfilled result (most flows leave this to backend notices). */
successNotice?: MutateNoticeInput
/** Shown when the command resolves with `{ status: 'busy' }`. */
busyNotice?: MutateNoticeInput
/** `false` suppresses the default error toast. */
errorNotice?: false | MutateNoticeInput
}
@@ -35,20 +28,6 @@ const isBusyOutcome = (value: unknown): boolean =>
(value as { status?: unknown }).status === 'busy'
const inFlight = new Map<string, Promise<void>>()
let storeDispatch: ((action: AppStoreAction) => void) | null = null
/** The app store registers its dispatcher so the funnel can record busy state. */
export const bindStoreDispatch = (
dispatch: ((action: AppStoreAction) => void) | null,
) => {
storeDispatch = dispatch
}
/**
* The single funnel for "user intent → backend command": optimistic apply,
* invoke through the services layer, Busy handling, failure rollback, and the
* error toast, in that order, for every mutating call site.
*/
export async function mutate<T>(
invoke: () => Promise<T>,
options: MutateOptions<T>,
@@ -61,15 +40,10 @@ export async function mutate<T>(
})
inFlight.set(id, settled)
if (previous) await previous
storeDispatch?.({ type: 'busy/started', id })
try {
options.optimistic?.()
const value = await invoke()
if (isBusyOutcome(value)) {
if (options.busyNotice !== undefined) {
showNotice.warning(options.busyNotice)
}
return { ok: false, busy: true, value }
}
options.onFulfilled?.(value)
@@ -81,7 +55,6 @@ export async function mutate<T>(
}
return { ok: true, value }
} catch (error) {
await options.rollback?.()
if (options.errorNotice !== false) {
showNotice.error(
options.errorNotice !== undefined ? options.errorNotice : error,
@@ -91,6 +64,5 @@ export async function mutate<T>(
} finally {
release()
if (inFlight.get(id) === settled) inFlight.delete(id)
storeDispatch?.({ type: 'busy/settled', id })
}
}
-17
View File
@@ -20,8 +20,6 @@ export interface AppStoreState {
readonly profileUpdates: ProfileUpdatesState
/** Bumped once per test-all request; test items run their delay test per bump. */
readonly testAllCounter: number
/** Mutate-funnel bookkeeping: in-flight intent ids. */
readonly busy: ReadonlySet<string>
}
export const initialAppStoreState: AppStoreState = {
@@ -34,7 +32,6 @@ export const initialAppStoreState: AppStoreState = {
timerRevisions: new Map(),
},
testAllCounter: 0,
busy: new Set(),
}
export type AppStoreAction =
@@ -46,8 +43,6 @@ export type AppStoreAction =
| { type: 'profileUpdate/timerTick'; uid: string }
| { type: 'profileLoading/set'; uids: readonly string[]; loading: boolean }
| { type: 'testAll/requested' }
| { type: 'busy/started'; id: string }
| { type: 'busy/settled'; id: string }
const bumpRevision = (
map: ReadonlyMap<string, number>,
@@ -125,17 +120,5 @@ export function appStoreReducer(
return withLoading(state, action.uids, action.loading)
case 'testAll/requested':
return { ...state, testAllCounter: state.testAllCounter + 1 }
case 'busy/started': {
if (state.busy.has(action.id)) return state
const busy = new Set(state.busy)
busy.add(action.id)
return { ...state, busy }
}
case 'busy/settled': {
if (!state.busy.has(action.id)) return state
const busy = new Set(state.busy)
busy.delete(action.id)
return { ...state, busy }
}
}
}
-7
View File
@@ -1,8 +1,6 @@
import { useCallback, useEffect, useMemo, useReducer } from 'react'
import type { ReactNode } from 'react'
import { bindStoreDispatch } from '@/services/mutate'
import {
type AppStoreAction,
appStoreReducer,
@@ -34,11 +32,6 @@ export const AppStoreProvider = ({ children }: { children?: ReactNode }) => {
[reads],
)
useEffect(() => {
bindStoreDispatch(dispatch)
return () => bindStoreDispatch(null)
}, [])
useEffect(() => {
const readRunState = () => {
void reads.readRunState().catch(() => {})