refactor(web): remove mutateCurrentWorkspace from AppContext, use service-layer invalidation hook

The previous implementation stored query invalidation logic (mutateCurrentWorkspace) and
isFetching state (isValidatingCurrentWorkspace) in React Context, causing QuotaPanel to
flash a loading spinner on every settings tab switch due to a useEffect calling
invalidateQueries on mount. This violated separation of concerns and React best practices.

- Remove mutateCurrentWorkspace and isValidatingCurrentWorkspace from AppContext
- Add useInvalidateCurrentWorkspace hook in service layer (consistent with project pattern)
- Remove redundant useEffect + invalidateQueries in ModelProviderPage
- QuotaPanel now derives loading from !currentWorkspace.id instead of external prop
- Update custom-web-app-brand to use the new service-layer hook
This commit is contained in:
yyh
2026-02-09 18:32:15 +08:00
parent 898e09264b
commit 72b96ba972
8 changed files with 41 additions and 70 deletions

View File

@ -25,11 +25,9 @@ export type AppContextValue = {
isCurrentWorkspaceOwner: boolean
isCurrentWorkspaceEditor: boolean
isCurrentWorkspaceDatasetOperator: boolean
mutateCurrentWorkspace: VoidFunction
langGeniusVersionInfo: LangGeniusVersionResponse
useSelector: typeof useSelector
isLoadingCurrentWorkspace: boolean
isValidatingCurrentWorkspace: boolean
}
const userProfilePlaceholder = {
@ -72,11 +70,9 @@ const AppContext = createContext<AppContextValue>({
isCurrentWorkspaceEditor: false,
isCurrentWorkspaceDatasetOperator: false,
mutateUserProfile: noop,
mutateCurrentWorkspace: noop,
langGeniusVersionInfo: initialLangGeniusVersionInfo,
useSelector,
isLoadingCurrentWorkspace: false,
isValidatingCurrentWorkspace: false,
})
export function useSelector<T>(selector: (value: AppContextValue) => T): T {
@ -91,7 +87,7 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
const queryClient = useQueryClient()
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
const { data: userProfileResp } = useUserProfile()
const { data: currentWorkspaceResp, isPending: isLoadingCurrentWorkspace, isFetching: isValidatingCurrentWorkspace } = useCurrentWorkspace()
const { data: currentWorkspaceResp, isPending: isLoadingCurrentWorkspace } = useCurrentWorkspace()
const langGeniusVersionQuery = useLangGeniusVersion(
userProfileResp?.meta.currentVersion,
!systemFeatures.branding.enabled,
@ -123,10 +119,6 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
queryClient.invalidateQueries({ queryKey: ['common', 'user-profile'] })
}, [queryClient])
const mutateCurrentWorkspace = useCallback(() => {
queryClient.invalidateQueries({ queryKey: ['common', 'current-workspace'] })
}, [queryClient])
// #region Zendesk conversation fields
useEffect(() => {
if (ZENDESK_FIELD_IDS.ENVIRONMENT && langGeniusVersionInfo?.current_env) {
@ -198,9 +190,7 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
isCurrentWorkspaceOwner,
isCurrentWorkspaceEditor,
isCurrentWorkspaceDatasetOperator,
mutateCurrentWorkspace,
isLoadingCurrentWorkspace,
isValidatingCurrentWorkspace,
}}
>
<div className="flex h-full flex-col overflow-y-auto">