From d182b2d34e7bf5da3b201a3d57cc0f5dd7e1f3d1 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:52:07 +0300 Subject: [PATCH] B: Fix cache invalidation and error handling --- .../frontend/src/app/ApplicationLayout.tsx | 71 +++++++++++-------- rsconcept/frontend/src/app/ErrorFallback.tsx | 4 +- .../frontend/src/app/GlobalProviders.tsx | 22 +----- .../src/backend/auth/useChangePassword.tsx | 2 +- .../frontend/src/backend/auth/useLogin.tsx | 2 +- .../frontend/src/backend/auth/useLogout.tsx | 2 +- .../src/backend/auth/useResetPassword.tsx | 4 +- rsconcept/frontend/src/backend/library/api.ts | 5 +- .../src/backend/library/useCloneItem.tsx | 2 +- .../src/backend/library/useDeleteItem.tsx | 9 ++- .../src/backend/library/useLibraryItem.tsx | 23 ------ ...singLibrary.tsx => useMutatingLibrary.tsx} | 7 +- .../src/backend/library/useRenameLocation.tsx | 10 ++- .../backend/library/useSetAccessPolicy.tsx | 36 ++++++---- .../src/backend/library/useSetEditors.tsx | 28 ++++---- .../src/backend/library/useSetLocation.tsx | 56 ++++++--------- .../src/backend/library/useSetOwner.tsx | 36 ++++++---- .../src/backend/library/useUpdateItem.tsx | 32 +++++---- .../src/backend/library/useVersionRestore.tsx | 4 +- .../src/backend/oss/useInputCreate.tsx | 8 ++- .../src/backend/oss/useInputUpdate.tsx | 8 ++- ...IsProcessingOss.tsx => useMutatingOss.tsx} | 2 +- .../src/backend/oss/useOperationDelete.tsx | 8 ++- .../src/backend/oss/useOperationExecute.tsx | 8 ++- .../src/backend/oss/useOperationUpdate.tsx | 20 +++++- .../backend/oss/useRelocateConstituents.tsx | 8 ++- rsconcept/frontend/src/backend/queryClient.ts | 2 +- .../src/backend/rsform/useCstCreate.tsx | 10 ++- .../src/backend/rsform/useCstDelete.tsx | 10 ++- .../src/backend/rsform/useCstMove.tsx | 1 - .../src/backend/rsform/useCstRename.tsx | 10 ++- .../src/backend/rsform/useCstSubstitute.tsx | 10 ++- .../src/backend/rsform/useCstUpdate.tsx | 23 ++++-- .../src/backend/rsform/useInlineSynthesis.tsx | 10 ++- ...essingRSForm.tsx => useMutatingRSForm.tsx} | 2 +- .../backend/rsform/useProduceStructure.tsx | 10 ++- .../src/backend/rsform/useResetAliases.tsx | 10 ++- .../src/backend/rsform/useUploadTRS.tsx | 9 +++ .../frontend/src/backend/users/useSignup.tsx | 2 +- .../src/backend/users/useUpdateProfile.tsx | 7 +- .../src/components/info/InfoError.tsx | 23 +++++- .../DlgEditVersions/DlgEditVersions.tsx | 4 +- .../OssPage/EditorOssCard/EditorOssCard.tsx | 3 +- .../pages/OssPage/EditorOssCard/FormOSS.tsx | 4 +- .../EditorOssGraph/NodeContextMenu.tsx | 4 +- .../pages/OssPage/EditorOssGraph/OssFlow.tsx | 6 +- .../EditorOssGraph/ToolbarOssGraph.tsx | 5 +- .../src/pages/OssPage/MenuOssTabs.tsx | 4 +- .../src/pages/OssPage/OssEditContext.tsx | 2 - .../frontend/src/pages/OssPage/OssPage.tsx | 7 -- .../frontend/src/pages/PasswordChangePage.tsx | 7 +- .../EditorConstituenta/EditorConstituenta.tsx | 4 +- .../EditorConstituenta/EditorControls.tsx | 4 +- .../EditorConstituenta/FormConstituenta.tsx | 4 +- .../ToolbarConstituenta.tsx | 4 +- .../EditorRSExpression/EditorRSExpression.tsx | 4 +- .../ToolbarRSExpression.tsx | 4 +- .../EditorRSFormCard/EditorLibraryItem.tsx | 53 +++++--------- .../EditorRSFormCard/EditorRSFormCard.tsx | 3 +- .../EditorRSFormCard/FormRSForm.tsx | 4 +- .../EditorRSFormCard/ToolbarItemAccess.tsx | 4 +- .../EditorRSFormCard/ToolbarRSFormCard.tsx | 4 +- .../RSFormPage/EditorRSList/EditorRSList.tsx | 4 +- .../RSFormPage/EditorRSList/ToolbarRSList.tsx | 4 +- .../RSFormPage/EditorTermGraph/TGFlow.tsx | 4 +- .../EditorTermGraph/ToolbarTermGraph.tsx | 4 +- .../src/pages/RSFormPage/MenuRSTabs.tsx | 4 +- .../src/pages/RSFormPage/RSFormPage.tsx | 14 +++- 68 files changed, 419 insertions(+), 309 deletions(-) delete mode 100644 rsconcept/frontend/src/backend/library/useLibraryItem.tsx rename rsconcept/frontend/src/backend/library/{useIsProcessingLibrary.tsx => useMutatingLibrary.tsx} (73%) rename rsconcept/frontend/src/backend/oss/{useIsProcessingOss.tsx => useMutatingOss.tsx} (88%) rename rsconcept/frontend/src/backend/rsform/{useIsProcessingRSForm.tsx => useMutatingRSForm.tsx} (88%) diff --git a/rsconcept/frontend/src/app/ApplicationLayout.tsx b/rsconcept/frontend/src/app/ApplicationLayout.tsx index fee8ee1e..a26267ca 100644 --- a/rsconcept/frontend/src/app/ApplicationLayout.tsx +++ b/rsconcept/frontend/src/app/ApplicationLayout.tsx @@ -1,16 +1,29 @@ import { Suspense } from 'react'; +import { ErrorBoundary } from 'react-error-boundary'; import { Outlet } from 'react-router'; import ConceptToaster from '@/app/ConceptToaster'; import Footer from '@/app/Footer'; import Navigation from '@/app/Navigation'; -import { NavigationState } from '@/app/Navigation/NavigationContext'; import Loader from '@/components/ui/Loader'; import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout'; import { globals } from '@/utils/constants'; +import ErrorFallback from './ErrorFallback'; import { GlobalDialogs } from './GlobalDialogs'; import { GlobalTooltips } from './GlobalTooltips'; +import { NavigationState } from './Navigation/NavigationContext'; + +const resetState = () => { + console.log('Resetting state after error fallback'); +}; + +const logError = (error: Error, info: { componentStack?: string | null | undefined }) => { + console.log('Error fallback: ' + error.message); + if (info.componentStack) { + console.log('Component stack: ' + info.componentStack); + } +}; function ApplicationLayout() { const mainHeight = useMainHeight(); @@ -23,37 +36,39 @@ function ApplicationLayout() { // TODO: prefetch data return ( - -
- + + +
+ - - + + - + -
-
- }> - - -
- {!noNavigation && !noFooter ?
: null} +
+
+ }> + + +
+ {!noNavigation && !noFooter ?
: null} +
-
-
+ +
); } diff --git a/rsconcept/frontend/src/app/ErrorFallback.tsx b/rsconcept/frontend/src/app/ErrorFallback.tsx index b653a361..843d0df8 100644 --- a/rsconcept/frontend/src/app/ErrorFallback.tsx +++ b/rsconcept/frontend/src/app/ErrorFallback.tsx @@ -5,8 +5,8 @@ import Button from '@/components/ui/Button'; function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) { return ( -
-

Что-то пошло не так!

+
+

Что-то пошло не так!

diff --git a/rsconcept/frontend/src/app/GlobalProviders.tsx b/rsconcept/frontend/src/app/GlobalProviders.tsx index 21f05748..7694b57e 100644 --- a/rsconcept/frontend/src/app/GlobalProviders.tsx +++ b/rsconcept/frontend/src/app/GlobalProviders.tsx @@ -2,32 +2,13 @@ import { QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; -import { ErrorBoundary } from 'react-error-boundary'; import { IntlProvider } from 'react-intl'; import { queryClient } from '@/backend/queryClient'; -import ErrorFallback from './ErrorFallback'; - -const resetState = () => { - console.log('Resetting state after error fallback'); -}; - -const logError = (error: Error, info: { componentStack?: string | null | undefined }) => { - console.log('Error fallback: ' + error.message); - if (info.componentStack) { - console.log('Component stack: ' + info.componentStack); - } -}; - // prettier-ignore function GlobalProviders({ children }: React.PropsWithChildren) { return ( - @@ -35,8 +16,7 @@ function GlobalProviders({ children }: React.PropsWithChildren) { {children} - - ); + ); } export default GlobalProviders; diff --git a/rsconcept/frontend/src/backend/auth/useChangePassword.tsx b/rsconcept/frontend/src/backend/auth/useChangePassword.tsx index 39f9af58..14f1e8e2 100644 --- a/rsconcept/frontend/src/backend/auth/useChangePassword.tsx +++ b/rsconcept/frontend/src/backend/auth/useChangePassword.tsx @@ -7,7 +7,7 @@ export const useChangePassword = () => { const mutation = useMutation({ mutationKey: ['change-password'], mutationFn: authApi.changePassword, - onSettled: async () => await client.invalidateQueries({ queryKey: [authApi.baseKey] }) + onSettled: () => client.invalidateQueries({ queryKey: [authApi.baseKey] }) }); return { changePassword: ( diff --git a/rsconcept/frontend/src/backend/auth/useLogin.tsx b/rsconcept/frontend/src/backend/auth/useLogin.tsx index a61beb2a..dd640db9 100644 --- a/rsconcept/frontend/src/backend/auth/useLogin.tsx +++ b/rsconcept/frontend/src/backend/auth/useLogin.tsx @@ -7,7 +7,7 @@ export const useLogin = () => { const mutation = useMutation({ mutationKey: ['login'], mutationFn: authApi.login, - onSettled: async () => await client.invalidateQueries({ queryKey: [authApi.baseKey] }) + onSettled: () => client.invalidateQueries({ queryKey: [authApi.baseKey] }) }); return { login: ( diff --git a/rsconcept/frontend/src/backend/auth/useLogout.tsx b/rsconcept/frontend/src/backend/auth/useLogout.tsx index 9bb5e3cd..d4e2f921 100644 --- a/rsconcept/frontend/src/backend/auth/useLogout.tsx +++ b/rsconcept/frontend/src/backend/auth/useLogout.tsx @@ -7,7 +7,7 @@ export const useLogout = () => { const mutation = useMutation({ mutationKey: ['logout'], mutationFn: authApi.logout, - onSettled: async () => await client.invalidateQueries({ queryKey: [authApi.baseKey] }) + onSettled: () => client.invalidateQueries({ queryKey: [authApi.baseKey] }) }); return { logout: (onSuccess?: () => void) => mutation.mutate(undefined, { onSuccess }) }; }; diff --git a/rsconcept/frontend/src/backend/auth/useResetPassword.tsx b/rsconcept/frontend/src/backend/auth/useResetPassword.tsx index 17f05316..040f7963 100644 --- a/rsconcept/frontend/src/backend/auth/useResetPassword.tsx +++ b/rsconcept/frontend/src/backend/auth/useResetPassword.tsx @@ -7,12 +7,12 @@ export const useResetPassword = () => { const validateMutation = useMutation({ mutationKey: ['reset-password'], mutationFn: authApi.validatePasswordToken, - onSuccess: async () => await client.invalidateQueries({ queryKey: [authApi.baseKey] }) + onSuccess: () => client.invalidateQueries({ queryKey: [authApi.baseKey] }) }); const resetMutation = useMutation({ mutationKey: ['reset-password'], mutationFn: authApi.resetPassword, - onSuccess: async () => await client.invalidateQueries({ queryKey: [authApi.baseKey] }) + onSuccess: () => client.invalidateQueries({ queryKey: [authApi.baseKey] }) }); return { validateToken: ( diff --git a/rsconcept/frontend/src/backend/library/api.ts b/rsconcept/frontend/src/backend/library/api.ts index b69320c4..b9b04301 100644 --- a/rsconcept/frontend/src/backend/library/api.ts +++ b/rsconcept/frontend/src/backend/library/api.ts @@ -2,6 +2,8 @@ import { queryOptions } from '@tanstack/react-query'; import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport'; import { DELAYS } from '@/backend/configuration'; +import { ossApi } from '@/backend/oss/api'; +import { rsformsApi } from '@/backend/rsform/api'; import { AccessPolicy, ILibraryItem, @@ -15,9 +17,6 @@ import { ConstituentaID, IRSFormData } from '@/models/rsform'; import { UserID } from '@/models/user'; import { information } from '@/utils/labels'; -import { ossApi } from '../oss/api'; -import { rsformsApi } from '../rsform/api'; - /** * Represents update data for renaming Location. */ diff --git a/rsconcept/frontend/src/backend/library/useCloneItem.tsx b/rsconcept/frontend/src/backend/library/useCloneItem.tsx index 0609a435..219aad9d 100644 --- a/rsconcept/frontend/src/backend/library/useCloneItem.tsx +++ b/rsconcept/frontend/src/backend/library/useCloneItem.tsx @@ -10,7 +10,7 @@ export const useCloneItem = () => { const mutation = useMutation({ mutationKey: [libraryApi.baseKey, 'clone-item'], mutationFn: libraryApi.cloneItem, - onSuccess: async () => await client.invalidateQueries({ queryKey: [libraryApi.baseKey] }) + onSuccess: () => client.invalidateQueries({ queryKey: [libraryApi.baseKey] }) }); return { cloneItem: ( diff --git a/rsconcept/frontend/src/backend/library/useDeleteItem.tsx b/rsconcept/frontend/src/backend/library/useDeleteItem.tsx index b44e6f6b..be09a490 100644 --- a/rsconcept/frontend/src/backend/library/useDeleteItem.tsx +++ b/rsconcept/frontend/src/backend/library/useDeleteItem.tsx @@ -1,5 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { ossApi } from '@/backend/oss/api'; +import { rsformsApi } from '@/backend/rsform/api'; import { ILibraryItem, LibraryItemID } from '@/models/library'; import { libraryApi } from './api'; @@ -9,11 +11,14 @@ export const useDeleteItem = () => { const mutation = useMutation({ mutationKey: [libraryApi.baseKey, 'delete-item'], mutationFn: libraryApi.deleteItem, - onSuccess: async (_, variables) => { - await client.cancelQueries({ queryKey: [libraryApi.libraryListKey] }); + onSuccess: (_, variables) => { client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => prev?.filter(item => item.id !== variables) ); + return Promise.allSettled([ + client.invalidateQueries({ queryKey: [ossApi.baseKey] }), + client.invalidateQueries({ queryKey: rsformsApi.getRSFormQueryOptions({ itemID: variables }).queryKey }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/library/useLibraryItem.tsx b/rsconcept/frontend/src/backend/library/useLibraryItem.tsx deleted file mode 100644 index 340a973d..00000000 --- a/rsconcept/frontend/src/backend/library/useLibraryItem.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; - -import { ILibraryItemVersioned, LibraryItemID, LibraryItemType } from '@/models/library'; - -import { ossApi } from '../oss/api'; -import { rsformsApi } from '../rsform/api'; - -export function useLibraryItem({ itemID, itemType }: { itemID: LibraryItemID; itemType: LibraryItemType }) { - const { data: rsForm } = useQuery({ - ...rsformsApi.getRSFormQueryOptions({ itemID }), - enabled: itemType === LibraryItemType.RSFORM - }); - const { data: oss } = useQuery({ - ...ossApi.getOssQueryOptions({ itemID }), - enabled: itemType === LibraryItemType.OSS - }); - return { - item: - itemType === LibraryItemType.RSFORM - ? (rsForm as ILibraryItemVersioned | undefined) - : (oss as ILibraryItemVersioned | undefined) - }; -} diff --git a/rsconcept/frontend/src/backend/library/useIsProcessingLibrary.tsx b/rsconcept/frontend/src/backend/library/useMutatingLibrary.tsx similarity index 73% rename from rsconcept/frontend/src/backend/library/useIsProcessingLibrary.tsx rename to rsconcept/frontend/src/backend/library/useMutatingLibrary.tsx index d906cc9b..c28906c8 100644 --- a/rsconcept/frontend/src/backend/library/useIsProcessingLibrary.tsx +++ b/rsconcept/frontend/src/backend/library/useMutatingLibrary.tsx @@ -1,10 +1,11 @@ import { useIsMutating } from '@tanstack/react-query'; -import { ossApi } from '../oss/api'; -import { rsformsApi } from '../rsform/api'; +import { ossApi } from '@/backend/oss/api'; +import { rsformsApi } from '@/backend/rsform/api'; + import { libraryApi } from './api'; -export const useIsProcessingLibrary = () => { +export const useMutatingLibrary = () => { const countMutations = useIsMutating({ mutationKey: [libraryApi.baseKey] }); const countOss = useIsMutating({ mutationKey: [ossApi.baseKey] }); const countRSForm = useIsMutating({ mutationKey: [rsformsApi.baseKey] }); diff --git a/rsconcept/frontend/src/backend/library/useRenameLocation.tsx b/rsconcept/frontend/src/backend/library/useRenameLocation.tsx index 0767c7e8..17394d93 100644 --- a/rsconcept/frontend/src/backend/library/useRenameLocation.tsx +++ b/rsconcept/frontend/src/backend/library/useRenameLocation.tsx @@ -1,5 +1,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { ossApi } from '@/backend/oss/api'; +import { rsformsApi } from '@/backend/rsform/api'; + import { IRenameLocationDTO, libraryApi } from './api'; export const useRenameLocation = () => { @@ -7,7 +10,12 @@ export const useRenameLocation = () => { const mutation = useMutation({ mutationKey: [libraryApi.baseKey, 'rename-location'], mutationFn: libraryApi.renameLocation, - onSuccess: () => client.invalidateQueries({ queryKey: [libraryApi.baseKey] }) + onSuccess: () => + Promise.allSettled([ + client.invalidateQueries({ queryKey: [libraryApi.baseKey] }), + client.invalidateQueries({ queryKey: [rsformsApi.baseKey] }), + client.invalidateQueries({ queryKey: [ossApi.baseKey] }) + ]) }); return { renameLocation: ( diff --git a/rsconcept/frontend/src/backend/library/useSetAccessPolicy.tsx b/rsconcept/frontend/src/backend/library/useSetAccessPolicy.tsx index b492d401..1754b768 100644 --- a/rsconcept/frontend/src/backend/library/useSetAccessPolicy.tsx +++ b/rsconcept/frontend/src/backend/library/useSetAccessPolicy.tsx @@ -1,7 +1,9 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { ossApi } from '@/backend/oss/api'; import { rsformsApi } from '@/backend/rsform/api'; -import { AccessPolicy, ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library'; +import { AccessPolicy, ILibraryItem, LibraryItemID } from '@/models/library'; +import { IOperationSchemaData } from '@/models/oss'; import { libraryApi } from './api'; @@ -11,21 +13,29 @@ export const useSetAccessPolicy = () => { mutationKey: [libraryApi.baseKey, 'set-location'], mutationFn: libraryApi.setAccessPolicy, onSuccess: (_, variables) => { + const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey; + const ossData: IOperationSchemaData | undefined = client.getQueryData(ossKey); + if (ossData) { + client.setQueryData(ossKey, { ...ossData, access_policy: variables.policy }); + return Promise.allSettled([ + client.invalidateQueries({ queryKey: libraryApi.libraryListKey }), + ...ossData.items + .map(item => { + if (!item.result) { + return; + } + const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey; + return client.invalidateQueries({ queryKey: itemKey }); + }) + .filter(item => !!item) + ]); + } + + const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey; + client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, access_policy: variables.policy })); client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => prev?.map(item => (item.id === variables.itemID ? { ...item, access_policy: variables.policy } : item)) ); - client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey, prev => { - if (!prev) { - return undefined; - } - if (prev.item_type === LibraryItemType.OSS) { - client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }).catch(console.error); - } - return { - ...prev, - access_policy: variables.policy - }; - }); } }); diff --git a/rsconcept/frontend/src/backend/library/useSetEditors.tsx b/rsconcept/frontend/src/backend/library/useSetEditors.tsx index 5eaca0f0..7680d4b1 100644 --- a/rsconcept/frontend/src/backend/library/useSetEditors.tsx +++ b/rsconcept/frontend/src/backend/library/useSetEditors.tsx @@ -1,10 +1,10 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { ossApi } from '@/backend/oss/api'; import { rsformsApi } from '@/backend/rsform/api'; import { LibraryItemID } from '@/models/library'; import { UserID } from '@/models/user'; -import { ossApi } from '../oss/api'; import { libraryApi } from './api'; export const useSetEditors = () => { @@ -17,19 +17,21 @@ export const useSetEditors = () => { const ossData = client.getQueryData(ossKey); if (ossData) { client.setQueryData(ossKey, { ...ossData, editors: variables.editors }); - Promise.allSettled([ - ...ossData.items.map(item => { - if (!item.result) { - return; - } - const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey; - return client.invalidateQueries({ queryKey: itemKey }); - }) - ]).catch(console.error); - } else { - const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey; - client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, editors: variables.editors })); + return Promise.allSettled( + ossData.items + .map(item => { + if (!item.result) { + return; + } + const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey; + return client.invalidateQueries({ queryKey: itemKey }); + }) + .filter(item => !!item) + ); } + + const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey; + client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, editors: variables.editors })); } }); diff --git a/rsconcept/frontend/src/backend/library/useSetLocation.tsx b/rsconcept/frontend/src/backend/library/useSetLocation.tsx index fd370b0a..80803e83 100644 --- a/rsconcept/frontend/src/backend/library/useSetLocation.tsx +++ b/rsconcept/frontend/src/backend/library/useSetLocation.tsx @@ -1,7 +1,9 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { ossApi } from '@/backend/oss/api'; import { rsformsApi } from '@/backend/rsform/api'; -import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library'; +import { ILibraryItem, LibraryItemID } from '@/models/library'; +import { IOperationSchemaData } from '@/models/oss'; import { libraryApi } from './api'; @@ -11,43 +13,29 @@ export const useSetLocation = () => { mutationKey: [libraryApi.baseKey, 'set-location'], mutationFn: libraryApi.setLocation, onSuccess: (_, variables) => { - // const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey; - // const ossData = client.getQueryData(ossKey); - // if (ossData) { - // client.setQueryData(ossKey, { ...ossData, editors: variables.editors }); - // Promise.allSettled([ - // client.invalidateQueries({ queryKey: libraryApi.libraryListKey }), - // ...ossData.items.map(item => { - // if (!item.result) { - // return; - // } - // const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey; - // return client.invalidateQueries({ queryKey: itemKey }); - // }) - // ]).catch(console.error); - // } else { - // const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey; - // client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, editors: variables.editors })); - // client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => - // prev?.map(item => (item.id === variables.itemID ? { ...item, editors: variables.editors } : item)) - // ); - // } + const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey; + const ossData: IOperationSchemaData | undefined = client.getQueryData(ossKey); + if (ossData) { + client.setQueryData(ossKey, { ...ossData, location: variables.location }); + return Promise.allSettled([ + client.invalidateQueries({ queryKey: libraryApi.libraryListKey }), + ...ossData.items + .map(item => { + if (!item.result) { + return; + } + const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey; + return client.invalidateQueries({ queryKey: itemKey }); + }) + .filter(item => !!item) + ]); + } + const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey; + client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, location: variables.location })); client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => prev?.map(item => (item.id === variables.itemID ? { ...item, location: variables.location } : item)) ); - client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey, prev => { - if (!prev) { - return undefined; - } - if (prev.item_type === LibraryItemType.OSS) { - client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }).catch(console.error); - } - return { - ...prev, - location: variables.location - }; - }); } }); diff --git a/rsconcept/frontend/src/backend/library/useSetOwner.tsx b/rsconcept/frontend/src/backend/library/useSetOwner.tsx index 24cc6213..7ec5ff84 100644 --- a/rsconcept/frontend/src/backend/library/useSetOwner.tsx +++ b/rsconcept/frontend/src/backend/library/useSetOwner.tsx @@ -1,7 +1,9 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { ossApi } from '@/backend/oss/api'; import { rsformsApi } from '@/backend/rsform/api'; -import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library'; +import { ILibraryItem, LibraryItemID } from '@/models/library'; +import { IOperationSchemaData } from '@/models/oss'; import { UserID } from '@/models/user'; import { libraryApi } from './api'; @@ -12,21 +14,29 @@ export const useSetOwner = () => { mutationKey: [libraryApi.baseKey, 'set-owner'], mutationFn: libraryApi.setOwner, onSuccess: (_, variables) => { + const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey; + const ossData: IOperationSchemaData | undefined = client.getQueryData(ossKey); + if (ossData) { + client.setQueryData(ossKey, { ...ossData, owner: variables.owner }); + return Promise.allSettled([ + client.invalidateQueries({ queryKey: libraryApi.libraryListKey }), + ...ossData.items + .map(item => { + if (!item.result) { + return; + } + const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey; + return client.invalidateQueries({ queryKey: itemKey }); + }) + .filter(item => !!item) + ]); + } + + const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey; + client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, owner: variables.owner })); client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => prev?.map(item => (item.id === variables.itemID ? { ...item, owner: variables.owner } : item)) ); - client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey, prev => { - if (!prev) { - return undefined; - } - if (prev.item_type === LibraryItemType.OSS) { - client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }).catch(console.error); - } - return { - ...prev, - owner: variables.owner - }; - }); } }); diff --git a/rsconcept/frontend/src/backend/library/useUpdateItem.tsx b/rsconcept/frontend/src/backend/library/useUpdateItem.tsx index 78fc4e65..4c53e9ed 100644 --- a/rsconcept/frontend/src/backend/library/useUpdateItem.tsx +++ b/rsconcept/frontend/src/backend/library/useUpdateItem.tsx @@ -1,7 +1,9 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { rsformsApi } from '@/backend/rsform/api'; -import { ILibraryItem } from '@/models/library'; +import { ossApi } from '@/backend/oss/api'; +import { ILibraryItem, LibraryItemType } from '@/models/library'; +import { IOperationSchemaData } from '@/models/oss'; +import { IRSFormData } from '@/models/rsform'; import { ILibraryUpdateDTO, libraryApi } from './api'; @@ -11,17 +13,23 @@ export const useUpdateItem = () => { mutationKey: [libraryApi.baseKey, 'update-item'], mutationFn: libraryApi.updateItem, onSuccess: (data: ILibraryItem) => { - client - .cancelQueries({ queryKey: libraryApi.libraryListKey }) - .then(async () => { - client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => - prev?.map(item => (item.id === data.id ? data : item)) + const itemKey = libraryApi.getItemQueryOptions({ itemID: data.id, itemType: data.item_type }).queryKey; + client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => + prev?.map(item => (item.id === data.id ? data : item)) + ); + client.setQueryData(itemKey, (prev: IRSFormData | IOperationSchemaData | undefined) => + !prev ? undefined : { ...prev, ...data } + ); + if (data.item_type === LibraryItemType.RSFORM) { + const schema: IRSFormData | undefined = client.getQueryData(itemKey); + if (schema) { + return Promise.allSettled( + schema.oss.map(item => + client.invalidateQueries({ queryKey: ossApi.getOssQueryOptions({ itemID: item.id }).queryKey }) + ) ); - await client.invalidateQueries({ - queryKey: [rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey] - }); - }) - .catch(console.error); + } + } } }); return { diff --git a/rsconcept/frontend/src/backend/library/useVersionRestore.tsx b/rsconcept/frontend/src/backend/library/useVersionRestore.tsx index 4aa4f887..bceecc54 100644 --- a/rsconcept/frontend/src/backend/library/useVersionRestore.tsx +++ b/rsconcept/frontend/src/backend/library/useVersionRestore.tsx @@ -1,6 +1,5 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp'; import { rsformsApi } from '@/backend/rsform/api'; import { VersionID } from '@/models/library'; @@ -8,13 +7,12 @@ import { libraryApi } from './api'; export const useVersionRestore = () => { const client = useQueryClient(); - const { updateTimestamp } = useUpdateTimestamp(); const mutation = useMutation({ mutationKey: [libraryApi.baseKey, 'restore-version'], mutationFn: libraryApi.versionRestore, onSuccess: data => { client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data); - updateTimestamp(data.id); + return client.invalidateQueries({ queryKey: [libraryApi.baseKey] }); } }); return { diff --git a/rsconcept/frontend/src/backend/oss/useInputCreate.tsx b/rsconcept/frontend/src/backend/oss/useInputCreate.tsx index 0544ca85..5b26b9d0 100644 --- a/rsconcept/frontend/src/backend/oss/useInputCreate.tsx +++ b/rsconcept/frontend/src/backend/oss/useInputCreate.tsx @@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { DataCallback } from '@/backend/apiTransport'; import { libraryApi } from '@/backend/library/api'; +import { rsformsApi } from '@/backend/rsform/api'; import { ILibraryItem, LibraryItemID } from '@/models/library'; import { ITargetOperation, ossApi } from './api'; @@ -11,9 +12,12 @@ export const useInputCreate = () => { const mutation = useMutation({ mutationKey: [ossApi.baseKey, 'input-create'], mutationFn: ossApi.inputCreate, - onSuccess: async data => { + onSuccess: data => { client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss); - await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }); + return Promise.allSettled([ + client.invalidateQueries({ queryKey: libraryApi.libraryListKey }), + client.invalidateQueries({ queryKey: [rsformsApi.baseKey] }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/oss/useInputUpdate.tsx b/rsconcept/frontend/src/backend/oss/useInputUpdate.tsx index 11aef44f..368def8e 100644 --- a/rsconcept/frontend/src/backend/oss/useInputUpdate.tsx +++ b/rsconcept/frontend/src/backend/oss/useInputUpdate.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { libraryApi } from '@/backend/library/api'; +import { rsformsApi } from '@/backend/rsform/api'; import { LibraryItemID } from '@/models/library'; import { IInputUpdateDTO, ossApi } from './api'; @@ -10,9 +11,12 @@ export const useInputUpdate = () => { const mutation = useMutation({ mutationKey: [ossApi.baseKey, 'input-update'], mutationFn: ossApi.inputUpdate, - onSuccess: async data => { + onSuccess: data => { client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data); - await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }); + return Promise.allSettled([ + client.invalidateQueries({ queryKey: libraryApi.libraryListKey }), + client.invalidateQueries({ queryKey: [rsformsApi.baseKey] }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/oss/useIsProcessingOss.tsx b/rsconcept/frontend/src/backend/oss/useMutatingOss.tsx similarity index 88% rename from rsconcept/frontend/src/backend/oss/useIsProcessingOss.tsx rename to rsconcept/frontend/src/backend/oss/useMutatingOss.tsx index eabf01fb..da1406f0 100644 --- a/rsconcept/frontend/src/backend/oss/useIsProcessingOss.tsx +++ b/rsconcept/frontend/src/backend/oss/useMutatingOss.tsx @@ -4,7 +4,7 @@ import { libraryApi } from '@/backend/library/api'; import { ossApi } from './api'; -export const useIsProcessingOss = () => { +export const useMutatingOss = () => { const countLibrary = useIsMutating({ mutationKey: [libraryApi.baseKey] }); const countOss = useIsMutating({ mutationKey: [ossApi.baseKey] }); return countLibrary + countOss !== 0; diff --git a/rsconcept/frontend/src/backend/oss/useOperationDelete.tsx b/rsconcept/frontend/src/backend/oss/useOperationDelete.tsx index eecd07a5..51884ae8 100644 --- a/rsconcept/frontend/src/backend/oss/useOperationDelete.tsx +++ b/rsconcept/frontend/src/backend/oss/useOperationDelete.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { libraryApi } from '@/backend/library/api'; +import { rsformsApi } from '@/backend/rsform/api'; import { LibraryItemID } from '@/models/library'; import { IOperationDeleteDTO, ossApi } from './api'; @@ -10,9 +11,12 @@ export const useOperationDelete = () => { const mutation = useMutation({ mutationKey: [ossApi.baseKey, 'operation-delete'], mutationFn: ossApi.operationDelete, - onSuccess: async data => { + onSuccess: data => { client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data); - await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }); + return Promise.allSettled([ + client.invalidateQueries({ queryKey: libraryApi.libraryListKey }), + client.invalidateQueries({ queryKey: [rsformsApi.baseKey] }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/oss/useOperationExecute.tsx b/rsconcept/frontend/src/backend/oss/useOperationExecute.tsx index c8d030f1..23477718 100644 --- a/rsconcept/frontend/src/backend/oss/useOperationExecute.tsx +++ b/rsconcept/frontend/src/backend/oss/useOperationExecute.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { libraryApi } from '@/backend/library/api'; +import { rsformsApi } from '@/backend/rsform/api'; import { LibraryItemID } from '@/models/library'; import { ITargetOperation, ossApi } from './api'; @@ -10,9 +11,12 @@ export const useOperationExecute = () => { const mutation = useMutation({ mutationKey: [ossApi.baseKey, 'operation-execute'], mutationFn: ossApi.operationExecute, - onSuccess: async data => { + onSuccess: data => { client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data); - await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }); + return Promise.allSettled([ + client.invalidateQueries({ queryKey: libraryApi.libraryListKey }), + client.invalidateQueries({ queryKey: [rsformsApi.baseKey] }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/oss/useOperationUpdate.tsx b/rsconcept/frontend/src/backend/oss/useOperationUpdate.tsx index 0f982040..09b1e484 100644 --- a/rsconcept/frontend/src/backend/oss/useOperationUpdate.tsx +++ b/rsconcept/frontend/src/backend/oss/useOperationUpdate.tsx @@ -1,7 +1,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { libraryApi } from '@/backend/library/api'; -import { LibraryItemID } from '@/models/library'; +import { rsformsApi } from '@/backend/rsform/api'; +import { ILibraryItem, LibraryItemID } from '@/models/library'; import { IOperationUpdateDTO, ossApi } from './api'; @@ -10,9 +11,22 @@ export const useOperationUpdate = () => { const mutation = useMutation({ mutationKey: [ossApi.baseKey, 'operation-update'], mutationFn: ossApi.operationUpdate, - onSuccess: async data => { + onSuccess: (data, variables) => { client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data); - await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }); + const schemaID = data.items.find(item => item.id === variables.data.target)?.result; + if (!schemaID) { + return; + } + client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => + !prev + ? undefined + : prev.map(item => + item.id === schemaID ? { ...item, ...variables.data.item_data, time_update: Date() } : item + ) + ); + return client.invalidateQueries({ + queryKey: rsformsApi.getRSFormQueryOptions({ itemID: schemaID }).queryKey + }); } }); return { diff --git a/rsconcept/frontend/src/backend/oss/useRelocateConstituents.tsx b/rsconcept/frontend/src/backend/oss/useRelocateConstituents.tsx index dd05fc3f..5cdb5f26 100644 --- a/rsconcept/frontend/src/backend/oss/useRelocateConstituents.tsx +++ b/rsconcept/frontend/src/backend/oss/useRelocateConstituents.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { libraryApi } from '@/backend/library/api'; +import { rsformsApi } from '@/backend/rsform/api'; import { LibraryItemID } from '@/models/library'; import { ICstRelocateDTO, ossApi } from './api'; @@ -10,9 +11,12 @@ export const useRelocateConstituents = () => { const mutation = useMutation({ mutationKey: [ossApi.baseKey, 'relocate-constituents'], mutationFn: ossApi.relocateConstituents, - onSuccess: async data => { + onSuccess: data => { client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data); - await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }); + return Promise.allSettled([ + client.invalidateQueries({ queryKey: libraryApi.libraryListKey }), + client.invalidateQueries({ queryKey: [rsformsApi.baseKey] }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/queryClient.ts b/rsconcept/frontend/src/backend/queryClient.ts index d36b81b0..059b7f25 100644 --- a/rsconcept/frontend/src/backend/queryClient.ts +++ b/rsconcept/frontend/src/backend/queryClient.ts @@ -14,7 +14,7 @@ export const queryClient = new QueryClient({ queries: { staleTime: DELAYS.staleDefault, gcTime: DELAYS.garbageCollection, - retry: 3, + retry: false, refetchOnWindowFocus: true, refetchOnMount: true, refetchOnReconnect: true diff --git a/rsconcept/frontend/src/backend/rsform/useCstCreate.tsx b/rsconcept/frontend/src/backend/rsform/useCstCreate.tsx index 03bbef6b..f3a10303 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstCreate.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstCreate.tsx @@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { DataCallback } from '@/backend/apiTransport'; import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp'; +import { ossApi } from '@/backend/oss/api'; import { LibraryItemID } from '@/models/library'; import { IConstituentaMeta } from '@/models/rsform'; @@ -16,7 +17,14 @@ export const useCstCreate = () => { onSuccess: data => { client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema); updateTimestamp(data.schema.id); - // TODO: invalidate OSS? + + return Promise.allSettled([ + client.invalidateQueries({ queryKey: [ossApi.baseKey] }), + client.invalidateQueries({ + queryKey: [rsformsApi.baseKey], + predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.schema.id + }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/rsform/useCstDelete.tsx b/rsconcept/frontend/src/backend/rsform/useCstDelete.tsx index ed7f8a72..e982a6ef 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstDelete.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstDelete.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp'; +import { ossApi } from '@/backend/oss/api'; import { LibraryItemID } from '@/models/library'; import { IConstituentaList } from '@/models/rsform'; @@ -15,7 +16,14 @@ export const useCstDelete = () => { onSuccess: data => { client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data); updateTimestamp(data.id); - // TODO: invalidate OSS? + + return Promise.allSettled([ + client.invalidateQueries({ queryKey: [ossApi.baseKey] }), + client.invalidateQueries({ + queryKey: [rsformsApi.baseKey], + predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id + }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/rsform/useCstMove.tsx b/rsconcept/frontend/src/backend/rsform/useCstMove.tsx index 6a85715b..ec8b7286 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstMove.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstMove.tsx @@ -14,7 +14,6 @@ export const useCstMove = () => { onSuccess: data => { client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data); updateTimestamp(data.id); - // TODO: invalidate OSS? } }); return { diff --git a/rsconcept/frontend/src/backend/rsform/useCstRename.tsx b/rsconcept/frontend/src/backend/rsform/useCstRename.tsx index e5f6edb8..76d9d598 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstRename.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstRename.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp'; +import { ossApi } from '@/backend/oss/api'; import { LibraryItemID } from '@/models/library'; import { ICstRenameDTO, rsformsApi } from './api'; @@ -14,7 +15,14 @@ export const useCstRename = () => { onSuccess: data => { client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema); updateTimestamp(data.schema.id); - // TODO: invalidate OSS? + + return Promise.allSettled([ + client.invalidateQueries({ queryKey: [ossApi.baseKey] }), + client.invalidateQueries({ + queryKey: [rsformsApi.baseKey], + predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.schema.id + }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/rsform/useCstSubstitute.tsx b/rsconcept/frontend/src/backend/rsform/useCstSubstitute.tsx index 997ae772..e0c18d63 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstSubstitute.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstSubstitute.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp'; +import { ossApi } from '@/backend/oss/api'; import { LibraryItemID } from '@/models/library'; import { ICstSubstitutions } from '@/models/oss'; @@ -15,7 +16,14 @@ export const useCstSubstitute = () => { onSuccess: data => { client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data); updateTimestamp(data.id); - // TODO: invalidate OSS? + + return Promise.allSettled([ + client.invalidateQueries({ queryKey: [ossApi.baseKey] }), + client.invalidateQueries({ + queryKey: [rsformsApi.baseKey], + predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id + }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/rsform/useCstUpdate.tsx b/rsconcept/frontend/src/backend/rsform/useCstUpdate.tsx index 96d21b8d..4de86d32 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstUpdate.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstUpdate.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp'; +import { ossApi } from '@/backend/oss/api'; import { LibraryItemID } from '@/models/library'; import { ICstUpdateDTO, rsformsApi } from './api'; @@ -11,12 +12,24 @@ export const useCstUpdate = () => { const mutation = useMutation({ mutationKey: [rsformsApi.baseKey, 'update-cst'], mutationFn: rsformsApi.cstUpdate, - onSuccess: async (_, variables) => { + onSuccess: (newCst, variables) => { + client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey, prev => + !prev + ? undefined + : { + ...prev, + items: prev.items.map(item => (item.id === newCst.id ? { ...item, ...newCst } : item)) + } + ); updateTimestamp(variables.itemID); - await client.invalidateQueries({ - queryKey: [rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey] - }); - // TODO: invalidate OSS? + + return Promise.allSettled([ + client.invalidateQueries({ queryKey: [ossApi.baseKey] }), + client.invalidateQueries({ + queryKey: [rsformsApi.baseKey], + predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== variables.itemID + }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/rsform/useInlineSynthesis.tsx b/rsconcept/frontend/src/backend/rsform/useInlineSynthesis.tsx index 802f53ad..4cb68644 100644 --- a/rsconcept/frontend/src/backend/rsform/useInlineSynthesis.tsx +++ b/rsconcept/frontend/src/backend/rsform/useInlineSynthesis.tsx @@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { DataCallback } from '@/backend/apiTransport'; import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp'; +import { ossApi } from '@/backend/oss/api'; import { LibraryItemID } from '@/models/library'; import { IRSFormData } from '@/models/rsform'; @@ -16,7 +17,14 @@ export const useInlineSynthesis = () => { onSuccess: data => { client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data); updateTimestamp(data.id); - // TODO: invalidate OSS? + + return Promise.allSettled([ + client.invalidateQueries({ queryKey: [ossApi.baseKey] }), + client.invalidateQueries({ + queryKey: [rsformsApi.baseKey], + predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id + }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/rsform/useIsProcessingRSForm.tsx b/rsconcept/frontend/src/backend/rsform/useMutatingRSForm.tsx similarity index 88% rename from rsconcept/frontend/src/backend/rsform/useIsProcessingRSForm.tsx rename to rsconcept/frontend/src/backend/rsform/useMutatingRSForm.tsx index 48a6a777..0e047591 100644 --- a/rsconcept/frontend/src/backend/rsform/useIsProcessingRSForm.tsx +++ b/rsconcept/frontend/src/backend/rsform/useMutatingRSForm.tsx @@ -4,7 +4,7 @@ import { libraryApi } from '@/backend/library/api'; import { rsformsApi } from './api'; -export const useIsProcessingRSForm = () => { +export const useMutatingRSForm = () => { const countLibrary = useIsMutating({ mutationKey: [libraryApi.baseKey] }); const countRsform = useIsMutating({ mutationKey: [rsformsApi.baseKey] }); return countLibrary + countRsform !== 0; diff --git a/rsconcept/frontend/src/backend/rsform/useProduceStructure.tsx b/rsconcept/frontend/src/backend/rsform/useProduceStructure.tsx index 72de9a6a..6529d6d1 100644 --- a/rsconcept/frontend/src/backend/rsform/useProduceStructure.tsx +++ b/rsconcept/frontend/src/backend/rsform/useProduceStructure.tsx @@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { DataCallback } from '@/backend/apiTransport'; import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp'; +import { ossApi } from '@/backend/oss/api'; import { LibraryItemID } from '@/models/library'; import { ConstituentaID, ITargetCst } from '@/models/rsform'; @@ -16,7 +17,14 @@ export const useProduceStructure = () => { onSuccess: data => { client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema); updateTimestamp(data.schema.id); - // TODO: invalidate OSS? + + return Promise.allSettled([ + client.invalidateQueries({ queryKey: [ossApi.baseKey] }), + client.invalidateQueries({ + queryKey: [rsformsApi.baseKey], + predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.schema.id + }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/rsform/useResetAliases.tsx b/rsconcept/frontend/src/backend/rsform/useResetAliases.tsx index 2315e86a..adbb67ec 100644 --- a/rsconcept/frontend/src/backend/rsform/useResetAliases.tsx +++ b/rsconcept/frontend/src/backend/rsform/useResetAliases.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp'; +import { ossApi } from '@/backend/oss/api'; import { LibraryItemID } from '@/models/library'; import { rsformsApi } from './api'; @@ -14,7 +15,14 @@ export const useResetAliases = () => { onSuccess: data => { client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data); updateTimestamp(data.id); - // TODO: invalidate OSS? + + return Promise.allSettled([ + client.invalidateQueries({ queryKey: [ossApi.baseKey] }), + client.invalidateQueries({ + queryKey: [rsformsApi.baseKey], + predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id + }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/rsform/useUploadTRS.tsx b/rsconcept/frontend/src/backend/rsform/useUploadTRS.tsx index 2a49118b..e76a2f25 100644 --- a/rsconcept/frontend/src/backend/rsform/useUploadTRS.tsx +++ b/rsconcept/frontend/src/backend/rsform/useUploadTRS.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { libraryApi } from '@/backend/library/api'; +import { ossApi } from '@/backend/oss/api'; import { ILibraryItem } from '@/models/library'; import { IRSFormUploadDTO, rsformsApi } from './api'; @@ -15,6 +16,14 @@ export const useUploadTRS = () => { client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => prev?.map(item => (item.id === data.id ? data : item)) ); + + return Promise.allSettled([ + client.invalidateQueries({ queryKey: [ossApi.baseKey] }), + client.invalidateQueries({ + queryKey: [rsformsApi.baseKey], + predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id + }) + ]); } }); return { diff --git a/rsconcept/frontend/src/backend/users/useSignup.tsx b/rsconcept/frontend/src/backend/users/useSignup.tsx index 1dba7864..319a736f 100644 --- a/rsconcept/frontend/src/backend/users/useSignup.tsx +++ b/rsconcept/frontend/src/backend/users/useSignup.tsx @@ -9,7 +9,7 @@ export const useSignup = () => { const mutation = useMutation({ mutationKey: ['signup'], mutationFn: usersApi.signup, - onSuccess: async () => await client.invalidateQueries({ queryKey: [usersApi.baseKey] }) + onSuccess: () => client.invalidateQueries({ queryKey: usersApi.getUsersQueryOptions().queryKey }) }); return { signup: ( diff --git a/rsconcept/frontend/src/backend/users/useUpdateProfile.tsx b/rsconcept/frontend/src/backend/users/useUpdateProfile.tsx index 86c24498..654c4fe7 100644 --- a/rsconcept/frontend/src/backend/users/useUpdateProfile.tsx +++ b/rsconcept/frontend/src/backend/users/useUpdateProfile.tsx @@ -2,14 +2,15 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { IUpdateProfileDTO, usersApi } from './api'; -// TODO: reload users / optimistic update - export const useUpdateProfile = () => { const client = useQueryClient(); const mutation = useMutation({ mutationKey: ['update-profile'], mutationFn: usersApi.updateProfile, - onSuccess: async () => await client.invalidateQueries({ queryKey: [usersApi.baseKey] }) + onSuccess: data => { + client.setQueryData(usersApi.getProfileQueryOptions().queryKey, data); + return client.invalidateQueries({ queryKey: usersApi.getUsersQueryOptions().queryKey }); + } }); return { updateProfile: (data: IUpdateProfileDTO) => mutation.mutate(data), diff --git a/rsconcept/frontend/src/components/info/InfoError.tsx b/rsconcept/frontend/src/components/info/InfoError.tsx index 1c55ba09..03fbb878 100644 --- a/rsconcept/frontend/src/components/info/InfoError.tsx +++ b/rsconcept/frontend/src/components/info/InfoError.tsx @@ -16,7 +16,28 @@ function DescribeError({ error }: { error: ErrorData }) { } else if (typeof error === 'string') { return

{error}

; } else if (!axios.isAxiosError(error)) { - return ; + return ( +
+

+ Error: {error.name} +

+

+ Message: {error.message} +

+ {error.stack && ( +
+            {error.stack}
+          
+ )} +
+ ); } if (!error?.response) { return

Нет ответа от сервера

; diff --git a/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx b/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx index 93ba8303..01cebd52 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx +++ b/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; -import { useIsProcessingLibrary } from '@/backend/library/useIsProcessingLibrary'; +import { useMutatingLibrary } from '@/backend/library/useMutatingLibrary'; import { useVersionDelete } from '@/backend/library/useVersionDelete'; import { useVersionUpdate } from '@/backend/library/useVersionUpdate'; import { IconReset, IconSave } from '@/components/Icons'; @@ -22,7 +22,7 @@ export interface DlgEditVersionsProps { function DlgEditVersions() { const { item, afterDelete } = useDialogsStore(state => state.props as DlgEditVersionsProps); - const processing = useIsProcessingLibrary(); + const processing = useMutatingLibrary(); const { versionDelete } = useVersionDelete(); const { versionUpdate } = useVersionUpdate(); diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssCard/EditorOssCard.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/EditorOssCard.tsx index 7c8db20b..ce8d9c90 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssCard/EditorOssCard.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/EditorOssCard.tsx @@ -3,7 +3,6 @@ import clsx from 'clsx'; import FlexColumn from '@/components/ui/FlexColumn'; -import { LibraryItemType } from '@/models/library'; import EditorLibraryItem from '@/pages/RSFormPage/EditorRSFormCard/EditorLibraryItem'; import ToolbarRSFormCard from '@/pages/RSFormPage/EditorRSFormCard/ToolbarRSFormCard'; import { useModificationStore } from '@/stores/modification'; @@ -47,7 +46,7 @@ function EditorOssCard() { > - + {controller.schema ? : null} diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx index 2e9d7494..050bc4cb 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx @@ -5,7 +5,7 @@ import { useEffect, useState } from 'react'; import { ILibraryUpdateDTO } from '@/backend/library/api'; import { useUpdateItem } from '@/backend/library/useUpdateItem'; -import { useIsProcessingOss } from '@/backend/oss/useIsProcessingOss'; +import { useMutatingOss } from '@/backend/oss/useMutatingOss'; import { IconSave } from '@/components/Icons'; import SubmitButton from '@/components/ui/SubmitButton'; import TextArea from '@/components/ui/TextArea'; @@ -24,7 +24,7 @@ function FormOSS({ id }: FormOSSProps) { const { updateItem: update } = useUpdateItem(); const controller = useOssEdit(); const { isModified, setIsModified } = useModificationStore(); - const isProcessing = useIsProcessingOss(); + const isProcessing = useMutatingOss(); const schema = controller.schema; const [title, setTitle] = useState(schema.title); diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx index 008ac3c9..4c43f9b2 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx @@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import { useIsProcessingOss } from '@/backend/oss/useIsProcessingOss'; +import { useMutatingOss } from '@/backend/oss/useMutatingOss'; import { IconChild, IconConnect, @@ -50,7 +50,7 @@ function NodeContextMenu({ onRelocateConstituents }: NodeContextMenuProps) { const controller = useOssEdit(); - const isProcessing = useIsProcessingOss(); + const isProcessing = useMutatingOss(); const [isOpen, setIsOpen] = useState(false); const ref = useRef(null); diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx index da1c8ca4..7669fdb4 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx @@ -20,7 +20,7 @@ import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { urls } from '@/app/urls'; import { useLibrary } from '@/backend/library/useLibrary'; import { useInputCreate } from '@/backend/oss/useInputCreate'; -import { useIsProcessingOss } from '@/backend/oss/useIsProcessingOss'; +import { useMutatingOss } from '@/backend/oss/useMutatingOss'; import { useOperationExecute } from '@/backend/oss/useOperationExecute'; import { useUpdatePositions } from '@/backend/oss/useUpdatePositions'; import { CProps } from '@/components/props'; @@ -50,7 +50,7 @@ function OssFlow() { const flow = useReactFlow(); const { setIsModified } = useModificationStore(); - const isProcessing = useIsProcessingOss(); + const isProcessing = useMutatingOss(); const showGrid = useOSSGraphStore(state => state.showGrid); const edgeAnimate = useOSSGraphStore(state => state.edgeAnimate); @@ -118,7 +118,7 @@ function OssFlow() { } function handleNodesChange(changes: NodeChange[]) { - if (changes.some(change => change.type === 'position' && change.position)) { + if (controller.isMutable && changes.some(change => change.type === 'position' && change.position)) { setIsModified(true); } onNodesChange(changes); diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx index f2c32ebf..679da677 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; -import { useIsProcessingOss } from '@/backend/oss/useIsProcessingOss'; +import { useMutatingOss } from '@/backend/oss/useMutatingOss'; import { IconAnimation, IconAnimationOff, @@ -52,7 +52,7 @@ function ToolbarOssGraph({ }: ToolbarOssGraphProps) { const controller = useOssEdit(); const { isModified } = useModificationStore(); - const isProcessing = useIsProcessingOss(); + const isProcessing = useMutatingOss(); const selectedOperation = controller.schema.operationByID.get(controller.selected[0]); const showGrid = useOSSGraphStore(state => state.showGrid); @@ -89,7 +89,6 @@ function ToolbarOssGraph({ } - disabled={!isModified} onClick={onResetPositions} /> state.role); const setRole = useRoleStore(state => state.setRole); diff --git a/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx b/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx index ffa2a203..b584865a 100644 --- a/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx +++ b/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx @@ -86,11 +86,9 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren UserRole.READER && !schema.read_only; const [showTooltip, setShowTooltip] = useState(true); - const [selected, setSelected] = useState([]); const showEditInput = useDialogsStore(state => state.showChangeInputSchema); diff --git a/rsconcept/frontend/src/pages/OssPage/OssPage.tsx b/rsconcept/frontend/src/pages/OssPage/OssPage.tsx index 999a70e8..ae870bfd 100644 --- a/rsconcept/frontend/src/pages/OssPage/OssPage.tsx +++ b/rsconcept/frontend/src/pages/OssPage/OssPage.tsx @@ -21,13 +21,6 @@ function OssPage() { const { isModified } = useModificationStore(); useBlockNavigation(isModified); - // useBlockNavigation( - // isModified && - // schema !== undefined && - // !!user && - // (user.is_staff || user.id == schema.owner || schema.editors.includes(user.id)) - // ); - if (!itemID) { router.replace(urls.page404); return null; diff --git a/rsconcept/frontend/src/pages/PasswordChangePage.tsx b/rsconcept/frontend/src/pages/PasswordChangePage.tsx index 81c68fa9..e6f01b27 100644 --- a/rsconcept/frontend/src/pages/PasswordChangePage.tsx +++ b/rsconcept/frontend/src/pages/PasswordChangePage.tsx @@ -6,7 +6,7 @@ import { useEffect, useState } from 'react'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { urls } from '@/app/urls'; -import { IPasswordTokenDTO, IResetPasswordDTO } from '@/backend/auth/api'; +import { IResetPasswordDTO } from '@/backend/auth/api'; import { useResetPassword } from '@/backend/auth/useResetPassword'; import InfoError, { ErrorData } from '@/components/info/InfoError'; import SubmitButton from '@/components/ui/SubmitButton'; @@ -48,10 +48,7 @@ function PasswordChangePage() { }, [newPassword, newPasswordRepeat, reset]); useEffect(() => { - const data: IPasswordTokenDTO = { - token: token ?? '' - }; - validateToken(data, () => setIsTokenValid(true)); + validateToken({ token: token ?? '' }, () => setIsTokenValid(true)); }, [token, validateToken]); return ( diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx index 7f4fcd28..c091b1f3 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx @@ -4,7 +4,7 @@ import clsx from 'clsx'; import { useState } from 'react'; import { useCstUpdate } from '@/backend/rsform/useCstUpdate'; -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import useWindowSize from '@/hooks/useWindowSize'; import { useMainHeight } from '@/stores/appLayout'; import { useDialogsStore } from '@/stores/dialogs'; @@ -33,7 +33,7 @@ function EditorConstituenta() { const [toggleReset, setToggleReset] = useState(false); - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); const disabled = !controller.activeCst || !controller.isContentEditable || isProcessing; const isNarrow = !!windowSize.width && windowSize.width <= SIDELIST_LAYOUT_THRESHOLD; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorControls.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorControls.tsx index 5ee5695b..c4844a38 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorControls.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorControls.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import { ICstRenameDTO } from '@/backend/rsform/api'; import { useCstRename } from '@/backend/rsform/useCstRename'; -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import { IconEdit } from '@/components/Icons'; import MiniButton from '@/components/ui/MiniButton'; import Overlay from '@/components/ui/Overlay'; @@ -23,7 +23,7 @@ interface EditorControlsProps { function EditorControls({ constituenta, disabled, onEditTerm }: EditorControlsProps) { const { schema } = useRSEdit(); const { isModified } = useModificationStore(); - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); const showRenameCst = useDialogsStore(state => state.showRenameCst); const { cstRename } = useCstRename(); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx index d45c4923..899a5027 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx @@ -6,7 +6,7 @@ import { toast } from 'react-toastify'; import { ICstUpdateDTO } from '@/backend/rsform/api'; import { useCstUpdate } from '@/backend/rsform/useCstUpdate'; -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import { IconChild, IconPredecessor, IconSave } from '@/components/Icons'; import { CProps } from '@/components/props'; import RefsInput from '@/components/RefsInput'; @@ -45,7 +45,7 @@ function FormConstituenta({ const { cstUpdate } = useCstUpdate(); const { schema, activeCst } = useRSEdit(); const { isModified, setIsModified } = useModificationStore(); - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); const [term, setTerm] = useState(activeCst?.term_raw ?? ''); const [textDefinition, setTextDefinition] = useState(activeCst?.definition_raw ?? ''); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/ToolbarConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/ToolbarConstituenta.tsx index 6be1990a..0a35c0c6 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/ToolbarConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/ToolbarConstituenta.tsx @@ -5,7 +5,7 @@ import clsx from 'clsx'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { urls } from '@/app/urls'; import { useFindPredecessor } from '@/backend/oss/useFindPredecessor'; -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import { IconClone, IconDestroy, @@ -53,7 +53,7 @@ function ToolbarConstituenta({ const showList = usePreferencesStore(state => state.showCstSideList); const toggleList = usePreferencesStore(state => state.toggleShowCstSideList); const { isModified } = useModificationStore(); - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); function viewPredecessor(target: ConstituentaID) { findPredecessor({ target: target }, reference => diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx index a3004f94..d40fded7 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx @@ -7,7 +7,7 @@ import { toast } from 'react-toastify'; import { DataCallback } from '@/backend/apiTransport'; import { ICheckConstituentaDTO } from '@/backend/rsform/api'; import { useCheckConstituenta } from '@/backend/rsform/useCheckConstituenta'; -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import BadgeHelp from '@/components/info/BadgeHelp'; import { CProps } from '@/components/props'; import RSInput from '@/components/RSInput'; @@ -65,7 +65,7 @@ function EditorRSExpression({ const rsInput = useRef(null); const [parseData, setParseData] = useState(undefined); - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); const showControls = usePreferencesStore(state => state.showExpressionControls); const showAST = useDialogsStore(state => state.showShowAST); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/ToolbarRSExpression.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/ToolbarRSExpression.tsx index de463193..f4f0687a 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/ToolbarRSExpression.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/ToolbarRSExpression.tsx @@ -1,4 +1,4 @@ -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import { IconControls, IconTree, IconTypeGraph } from '@/components/Icons'; import { CProps } from '@/components/props'; import MiniButton from '@/components/ui/MiniButton'; @@ -12,7 +12,7 @@ interface ToolbarRSExpressionProps { } function ToolbarRSExpression({ disabled, showTypeGraph, showAST }: ToolbarRSExpressionProps) { - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); const showControls = usePreferencesStore(state => state.showExpressionControls); const toggleControls = usePreferencesStore(state => state.toggleShowExpressionControls); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorLibraryItem.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorLibraryItem.tsx index 411abd95..e9faf4f8 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorLibraryItem.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorLibraryItem.tsx @@ -3,8 +3,7 @@ import { useIntl } from 'react-intl'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { urls } from '@/app/urls'; -import { useIsProcessingLibrary } from '@/backend/library/useIsProcessingLibrary'; -import { useLibraryItem } from '@/backend/library/useLibraryItem'; +import { useMutatingLibrary } from '@/backend/library/useMutatingLibrary'; import { useSetEditors } from '@/backend/library/useSetEditors'; import { useSetLocation } from '@/backend/library/useSetLocation'; import { useSetOwner } from '@/backend/library/useSetOwner'; @@ -26,7 +25,7 @@ import Overlay from '@/components/ui/Overlay'; import Tooltip from '@/components/ui/Tooltip'; import ValueIcon from '@/components/ui/ValueIcon'; import useDropdown from '@/hooks/useDropdown'; -import { ILibraryItemEditor, LibraryItemID, LibraryItemType } from '@/models/library'; +import { ILibraryItemEditor } from '@/models/library'; import { UserID, UserRole } from '@/models/user'; import { useDialogsStore } from '@/stores/dialogs'; import { useLibrarySearchStore } from '@/stores/librarySearch'; @@ -36,20 +35,17 @@ import { prefixes } from '@/utils/constants'; import { prompts } from '@/utils/labels'; interface EditorLibraryItemProps { - itemID: LibraryItemID; - itemType: LibraryItemType; controller: ILibraryItemEditor; } -function EditorLibraryItem({ itemID, itemType, controller }: EditorLibraryItemProps) { +function EditorLibraryItem({ controller }: EditorLibraryItemProps) { const getUserLabel = useLabelUser(); const role = useRoleStore(state => state.role); const intl = useIntl(); const router = useConceptNavigation(); const setGlobalLocation = useLibrarySearchStore(state => state.setLocation); - const { item } = useLibraryItem({ itemID, itemType }); - const isProcessing = useIsProcessingLibrary(); + const isProcessing = useMutatingLibrary(); const { isModified } = useModificationStore(); const { setOwner } = useSetOwner(); @@ -62,47 +58,34 @@ function EditorLibraryItem({ itemID, itemType, controller }: EditorLibraryItemPr const ownerSelector = useDropdown(); const onSelectUser = function (newValue: UserID) { ownerSelector.hide(); - if (newValue === item?.owner) { + if (newValue === controller.schema.owner) { return; } if (!window.confirm(prompts.ownerChange)) { return; } - setOwner({ itemID: itemID, owner: newValue }); + setOwner({ itemID: controller.schema.id, owner: newValue }); }; function handleOpenLibrary(event: CProps.EventMouse) { - if (!item) { - return; - } - setGlobalLocation(item.location); + setGlobalLocation(controller.schema.location); router.push(urls.library, event.ctrlKey || event.metaKey); } function handleEditLocation() { - if (!item) { - return; - } showEditLocation({ - initial: item.location, - onChangeLocation: newLocation => setLocation({ itemID: itemID, location: newLocation }) + initial: controller.schema.location, + onChangeLocation: newLocation => setLocation({ itemID: controller.schema.id, location: newLocation }) }); } function handleEditEditors() { - if (!item) { - return; - } showEditEditors({ - editors: item.editors, - onChangeEditors: newEditors => setEditors({ itemID: itemID, editors: newEditors }) + editors: controller.schema.editors, + onChangeEditors: newEditors => setEditors({ itemID: controller.schema.id, editors: newEditors }) }); } - if (!item) { - return null; - } - return (
@@ -116,7 +99,7 @@ function EditorLibraryItem({ itemID, itemType, controller }: EditorLibraryItemPr } - value={item.location} + value={controller.schema.location} title={controller.isAttachedToOSS ? 'Путь наследуется от ОСС' : 'Путь'} onClick={handleEditLocation} disabled={isModified || isProcessing || controller.isAttachedToOSS || role < UserRole.OWNER} @@ -128,7 +111,7 @@ function EditorLibraryItem({ itemID, itemType, controller }: EditorLibraryItemPr {ownerSelector.isOpen ? ( ) : null} @@ -137,7 +120,7 @@ function EditorLibraryItem({ itemID, itemType, controller }: EditorLibraryItemPr } - value={getUserLabel(item.owner)} + value={getUserLabel(controller.schema.owner)} title={controller.isAttachedToOSS ? 'Владелец наследуется от ОСС' : 'Владелец'} onClick={ownerSelector.toggle} disabled={isModified || isProcessing || controller.isAttachedToOSS || role < UserRole.OWNER} @@ -148,13 +131,13 @@ function EditorLibraryItem({ itemID, itemType, controller }: EditorLibraryItemPr id='editor_stats' dense icon={} - value={item.editors.length} + value={controller.schema.editors.length} onClick={handleEditEditors} disabled={isModified || isProcessing || role < UserRole.OWNER} /> }> - + @@ -162,7 +145,7 @@ function EditorLibraryItem({ itemID, itemType, controller }: EditorLibraryItemPr dense disabled icon={} - value={new Date(item.time_update).toLocaleString(intl.locale)} + value={new Date(controller.schema.time_update).toLocaleString(intl.locale)} title='Дата обновления' /> @@ -170,7 +153,7 @@ function EditorLibraryItem({ itemID, itemType, controller }: EditorLibraryItemPr dense disabled icon={} - value={new Date(item.time_create).toLocaleString(intl.locale, { + value={new Date(controller.schema.time_create).toLocaleString(intl.locale, { year: '2-digit', month: '2-digit', day: '2-digit' diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorRSFormCard.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorRSFormCard.tsx index 754195e5..81563d6a 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorRSFormCard.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorRSFormCard.tsx @@ -3,7 +3,6 @@ import clsx from 'clsx'; import FlexColumn from '@/components/ui/FlexColumn'; -import { LibraryItemType } from '@/models/library'; import { useModificationStore } from '@/stores/modification'; import { globals } from '@/utils/constants'; @@ -46,7 +45,7 @@ function EditorRSFormCard() { > - + {controller.schema ? : null} diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx index 0d9e6982..5d6f23a9 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx @@ -7,7 +7,7 @@ import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { urls } from '@/app/urls'; import { ILibraryUpdateDTO } from '@/backend/library/api'; import { useUpdateItem } from '@/backend/library/useUpdateItem'; -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import { IconSave } from '@/components/Icons'; import SelectVersion from '@/components/select/SelectVersion'; import Label from '@/components/ui/Label'; @@ -31,7 +31,7 @@ function FormRSForm({ id }: FormRSFormProps) { const schema = controller.schema; const { updateItem: update } = useUpdateItem(); const { isModified, setIsModified } = useModificationStore(); - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); const [title, setTitle] = useState(schema.title); const [alias, setAlias] = useState(schema.alias); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/ToolbarItemAccess.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/ToolbarItemAccess.tsx index 2dbea23b..795bde64 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/ToolbarItemAccess.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/ToolbarItemAccess.tsx @@ -1,4 +1,4 @@ -import { useIsProcessingLibrary } from '@/backend/library/useIsProcessingLibrary'; +import { useMutatingLibrary } from '@/backend/library/useMutatingLibrary'; import { useSetAccessPolicy } from '@/backend/library/useSetAccessPolicy'; import { VisibilityIcon } from '@/components/DomainIcons'; import { IconImmutable, IconMutable } from '@/components/Icons'; @@ -23,7 +23,7 @@ interface ToolbarItemAccessProps { function ToolbarItemAccess({ visible, toggleVisible, readOnly, toggleReadOnly, controller }: ToolbarItemAccessProps) { const role = useRoleStore(state => state.role); - const isProcessing = useIsProcessingLibrary(); + const isProcessing = useMutatingLibrary(); const policy = controller.schema.access_policy; const { setAccessPolicy } = useSetAccessPolicy(); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/ToolbarRSFormCard.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/ToolbarRSFormCard.tsx index 8a3030e2..7cfbd6bd 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/ToolbarRSFormCard.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/ToolbarRSFormCard.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useIsProcessingLibrary } from '@/backend/library/useIsProcessingLibrary'; +import { useMutatingLibrary } from '@/backend/library/useMutatingLibrary'; import { IconDestroy, IconSave, IconShare } from '@/components/Icons'; import BadgeHelp from '@/components/info/BadgeHelp'; import MiniSelectorOSS from '@/components/select/MiniSelectorOSS'; @@ -26,7 +26,7 @@ interface ToolbarRSFormCardProps { function ToolbarRSFormCard({ controller, onSubmit }: ToolbarRSFormCardProps) { const role = useRoleStore(state => state.role); const { isModified } = useModificationStore(); - const isProcessing = useIsProcessingLibrary(); + const isProcessing = useMutatingLibrary(); const canSave = isModified && !isProcessing; const ossSelector = (() => { diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx index 090599e3..caa51143 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx @@ -4,7 +4,7 @@ import fileDownload from 'js-file-download'; import { useEffect, useState } from 'react'; import { toast } from 'react-toastify'; -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import { IconCSV } from '@/components/Icons'; import { type RowSelectionState } from '@/components/ui/DataTable'; import MiniButton from '@/components/ui/MiniButton'; @@ -24,7 +24,7 @@ import ToolbarRSList from './ToolbarRSList'; function EditorRSList() { const [rowSelection, setRowSelection] = useState({}); const controller = useRSEdit(); - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); const [filtered, setFiltered] = useState(controller.schema.items); const [filterText, setFilterText] = useState(''); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/ToolbarRSList.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/ToolbarRSList.tsx index b4ddddd9..a1ee6dc2 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/ToolbarRSList.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/ToolbarRSList.tsx @@ -1,4 +1,4 @@ -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import { CstTypeIcon } from '@/components/DomainIcons'; import { IconClone, @@ -25,7 +25,7 @@ import { useRSEdit } from '../RSEditContext'; function ToolbarRSList() { const controller = useRSEdit(); - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); const insertMenu = useDropdown(); return ( diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx index 198a2386..e2024ca0 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx @@ -19,7 +19,7 @@ import { import { useStoreApi } from 'reactflow'; import { useDebounce } from 'use-debounce'; -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import InfoConstituenta from '@/components/info/InfoConstituenta'; import SelectedCounter from '@/components/info/SelectedCounter'; import { CProps } from '@/components/props'; @@ -54,7 +54,7 @@ function TGFlow() { const flow = useReactFlow(); const store = useStoreApi(); const { addSelectedNodes } = store.getState(); - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); const showParams = useDialogsStore(state => state.showGraphParams); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ToolbarTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ToolbarTermGraph.tsx index dfcf7adc..f432c06e 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ToolbarTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ToolbarTermGraph.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import { IconClustering, IconClusteringOff, @@ -48,7 +48,7 @@ function ToolbarTermGraph({ onSaveImage }: ToolbarTermGraphProps) { const controller = useRSEdit(); - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); const showTypeGraph = useDialogsStore(state => state.showShowTypeGraph); function handleShowTypeGraph() { diff --git a/rsconcept/frontend/src/pages/RSFormPage/MenuRSTabs.tsx b/rsconcept/frontend/src/pages/RSFormPage/MenuRSTabs.tsx index 14e8cdcd..f223448c 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/MenuRSTabs.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/MenuRSTabs.tsx @@ -8,7 +8,7 @@ import { useAuthSuspense } from '@/backend/auth/useAuth'; import { useCstSubstitute } from '@/backend/rsform/useCstSubstitute'; import { useDownloadRSForm } from '@/backend/rsform/useDownloadRSForm'; import { useInlineSynthesis } from '@/backend/rsform/useInlineSynthesis'; -import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm'; +import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import { useProduceStructure } from '@/backend/rsform/useProduceStructure'; import { useResetAliases } from '@/backend/rsform/useResetAliases'; import { useRestoreOrder } from '@/backend/rsform/useRestoreOrder'; @@ -63,7 +63,7 @@ function MenuRSTabs() { const role = useRoleStore(state => state.role); const setRole = useRoleStore(state => state.setRole); const { isModified } = useModificationStore(); - const isProcessing = useIsProcessingRSForm(); + const isProcessing = useMutatingRSForm(); const { resetAliases } = useResetAliases(); const { restoreOrder } = useRestoreOrder(); diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSFormPage.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSFormPage.tsx index a78a98be..c7d51103 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSFormPage.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSFormPage.tsx @@ -6,7 +6,7 @@ import { useParams } from 'react-router'; import { useBlockNavigation, useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { urls } from '@/app/urls'; -import InfoError, { ErrorData } from '@/components/info/InfoError'; +import { ErrorData } from '@/components/info/InfoError'; import Divider from '@/components/ui/Divider'; import TextURL from '@/components/ui/TextURL'; import useQueryStrings from '@/hooks/useQueryStrings'; @@ -33,6 +33,7 @@ function RSFormPage() { } return ( ( )} @@ -47,6 +48,13 @@ function RSFormPage() { export default RSFormPage; // ====== Internals ========= +const filterErrors = (error: Error) => { + if (axios.isAxiosError(error) && error.response && (error.response.status === 404 || error.response.status === 403)) { + return; + } + throw error; +}; + function ProcessError({ error, isArchive, @@ -55,7 +63,7 @@ function ProcessError({ error: ErrorData; isArchive: boolean; itemID?: LibraryItemID; -}): React.ReactElement { +}): React.ReactElement | null { if (axios.isAxiosError(error) && error.response) { if (error.response.status === 404) { return ( @@ -77,5 +85,5 @@ function ProcessError({ ); } } - return ; + return null; }