R: Prepare to migrate to react-compiler

This commit is contained in:
Ivan 2024-12-12 21:36:46 +03:00
parent a2e080d6e1
commit 56b7d3d71f
52 changed files with 477 additions and 316 deletions

View File

@ -56,6 +56,7 @@ This readme file is used mostly to document project dependencies and conventions
- tailwindcss - tailwindcss
- postcss - postcss
- autoprefixer - autoprefixer
- eslint-plugin-react-compiler
- eslint-plugin-simple-import-sort - eslint-plugin-simple-import-sort
- eslint-plugin-react-hooks - eslint-plugin-react-hooks
- eslint-plugin-tsdoc - eslint-plugin-tsdoc

View File

@ -2,6 +2,7 @@ import globals from 'globals';
import typescriptPlugin from 'typescript-eslint'; import typescriptPlugin from 'typescript-eslint';
import typescriptParser from '@typescript-eslint/parser'; import typescriptParser from '@typescript-eslint/parser';
import reactPlugin from 'eslint-plugin-react'; import reactPlugin from 'eslint-plugin-react';
import reactCompilerPlugin from 'eslint-plugin-react-compiler';
import reactHooksPlugin from 'eslint-plugin-react-hooks'; import reactHooksPlugin from 'eslint-plugin-react-hooks';
import simpleImportSort from 'eslint-plugin-simple-import-sort'; import simpleImportSort from 'eslint-plugin-simple-import-sort';
@ -34,11 +35,13 @@ export default [
{ {
plugins: { plugins: {
'react': reactPlugin, 'react': reactPlugin,
'react-compiler': reactCompilerPlugin,
'react-hooks': reactHooksPlugin, 'react-hooks': reactHooksPlugin,
'simple-import-sort': simpleImportSort 'simple-import-sort': simpleImportSort
}, },
settings: { react: { version: 'detect' } }, settings: { react: { version: 'detect' } },
rules: { rules: {
'react-compiler/react-compiler': 'error',
'@typescript-eslint/no-empty-object-type': ['error', { allowInterfaces: 'with-single-extends' }], '@typescript-eslint/no-empty-object-type': ['error', { allowInterfaces: 'with-single-extends' }],
'@typescript-eslint/prefer-nullish-coalescing': 'off', '@typescript-eslint/prefer-nullish-coalescing': 'off',
'@typescript-eslint/no-inferrable-types': 'off', '@typescript-eslint/no-inferrable-types': 'off',

View File

@ -43,6 +43,7 @@
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"eslint": "^9.16.0", "eslint": "^9.16.0",
"eslint-plugin-react": "^7.37.2", "eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-compiler": "^19.0.0-beta-37ed2a7-20241206",
"eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-simple-import-sort": "^12.1.1",
"globals": "^15.13.0", "globals": "^15.13.0",
@ -163,6 +164,19 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-annotate-as-pure": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz",
"integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-compilation-targets": { "node_modules/@babel/helper-compilation-targets": {
"version": "7.25.9", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz",
@ -190,6 +204,52 @@
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
}, },
"node_modules/@babel/helper-create-class-features-plugin": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz",
"integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9",
"@babel/helper-member-expression-to-functions": "^7.25.9",
"@babel/helper-optimise-call-expression": "^7.25.9",
"@babel/helper-replace-supers": "^7.25.9",
"@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
"@babel/traverse": "^7.25.9",
"semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/helper-member-expression-to-functions": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz",
"integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.25.9",
"@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-imports": { "node_modules/@babel/helper-module-imports": {
"version": "7.25.9", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
@ -221,6 +281,19 @@
"@babel/core": "^7.0.0" "@babel/core": "^7.0.0"
} }
}, },
"node_modules/@babel/helper-optimise-call-expression": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz",
"integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-plugin-utils": { "node_modules/@babel/helper-plugin-utils": {
"version": "7.25.9", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz",
@ -231,6 +304,38 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-replace-supers": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz",
"integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-member-expression-to-functions": "^7.25.9",
"@babel/helper-optimise-call-expression": "^7.25.9",
"@babel/traverse": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-skip-transparent-expression-wrappers": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz",
"integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.25.9",
"@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": { "node_modules/@babel/helper-string-parser": {
"version": "7.25.9", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
@ -288,6 +393,24 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@babel/plugin-proposal-private-methods": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
"integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
"deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-syntax-async-generators": { "node_modules/@babel/plugin-syntax-async-generators": {
"version": "7.8.4", "version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
@ -5153,6 +5276,27 @@
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
} }
}, },
"node_modules/eslint-plugin-react-compiler": {
"version": "19.0.0-beta-37ed2a7-20241206",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.0.0-beta-37ed2a7-20241206.tgz",
"integrity": "sha512-5Pex1fUCJwLwwqEJe6NkgTn45kUjjj9TZP6IrW4IcpWM/YaEe+QvcOeF60huDjBq0kz1svGeW2nw8WdY+qszAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.24.4",
"@babel/parser": "^7.24.4",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"hermes-parser": "^0.25.1",
"zod": "^3.22.4",
"zod-validation-error": "^3.0.3"
},
"engines": {
"node": "^14.17.0 || ^16.0.0 || >= 18.0.0"
},
"peerDependencies": {
"eslint": ">=7"
}
},
"node_modules/eslint-plugin-react-hooks": { "node_modules/eslint-plugin-react-hooks": {
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz",
@ -6022,6 +6166,23 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/hermes-estree": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
"integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
"dev": true,
"license": "MIT"
},
"node_modules/hermes-parser": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
"integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"hermes-estree": "0.25.1"
}
},
"node_modules/hoist-non-react-statics": { "node_modules/hoist-non-react-statics": {
"version": "3.3.2", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@ -10333,6 +10494,29 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/zod": {
"version": "3.24.1",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/zod-validation-error": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.4.0.tgz",
"integrity": "sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"zod": "^3.18.0"
}
},
"node_modules/zustand": { "node_modules/zustand": {
"version": "4.5.5", "version": "4.5.5",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.5.tgz", "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.5.tgz",

View File

@ -47,6 +47,7 @@
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"eslint": "^9.16.0", "eslint": "^9.16.0",
"eslint-plugin-react": "^7.37.2", "eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-compiler": "^19.0.0-beta-37ed2a7-20241206",
"eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-simple-import-sort": "^12.1.1",
"globals": "^15.13.0", "globals": "^15.13.0",

View File

@ -62,7 +62,6 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
} }
function handleToggleDarkMode() { function handleToggleDarkMode() {
hideDropdown();
toggleDarkMode(); toggleDarkMode();
} }

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useLayoutEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import DataTable, { createColumnHelper, RowSelectionState } from '@/components/ui/DataTable'; import DataTable, { createColumnHelper, RowSelectionState } from '@/components/ui/DataTable';
import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useConceptOptions } from '@/context/ConceptOptionsContext';
@ -68,7 +68,7 @@ function PickMultiConstituenta({
return newGraph; return newGraph;
}, [data, schema.graph, schema.items]); }, [data, schema.graph, schema.items]);
useLayoutEffect(() => { useEffect(() => {
if (filtered.length === 0) { if (filtered.length === 0) {
setRowSelection({}); setRowSelection({});
return; return;
@ -80,7 +80,7 @@ function PickMultiConstituenta({
setRowSelection(newRowSelection); setRowSelection(newRowSelection);
}, [filtered, setRowSelection, selected]); }, [filtered, setRowSelection, selected]);
useLayoutEffect(() => { useEffect(() => {
if (data.length === 0) { if (data.length === 0) {
setFiltered([]); setFiltered([]);
} else if (filterText) { } else if (filterText) {

View File

@ -1,5 +1,5 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable'; import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable';
@ -58,7 +58,7 @@ function PickSchema({
const locationMenu = useDropdown(); const locationMenu = useDropdown();
useLayoutEffect(() => { useEffect(() => {
let newFiltered = baseFiltered.filter(item => matchLibraryItem(item, filterText)); let newFiltered = baseFiltered.filter(item => matchLibraryItem(item, filterText));
if (filterLocation.length > 0) { if (filterLocation.length > 0) {
newFiltered = newFiltered.filter( newFiltered = newFiltered.filter(

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { FolderNode, FolderTree } from '@/models/FolderTree'; import { FolderNode, FolderTree } from '@/models/FolderTree';
import { labelFolderNode } from '@/utils/labels'; import { labelFolderNode } from '@/utils/labels';
@ -24,7 +24,7 @@ function SelectLocation({ value, folderTree, dense, prefix, onClick, className,
const items = useMemo(() => folderTree.getTree(), [folderTree]); const items = useMemo(() => folderTree.getTree(), [folderTree]);
const [folded, setFolded] = useState<FolderNode[]>(items); const [folded, setFolded] = useState<FolderNode[]>(items);
useLayoutEffect(() => { useEffect(() => {
setFolded(items.filter(item => item !== activeNode && !activeNode?.hasPredecessor(item))); setFolded(items.filter(item => item !== activeNode && !activeNode?.hasPredecessor(item)));
}, [items, activeNode]); }, [items, activeNode]);

View File

@ -1,5 +1,5 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { globals, PARAMETER } from '@/utils/constants'; import { globals, PARAMETER } from '@/utils/constants';
@ -50,7 +50,7 @@ function SelectTree<ItemType>({
); );
const [folded, setFolded] = useState<ItemType[]>(items); const [folded, setFolded] = useState<ItemType[]>(items);
useLayoutEffect(() => { useEffect(() => {
setFolded(items.filter(item => getParent(value) !== item && getParent(getParent(value)) !== item)); setFolded(items.filter(item => getParent(value) !== item && getParent(getParent(value)) !== item));
}, [value, getParent, items]); }, [value, getParent, items]);

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { createContext, useCallback, useContext, useLayoutEffect, useState } from 'react'; import { createContext, useCallback, useContext, useEffect, useState } from 'react';
import { DataCallback } from '@/backend/apiTransport'; import { DataCallback } from '@/backend/apiTransport';
import { import {
@ -124,10 +124,7 @@ export const AuthState = ({ children }: React.PropsWithChildren) => {
showError: true, showError: true,
setLoading: setLoading, setLoading: setLoading,
onError: setError, onError: setError,
onSuccess: () => onSuccess: () => reload(callback)
reload(() => {
callback?.();
})
}); });
}, },
[reload] [reload]
@ -184,7 +181,7 @@ export const AuthState = ({ children }: React.PropsWithChildren) => {
[reload] [reload]
); );
useLayoutEffect(() => { useEffect(() => {
reload(); reload();
}, [reload]); }, [reload]);

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react'; import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import Tooltip from '@/components/ui/Tooltip'; import Tooltip from '@/components/ui/Tooltip';
import useLocalStorage from '@/hooks/useLocalStorage'; import useLocalStorage from '@/hooks/useLocalStorage';
@ -76,11 +76,11 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => {
root.setAttribute('data-color-scheme', !isDark ? 'light' : 'dark'); root.setAttribute('data-color-scheme', !isDark ? 'light' : 'dark');
} }
useLayoutEffect(() => { useEffect(() => {
setDarkClass(darkMode); setDarkClass(darkMode);
}, [darkMode]); }, [darkMode]);
useLayoutEffect(() => { useEffect(() => {
setColors(darkMode ? darkT : lightT); setColors(darkMode ? darkT : lightT);
}, [darkMode, setColors]); }, [darkMode, setColors]);

View File

@ -8,8 +8,6 @@ import { LibraryItemID } from '@/models/library';
import { IOperationSchema, IOperationSchemaData } from '@/models/oss'; import { IOperationSchema, IOperationSchemaData } from '@/models/oss';
import { contextOutsideScope } from '@/utils/labels'; import { contextOutsideScope } from '@/utils/labels';
import { useLibrary } from './LibraryContext';
interface IGlobalOssContext { interface IGlobalOssContext {
schema: IOperationSchema | undefined; schema: IOperationSchema | undefined;
setID: (id: string | undefined) => void; setID: (id: string | undefined) => void;
@ -20,6 +18,7 @@ interface IGlobalOssContext {
invalidate: () => void; invalidate: () => void;
invalidateItem: (target: LibraryItemID) => void; invalidateItem: (target: LibraryItemID) => void;
partialUpdate: (data: Partial<IOperationSchema>) => void;
reload: (callback?: () => void) => void; reload: (callback?: () => void) => void;
} }
@ -33,16 +32,16 @@ export const useGlobalOss = (): IGlobalOssContext => {
}; };
export const GlobalOssState = ({ children }: React.PropsWithChildren) => { export const GlobalOssState = ({ children }: React.PropsWithChildren) => {
const library = useLibrary(); const [isValid, setIsValid] = useState(true);
const [isValid, setIsValid] = useState(false);
const [ossID, setIdInternal] = useState<string | undefined>(undefined); const [ossID, setIdInternal] = useState<string | undefined>(undefined);
const { const {
schema: schema, // prettier: split lines schema: schema, // prettier: split lines
error: loadingError, error: loadingError,
setSchema: setDataInternal, setSchema: setDataInternal,
loading: loading, loading: loading,
reload: reloadInternal reload: reloadInternal,
} = useOssDetails({ target: ossID, items: library.items }); partialUpdate
} = useOssDetails({ target: ossID });
const reload = useCallback( const reload = useCallback(
(callback?: () => void) => { (callback?: () => void) => {
@ -96,6 +95,7 @@ export const GlobalOssState = ({ children }: React.PropsWithChildren) => {
loading, loading,
loadingError, loadingError,
reload, reload,
partialUpdate,
isValid, isValid,
invalidateItem, invalidateItem,
invalidate invalidate

View File

@ -48,7 +48,7 @@ interface ILibraryContext {
destroyItem: (target: LibraryItemID, callback?: () => void) => void; destroyItem: (target: LibraryItemID, callback?: () => void) => void;
renameLocation: (data: IRenameLocationData, callback?: () => void) => void; renameLocation: (data: IRenameLocationData, callback?: () => void) => void;
localUpdateItem: (data: ILibraryItem) => void; localUpdateItem: (data: Partial<ILibraryItem>) => void;
localUpdateTimestamp: (target: LibraryItemID) => void; localUpdateTimestamp: (target: LibraryItemID) => void;
} }
@ -196,21 +196,17 @@ export const LibraryState = ({ children }: React.PropsWithChildren) => {
}, [reloadTemplates]); }, [reloadTemplates]);
const localUpdateItem = useCallback( const localUpdateItem = useCallback(
(data: ILibraryItem) => { (data: Partial<ILibraryItem>) => {
const libraryItem = items.find(item => item.id === data.id); setItems(prev => prev.map(item => (item.id === data.id ? { ...item, ...data } : item)));
if (libraryItem) Object.assign(libraryItem, data);
}, },
[items] [setItems]
); );
const localUpdateTimestamp = useCallback( const localUpdateTimestamp = useCallback(
(target: LibraryItemID) => { (target: LibraryItemID) => {
const libraryItem = items.find(item => item.id === target); setItems(prev => prev.map(item => (item.id === target ? { ...item, time_update: Date() } : item)));
if (libraryItem) {
libraryItem.time_update = Date();
}
}, },
[items] [setItems]
); );
const createItem = useCallback( const createItem = useCallback(

View File

@ -85,23 +85,22 @@ interface OssStateProps {
export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateProps>) => { export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateProps>) => {
const library = useLibrary(); const library = useLibrary();
const oss = useGlobalOss(); const ossData = useGlobalOss();
const model = oss.schema;
const { user } = useAuth(); const { user } = useAuth();
const [processing, setProcessing] = useState(false); const [processing, setProcessing] = useState(false);
const [processingError, setProcessingError] = useState<ErrorData>(undefined); const [processingError, setProcessingError] = useState<ErrorData>(undefined);
const isOwned = useMemo(() => { const isOwned = useMemo(() => {
return user?.id === model?.owner || false; return user?.id === ossData.schema?.owner || false;
}, [user, model?.owner]); }, [user, ossData.schema?.owner]);
useEffect(() => { useEffect(() => {
oss.setID(itemID); ossData.setID(itemID);
}, [itemID, oss]); }, [itemID, ossData]);
const update = useCallback( const update = useCallback(
(data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => { (data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => {
if (!model) { if (!ossData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -111,43 +110,39 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
const fullData: IOperationSchemaData = Object.assign(model, newData); const fullData: IOperationSchemaData = Object.assign(ossData.schema!, newData);
oss.setData(fullData); ossData.setData(fullData);
library.localUpdateItem(newData); library.localUpdateItem(newData);
callback?.(newData); callback?.(newData);
} }
}); });
}, },
[itemID, model, library, oss] [itemID, library, ossData]
); );
const setOwner = useCallback( const setOwner = useCallback(
(newOwner: UserID, callback?: () => void) => { (newOwner: UserID, callback?: () => void) => {
if (!model) { if (!ossData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
patchSetOwner(itemID, { patchSetOwner(itemID, {
data: { data: { user: newOwner },
user: newOwner
},
showError: true, showError: true,
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: () => { onSuccess: () => {
model.owner = newOwner; ossData.partialUpdate({ owner: newOwner });
library.reloadItems(() => { library.reloadItems(callback);
callback?.();
});
} }
}); });
}, },
[itemID, model, library] [itemID, ossData, library]
); );
const setAccessPolicy = useCallback( const setAccessPolicy = useCallback(
(newPolicy: AccessPolicy, callback?: () => void) => { (newPolicy: AccessPolicy, callback?: () => void) => {
if (!model) { if (!ossData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -159,19 +154,17 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: () => { onSuccess: () => {
model.access_policy = newPolicy; ossData.partialUpdate({ access_policy: newPolicy });
library.reloadItems(() => { library.reloadItems(callback);
callback?.();
});
} }
}); });
}, },
[itemID, model, library] [itemID, ossData, library]
); );
const setLocation = useCallback( const setLocation = useCallback(
(newLocation: string, callback?: () => void) => { (newLocation: string, callback?: () => void) => {
if (!model) { if (!ossData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -183,19 +176,17 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: () => { onSuccess: () => {
model.location = newLocation; ossData.partialUpdate({ location: newLocation });
library.reloadItems(() => { library.reloadItems(callback);
callback?.();
});
} }
}); });
}, },
[itemID, model, library] [itemID, ossData, library]
); );
const setEditors = useCallback( const setEditors = useCallback(
(newEditors: UserID[], callback?: () => void) => { (newEditors: UserID[], callback?: () => void) => {
if (!model) { if (!ossData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -207,14 +198,12 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: () => { onSuccess: () => {
model.editors = newEditors; ossData.partialUpdate({ editors: newEditors });
library.reloadItems(() => { library.reloadItems(callback);
callback?.();
});
} }
}); });
}, },
[itemID, model, library] [itemID, ossData, library]
); );
const savePositions = useCallback( const savePositions = useCallback(
@ -243,13 +232,13 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
oss.setData(newData.oss); ossData.setData(newData.oss);
library.localUpdateTimestamp(newData.oss.id); library.localUpdateTimestamp(newData.oss.id);
callback?.(newData.new_operation); callback?.(newData.new_operation);
} }
}); });
}, },
[itemID, library, oss] [itemID, library, ossData]
); );
const deleteOperation = useCallback( const deleteOperation = useCallback(
@ -261,14 +250,12 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
oss.setData(newData); ossData.setData(newData);
library.reloadItems(() => { library.reloadItems(callback);
callback?.();
});
} }
}); });
}, },
[itemID, library, oss] [itemID, library, ossData]
); );
const createInput = useCallback( const createInput = useCallback(
@ -280,19 +267,19 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
oss.setData(newData.oss); ossData.setData(newData.oss);
library.reloadItems(() => { library.reloadItems(() => {
callback?.(newData.new_schema); callback?.(newData.new_schema);
}); });
} }
}); });
}, },
[itemID, library, oss] [itemID, library, ossData]
); );
const setInput = useCallback( const setInput = useCallback(
(data: IOperationSetInputData, callback?: () => void) => { (data: IOperationSetInputData, callback?: () => void) => {
if (!model) { if (!ossData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -302,19 +289,17 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
oss.setData(newData); ossData.setData(newData);
library.reloadItems(() => { library.reloadItems(callback);
callback?.();
});
} }
}); });
}, },
[itemID, model, library, oss] [itemID, ossData, library]
); );
const updateOperation = useCallback( const updateOperation = useCallback(
(data: IOperationUpdateData, callback?: () => void) => { (data: IOperationUpdateData, callback?: () => void) => {
if (!model) { if (!ossData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -324,19 +309,17 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
oss.setData(newData); ossData.setData(newData);
library.reloadItems(() => { library.reloadItems(callback);
callback?.();
});
} }
}); });
}, },
[itemID, model, library, oss] [itemID, library, ossData]
); );
const executeOperation = useCallback( const executeOperation = useCallback(
(data: ITargetOperation, callback?: () => void) => { (data: ITargetOperation, callback?: () => void) => {
if (!model) { if (!ossData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -346,19 +329,17 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
oss.setData(newData); ossData.setData(newData);
library.reloadItems(() => { library.reloadItems(callback);
callback?.();
});
} }
}); });
}, },
[itemID, model, library, oss] [itemID, library, ossData]
); );
const relocateConstituents = useCallback( const relocateConstituents = useCallback(
(data: ICstRelocateData, callback?: () => void) => { (data: ICstRelocateData, callback?: () => void) => {
if (!model) { if (!ossData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -368,23 +349,21 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: () => { onSuccess: () => {
oss.reload(); ossData.reload();
library.reloadItems(() => { library.reloadItems(callback);
callback?.();
});
} }
}); });
}, },
[model, library, oss] [library, ossData]
); );
return ( return (
<OssContext.Provider <OssContext.Provider
value={{ value={{
schema: model, schema: ossData.schema,
itemID, itemID,
loading: oss.loading, loading: ossData.loading,
loadingError: oss.loadingError, loadingError: ossData.loadingError,
processing, processing,
processingError, processingError,
isOwned, isOwned,

View File

@ -113,28 +113,19 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
const library = useLibrary(); const library = useLibrary();
const oss = useGlobalOss(); const oss = useGlobalOss();
const { user } = useAuth(); const { user } = useAuth();
const { const rsData = useRSFormDetails({ target: itemID, version: versionID });
schema, // prettier: split lines
reload,
error: errorLoading,
setSchema,
loading
} = useRSFormDetails({
target: itemID,
version: versionID
});
const [processing, setProcessing] = useState(false); const [processing, setProcessing] = useState(false);
const [processingError, setProcessingError] = useState<ErrorData>(undefined); const [processingError, setProcessingError] = useState<ErrorData>(undefined);
const isOwned = useMemo(() => { const isOwned = useMemo(() => {
return user?.id === schema?.owner || false; return user?.id === rsData.schema?.owner || false;
}, [user, schema?.owner]); }, [user, rsData.schema?.owner]);
const isArchive = useMemo(() => !!versionID, [versionID]); const isArchive = useMemo(() => !!versionID, [versionID]);
const update = useCallback( const update = useCallback(
(data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => { (data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => {
if (!schema) { if (!rsData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -144,19 +135,19 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(Object.assign(schema, newData)); rsData.setSchema(Object.assign(rsData.schema!, newData));
library.localUpdateItem(newData); library.localUpdateItem(newData);
oss.invalidateItem(newData.id); oss.invalidateItem(newData.id);
callback?.(newData); callback?.(newData);
} }
}); });
}, },
[itemID, setSchema, schema, library, oss] [itemID, rsData, library, oss]
); );
const upload = useCallback( const upload = useCallback(
(data: IRSFormUploadData, callback?: () => void) => { (data: IRSFormUploadData, callback?: () => void) => {
if (!schema) { if (!rsData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -166,20 +157,17 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData); rsData.setSchema(newData);
library.localUpdateItem(newData); library.localUpdateItem(newData);
callback?.(); callback?.();
} }
}); });
}, },
[itemID, setSchema, schema, library] [itemID, rsData, library]
); );
const setOwner = useCallback( const setOwner = useCallback(
(newOwner: UserID, callback?: () => void) => { (newOwner: UserID, callback?: () => void) => {
if (!schema) {
return;
}
setProcessingError(undefined); setProcessingError(undefined);
patchSetOwner(itemID, { patchSetOwner(itemID, {
data: { data: {
@ -189,18 +177,18 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: () => { onSuccess: () => {
schema.owner = newOwner; rsData.partialUpdate({ owner: newOwner });
library.localUpdateItem(schema); library.localUpdateItem({ id: Number(itemID), owner: newOwner });
callback?.(); callback?.();
} }
}); });
}, },
[itemID, schema, library] [itemID, rsData, library]
); );
const setAccessPolicy = useCallback( const setAccessPolicy = useCallback(
(newPolicy: AccessPolicy, callback?: () => void) => { (newPolicy: AccessPolicy, callback?: () => void) => {
if (!schema) { if (!rsData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -212,18 +200,18 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: () => { onSuccess: () => {
schema.access_policy = newPolicy; rsData.partialUpdate({ access_policy: newPolicy });
library.localUpdateItem(schema); library.localUpdateItem({ id: Number(itemID), access_policy: newPolicy });
callback?.(); callback?.();
} }
}); });
}, },
[itemID, schema, library] [itemID, rsData, library]
); );
const setLocation = useCallback( const setLocation = useCallback(
(newLocation: string, callback?: () => void) => { (newLocation: string, callback?: () => void) => {
if (!schema) { if (!rsData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -235,17 +223,18 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: () => { onSuccess: () => {
schema.location = newLocation; rsData.partialUpdate({ location: newLocation });
library.reloadItems(callback); library.localUpdateItem({ id: Number(itemID), location: newLocation });
callback?.();
} }
}); });
}, },
[itemID, schema, library] [itemID, rsData, library]
); );
const setEditors = useCallback( const setEditors = useCallback(
(newEditors: UserID[], callback?: () => void) => { (newEditors: UserID[], callback?: () => void) => {
if (!schema) { if (!rsData.schema) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -257,17 +246,17 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: () => { onSuccess: () => {
schema.editors = newEditors; rsData.partialUpdate({ editors: newEditors });
callback?.(); callback?.();
} }
}); });
}, },
[itemID, schema] [itemID, rsData]
); );
const resetAliases = useCallback( const resetAliases = useCallback(
(callback?: () => void) => { (callback?: () => void) => {
if (!schema || !user) { if (!rsData.schema || !user) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -276,19 +265,19 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData); rsData.setSchema(newData);
library.localUpdateTimestamp(newData.id); library.localUpdateTimestamp(newData.id);
oss.invalidateItem(newData.id); oss.invalidateItem(newData.id);
callback?.(); callback?.();
} }
}); });
}, },
[itemID, schema, user, setSchema, library, oss] [itemID, rsData, user, library, oss]
); );
const restoreOrder = useCallback( const restoreOrder = useCallback(
(callback?: () => void) => { (callback?: () => void) => {
if (!schema || !user) { if (!rsData.schema || !user) {
return; return;
} }
setProcessingError(undefined); setProcessingError(undefined);
@ -297,13 +286,13 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData); rsData.setSchema(newData);
library.localUpdateTimestamp(newData.id); library.localUpdateTimestamp(newData.id);
callback?.(); callback?.();
} }
}); });
}, },
[itemID, schema, user, setSchema, library] [itemID, rsData, user, library]
); );
const produceStructure = useCallback( const produceStructure = useCallback(
@ -315,27 +304,27 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData.schema); rsData.setSchema(newData.schema);
library.localUpdateTimestamp(newData.schema.id); library.localUpdateTimestamp(newData.schema.id);
oss.invalidateItem(newData.schema.id); oss.invalidateItem(newData.schema.id);
callback?.(newData.cst_list); callback?.(newData.cst_list);
} }
}); });
}, },
[setSchema, itemID, library, oss] [rsData, itemID, library, oss]
); );
const download = useCallback( const download = useCallback(
(callback: DataCallback<Blob>) => { (callback: DataCallback<Blob>) => {
setProcessingError(undefined); setProcessingError(undefined);
getTRSFile(itemID, String(schema?.version ?? ''), { getTRSFile(itemID, String(rsData.schema?.version ?? ''), {
showError: true, showError: true,
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: callback onSuccess: callback
}); });
}, },
[itemID, schema] [itemID, rsData]
); );
const cstCreate = useCallback( const cstCreate = useCallback(
@ -347,14 +336,14 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData.schema); rsData.setSchema(newData.schema);
library.localUpdateTimestamp(newData.schema.id); library.localUpdateTimestamp(newData.schema.id);
oss.invalidateItem(newData.schema.id); oss.invalidateItem(newData.schema.id);
callback?.(newData.new_cst); callback?.(newData.new_cst);
} }
}); });
}, },
[itemID, setSchema, library, oss] [itemID, rsData, library, oss]
); );
const cstDelete = useCallback( const cstDelete = useCallback(
@ -366,14 +355,14 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData); rsData.setSchema(newData);
library.localUpdateTimestamp(newData.id); library.localUpdateTimestamp(newData.id);
oss.invalidateItem(newData.id); oss.invalidateItem(newData.id);
callback?.(); callback?.();
} }
}); });
}, },
[itemID, setSchema, library, oss] [itemID, rsData, library, oss]
); );
const cstUpdate = useCallback( const cstUpdate = useCallback(
@ -385,14 +374,14 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => onSuccess: newData =>
reload(setProcessing, () => { rsData.reload(setProcessing, () => {
library.localUpdateTimestamp(Number(itemID)); library.localUpdateTimestamp(Number(itemID));
oss.invalidateItem(Number(itemID)); oss.invalidateItem(Number(itemID));
callback?.(newData); callback?.(newData);
}) })
}); });
}, },
[itemID, reload, library, oss] [itemID, rsData, library, oss]
); );
const cstRename = useCallback( const cstRename = useCallback(
@ -404,14 +393,14 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData.schema); rsData.setSchema(newData.schema);
library.localUpdateTimestamp(newData.schema.id); library.localUpdateTimestamp(newData.schema.id);
oss.invalidateItem(newData.schema.id); oss.invalidateItem(newData.schema.id);
callback?.(newData.new_cst); callback?.(newData.new_cst);
} }
}); });
}, },
[setSchema, itemID, library, oss] [rsData, itemID, library, oss]
); );
const cstSubstitute = useCallback( const cstSubstitute = useCallback(
@ -423,14 +412,14 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData); rsData.setSchema(newData);
library.localUpdateTimestamp(newData.id); library.localUpdateTimestamp(newData.id);
oss.invalidateItem(newData.id); oss.invalidateItem(newData.id);
callback?.(); callback?.();
} }
}); });
}, },
[setSchema, itemID, library, oss] [rsData, itemID, library, oss]
); );
const cstMoveTo = useCallback( const cstMoveTo = useCallback(
@ -442,13 +431,13 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData); rsData.setSchema(newData);
library.localUpdateTimestamp(Number(itemID)); library.localUpdateTimestamp(Number(itemID));
callback?.(); callback?.();
} }
}); });
}, },
[itemID, setSchema, library] [itemID, rsData, library]
); );
const versionCreate = useCallback( const versionCreate = useCallback(
@ -460,13 +449,13 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData.schema); rsData.setSchema(newData.schema);
library.localUpdateTimestamp(Number(itemID)); library.localUpdateTimestamp(Number(itemID));
callback?.(newData.version); callback?.(newData.version);
} }
}); });
}, },
[itemID, setSchema, library] [itemID, rsData, library]
); );
const findPredecessor = useCallback((data: ITargetCst, callback: (reference: IConstituentaReference) => void) => { const findPredecessor = useCallback((data: ITargetCst, callback: (reference: IConstituentaReference) => void) => {
@ -489,7 +478,7 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: () => { onSuccess: () => {
schema!.versions = schema!.versions.map(prev => { const newVersions = rsData.schema!.versions.map(prev => {
if (prev.id === target) { if (prev.id === target) {
prev.description = data.description; prev.description = data.description;
prev.version = data.version; prev.version = data.version;
@ -498,12 +487,12 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
return prev; return prev;
} }
}); });
setSchema(schema); rsData.partialUpdate({ versions: newVersions });
callback?.(); callback?.();
} }
}); });
}, },
[schema, setSchema] [rsData]
); );
const versionDelete = useCallback( const versionDelete = useCallback(
@ -514,13 +503,13 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: () => { onSuccess: () => {
schema!.versions = schema!.versions.filter(prev => prev.id !== target); const newVersions = rsData.schema!.versions.filter(prev => prev.id !== target);
setSchema(schema); rsData.partialUpdate({ versions: newVersions });
callback?.(); callback?.();
} }
}); });
}, },
[schema, setSchema] [rsData]
); );
const versionRestore = useCallback( const versionRestore = useCallback(
@ -531,13 +520,13 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData); rsData.setSchema(newData);
library.localUpdateItem(newData); library.localUpdateItem(newData);
callback?.(); callback?.();
} }
}); });
}, },
[setSchema, library] [rsData, library]
); );
const inlineSynthesis = useCallback( const inlineSynthesis = useCallback(
@ -549,24 +538,24 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
setLoading: setProcessing, setLoading: setProcessing,
onError: setProcessingError, onError: setProcessingError,
onSuccess: newData => { onSuccess: newData => {
setSchema(newData); rsData.setSchema(newData);
library.localUpdateTimestamp(newData.id); library.localUpdateTimestamp(newData.id);
oss.invalidateItem(newData.id); oss.invalidateItem(newData.id);
callback?.(newData); callback?.(newData);
} }
}); });
}, },
[setSchema, library, oss] [rsData, library, oss]
); );
return ( return (
<RSFormContext.Provider <RSFormContext.Provider
value={{ value={{
schema, schema: rsData.schema,
itemID, itemID,
versionID, versionID,
loading, loading: rsData.loading,
errorLoading, errorLoading: rsData.error,
processing, processing,
processingError, processingError,
isOwned, isOwned,

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useLayoutEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { TabList, TabPanel, Tabs } from 'react-tabs'; import { TabList, TabPanel, Tabs } from 'react-tabs';
import Modal, { ModalProps } from '@/components/ui/Modal'; import Modal, { ModalProps } from '@/components/ui/Modal';
@ -65,7 +65,7 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
return true; return true;
} }
useLayoutEffect(() => { useEffect(() => {
if (!template.templateID) { if (!template.templateID) {
setTemplateSchema(undefined); setTemplateSchema(undefined);
} else { } else {
@ -73,7 +73,7 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
} }
}, [template.templateID, retrieveTemplate]); }, [template.templateID, retrieveTemplate]);
useLayoutEffect(() => { useEffect(() => {
if (!template.prototype) { if (!template.prototype) {
updateConstituenta({ updateConstituenta({
definition_raw: '', definition_raw: '',
@ -103,7 +103,7 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
} }
}, [template.prototype, updateConstituenta, updateSubstitutes, schema]); }, [template.prototype, updateConstituenta, updateSubstitutes, schema]);
useLayoutEffect(() => { useEffect(() => {
if (substitutes.arguments.length === 0 || !template.prototype) { if (substitutes.arguments.length === 0 || !template.prototype) {
return; return;
} }
@ -119,7 +119,7 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
}); });
}, [substitutes.arguments, template.prototype, updateConstituenta, updateSubstitutes, schema]); }, [substitutes.arguments, template.prototype, updateConstituenta, updateSubstitutes, schema]);
useLayoutEffect(() => { useEffect(() => {
setValidated(!!template.prototype && validateNewAlias(constituenta.alias, constituenta.cst_type, schema)); setValidated(!!template.prototype && validateNewAlias(constituenta.alias, constituenta.cst_type, schema));
}, [constituenta.alias, constituenta.cst_type, schema, template.prototype]); }, [constituenta.alias, constituenta.cst_type, schema, template.prototype]);

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import BadgeHelp from '@/components/info/BadgeHelp'; import BadgeHelp from '@/components/info/BadgeHelp';
import RSInput from '@/components/RSInput'; import RSInput from '@/components/RSInput';
@ -30,11 +30,11 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
const isElementary = useMemo(() => isBaseSet(state.cst_type), [state]); const isElementary = useMemo(() => isBaseSet(state.cst_type), [state]);
const showConvention = useMemo(() => !!state.convention || forceComment || isBasic, [state, forceComment, isBasic]); const showConvention = useMemo(() => !!state.convention || forceComment || isBasic, [state, forceComment, isBasic]);
useLayoutEffect(() => { useEffect(() => {
setForceComment(false); setForceComment(false);
}, [state.cst_type, partialUpdate, schema]); }, [state.cst_type, partialUpdate, schema]);
useLayoutEffect(() => { useEffect(() => {
if (setValidated) { if (setValidated) {
setValidated(validateNewAlias(state.alias, state.cst_type, schema)); setValidated(validateNewAlias(state.alias, state.cst_type, schema));
} }

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { TabList, TabPanel, Tabs } from 'react-tabs'; import { TabList, TabPanel, Tabs } from 'react-tabs';
import Modal from '@/components/ui/Modal'; import Modal from '@/components/ui/Modal';
@ -53,7 +53,7 @@ function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCre
return true; return true;
}, [alias, activeTab, inputs, attachedID, oss.items]); }, [alias, activeTab, inputs, attachedID, oss.items]);
useLayoutEffect(() => { useEffect(() => {
if (attachedID) { if (attachedID) {
const schema = library.items.find(value => value.id === attachedID); const schema = library.items.find(value => value.id === attachedID);
if (schema) { if (schema) {

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { TabList, TabPanel, Tabs } from 'react-tabs'; import { TabList, TabPanel, Tabs } from 'react-tabs';
import Modal from '@/components/ui/Modal'; import Modal from '@/components/ui/Modal';
@ -59,6 +59,7 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
const cache = useRSFormCache(); const cache = useRSFormCache();
const schemas = useMemo( const schemas = useMemo(
() => schemasIDs.map(id => cache.getSchema(id)).filter(item => item !== undefined), () => schemasIDs.map(id => cache.getSchema(id)).filter(item => item !== undefined),
// eslint-disable-next-line react-compiler/react-compiler
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
[schemasIDs, cache.getSchema] [schemasIDs, cache.getSchema]
); );
@ -86,12 +87,13 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
const canSubmit = useMemo(() => isModified && alias !== '', [isModified, alias]); const canSubmit = useMemo(() => isModified && alias !== '', [isModified, alias]);
useLayoutEffect(() => { useEffect(() => {
cache.preload(schemasIDs); cache.preload(schemasIDs);
// eslint-disable-next-line react-compiler/react-compiler
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [schemasIDs]); }, [schemasIDs]);
useLayoutEffect(() => { useEffect(() => {
if (cache.loading || schemas.length !== schemasIDs.length) { if (cache.loading || schemas.length !== schemasIDs.length) {
return; return;
} }
@ -108,10 +110,11 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
return true; return true;
}) })
); );
// eslint-disable-next-line react-compiler/react-compiler
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [schemasIDs, schemas, cache.loading]); }, [schemasIDs, schemas, cache.loading]);
useLayoutEffect(() => { useEffect(() => {
if (cache.loading || schemas.length !== schemasIDs.length) { if (cache.loading || schemas.length !== schemasIDs.length) {
return; return;
} }

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useEffect, useLayoutEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import PickConstituenta from '@/components/select/PickConstituenta'; import PickConstituenta from '@/components/select/PickConstituenta';
import SelectMultiGrammeme from '@/components/select/SelectMultiGrammeme'; import SelectMultiGrammeme from '@/components/select/SelectMultiGrammeme';
@ -31,7 +31,7 @@ function TabEntityReference({ initial, schema, onChangeValid, onChangeReference
const [selectedGrams, setSelectedGrams] = useState<IGrammemeOption[]>([]); const [selectedGrams, setSelectedGrams] = useState<IGrammemeOption[]>([]);
// Initialization // Initialization
useLayoutEffect(() => { useEffect(() => {
if (!!initial.refRaw && initial.type === ReferenceType.ENTITY) { if (!!initial.refRaw && initial.type === ReferenceType.ENTITY) {
const ref = parseEntityReference(initial.refRaw); const ref = parseEntityReference(initial.refRaw);
setAlias(ref.entity); setAlias(ref.entity);

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useEffect, useLayoutEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import TextInput from '@/components/ui/TextInput'; import TextInput from '@/components/ui/TextInput';
import { ReferenceType } from '@/models/language'; import { ReferenceType } from '@/models/language';
@ -27,7 +27,7 @@ function TabSyntacticReference({ initial, onChangeValid, onChangeReference }: Ta
} }
}, [initial, offset]); }, [initial, offset]);
useLayoutEffect(() => { useEffect(() => {
if (initial.refRaw && initial.type === ReferenceType.SYNTACTIC) { if (initial.refRaw && initial.type === ReferenceType.SYNTACTIC) {
const ref = parseSyntacticReference(initial.refRaw); const ref = parseSyntacticReference(initial.refRaw);
setOffset(ref.offset); setOffset(ref.offset);

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useLayoutEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { IconReset, IconSave } from '@/components/Icons'; import { IconReset, IconSave } from '@/components/Icons';
import MiniButton from '@/components/ui/MiniButton'; import MiniButton from '@/components/ui/MiniButton';
@ -59,7 +59,7 @@ function DlgEditVersions({ hideWindow, versions, onDelete, onUpdate }: DlgEditVe
setDescription(selected?.description ?? ''); setDescription(selected?.description ?? '');
} }
useLayoutEffect(() => { useEffect(() => {
setVersion(selected?.version ?? ''); setVersion(selected?.version ?? '');
setDescription(selected?.description ?? ''); setDescription(selected?.description ?? '');
}, [selected]); }, [selected]);

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useLayoutEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { IconAccept, IconMoveDown, IconMoveLeft, IconMoveRight, IconRemove } from '@/components/Icons'; import { IconAccept, IconMoveDown, IconMoveLeft, IconMoveRight, IconRemove } from '@/components/Icons';
import SelectMultiGrammeme from '@/components/select/SelectMultiGrammeme'; import SelectMultiGrammeme from '@/components/select/SelectMultiGrammeme';
@ -33,7 +33,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
const [inputGrams, setInputGrams] = useState<IGrammemeOption[]>([]); const [inputGrams, setInputGrams] = useState<IGrammemeOption[]>([]);
const [forms, setForms] = useState<IWordForm[]>([]); const [forms, setForms] = useState<IWordForm[]>([]);
useLayoutEffect(() => { useEffect(() => {
const initForms: IWordForm[] = []; const initForms: IWordForm[] = [];
target.term_forms.forEach(term => target.term_forms.forEach(term =>
initForms.push({ initForms.push({

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useLayoutEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import Modal, { ModalProps } from '@/components/ui/Modal'; import Modal, { ModalProps } from '@/components/ui/Modal';
import SelectSingle from '@/components/ui/SelectSingle'; import SelectSingle from '@/components/ui/SelectSingle';
@ -25,13 +25,13 @@ function DlgRenameCst({ hideWindow, initial, allowChangeType, onRename }: DlgRen
const [validated, setValidated] = useState(false); const [validated, setValidated] = useState(false);
const [cstData, updateData] = usePartialUpdate(initial); const [cstData, updateData] = usePartialUpdate(initial);
useLayoutEffect(() => { useEffect(() => {
if (schema && initial && cstData.cst_type !== initial.cst_type) { if (schema && initial && cstData.cst_type !== initial.cst_type) {
updateData({ alias: generateAlias(cstData.cst_type, schema) }); updateData({ alias: generateAlias(cstData.cst_type, schema) });
} }
}, [initial, cstData.cst_type, updateData, schema]); }, [initial, cstData.cst_type, updateData, schema]);
useLayoutEffect(() => { useEffect(() => {
setValidated( setValidated(
!!schema && cstData.alias !== initial.alias && validateNewAlias(cstData.alias, cstData.cst_type, schema) !!schema && cstData.alias !== initial.alias && validateNewAlias(cstData.alias, cstData.cst_type, schema)
); );

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useLayoutEffect } from 'react'; import { useEffect } from 'react';
import { Edge, MarkerType, Node, ReactFlow, useEdgesState, useNodesState } from 'reactflow'; import { Edge, MarkerType, Node, ReactFlow, useEdgesState, useNodesState } from 'reactflow';
import { SyntaxTree } from '@/models/rslang'; import { SyntaxTree } from '@/models/rslang';
@ -20,7 +20,7 @@ function ASTFlow({ data, onNodeEnter, onNodeLeave, onChangeDragging }: ASTFlowPr
const [nodes, setNodes, onNodesChange] = useNodesState([]); const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges] = useEdgesState([]); const [edges, setEdges] = useEdgesState([]);
useLayoutEffect(() => { useEffect(() => {
const newNodes = data.map(node => ({ const newNodes = data.map(node => ({
id: String(node.uid), id: String(node.uid),
data: node, data: node,

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useLayoutEffect } from 'react'; import { useEffect } from 'react';
import { Edge, ReactFlow, useEdgesState, useNodesState } from 'reactflow'; import { Edge, ReactFlow, useEdgesState, useNodesState } from 'reactflow';
import { TMGraph } from '@/models/TMGraph'; import { TMGraph } from '@/models/TMGraph';
@ -20,7 +20,7 @@ function MGraphFlow({ data }: MGraphFlowProps) {
const [nodes, setNodes, onNodesChange] = useNodesState([]); const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges] = useEdgesState([]); const [edges, setEdges] = useEdgesState([]);
useLayoutEffect(() => { useEffect(() => {
const newNodes = data.nodes.map(node => ({ const newNodes = data.nodes.map(node => ({
id: String(node.id), id: String(node.id),
data: node, data: node,

View File

@ -4,7 +4,7 @@ import { useEffect } from 'react';
import { assertIsNode } from '@/utils/utils'; import { assertIsNode } from '@/utils/utils';
function useClickedOutside(enabled: boolean, ref: React.RefObject<HTMLElement>, callback?: () => void) { function useClickedOutside(enabled: boolean, ref: React.RefObject<HTMLDivElement | null>, callback?: () => void) {
useEffect(() => { useEffect(() => {
if (!enabled) { if (!enabled) {
return; return;
@ -12,7 +12,7 @@ function useClickedOutside(enabled: boolean, ref: React.RefObject<HTMLElement>,
function handleClickOutside(event: MouseEvent) { function handleClickOutside(event: MouseEvent) {
assertIsNode(event.target); assertIsNode(event.target);
if (ref.current && !ref.current.contains(event.target)) { if (ref?.current && !ref.current.contains(event.target)) {
callback?.(); callback?.();
} }
} }

View File

@ -5,12 +5,13 @@ import { useCallback, useEffect, useState } from 'react';
import { getOssDetails } from '@/backend/oss'; import { getOssDetails } from '@/backend/oss';
import { type ErrorData } from '@/components/info/InfoError'; import { type ErrorData } from '@/components/info/InfoError';
import { useAuth } from '@/context/AuthContext'; import { useAuth } from '@/context/AuthContext';
import { ILibraryItem } from '@/models/library'; import { useLibrary } from '@/context/LibraryContext';
import { IOperationSchema, IOperationSchemaData } from '@/models/oss'; import { IOperationSchema, IOperationSchemaData } from '@/models/oss';
import { OssLoader } from '@/models/OssLoader'; import { OssLoader } from '@/models/OssLoader';
function useOssDetails({ target, items }: { target?: string; items: ILibraryItem[] }) { function useOssDetails({ target }: { target?: string }) {
const { loading: userLoading } = useAuth(); const { loading: userLoading } = useAuth();
const library = useLibrary();
const [schema, setInner] = useState<IOperationSchema | undefined>(undefined); const [schema, setInner] = useState<IOperationSchema | undefined>(undefined);
const [loading, setLoading] = useState(target != undefined); const [loading, setLoading] = useState(target != undefined);
const [error, setError] = useState<ErrorData>(undefined); const [error, setError] = useState<ErrorData>(undefined);
@ -21,12 +22,16 @@ function useOssDetails({ target, items }: { target?: string; items: ILibraryItem
setInner(undefined); setInner(undefined);
return; return;
} }
const newSchema = new OssLoader(data, items).produceOSS(); const newSchema = new OssLoader(data, library.items).produceOSS();
setInner(newSchema); setInner(newSchema);
}, },
[items] [library.items]
); );
function partialUpdate(data: Partial<IOperationSchema>) {
setInner(prev => (prev ? { ...prev, ...data } : prev));
}
const reload = useCallback( const reload = useCallback(
(setCustomLoading?: typeof setLoading, callback?: () => void) => { (setCustomLoading?: typeof setLoading, callback?: () => void) => {
setError(undefined); setError(undefined);
@ -50,12 +55,12 @@ function useOssDetails({ target, items }: { target?: string; items: ILibraryItem
); );
useEffect(() => { useEffect(() => {
if (!userLoading) { if (!userLoading && !library.loading && library.items.length > 0) {
reload(); reload();
} }
}, [reload, userLoading]); }, [reload, userLoading, library.loading, library.items]);
return { schema, setSchema, reload, error, setError, loading }; return { schema, setSchema, partialUpdate, reload, error, setError, loading };
} }
export default useOssDetails; export default useOssDetails;

View File

@ -76,6 +76,7 @@ function useRSFormCache() {
} }
}) })
); );
// eslint-disable-next-line react-compiler/react-compiler
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [pending]); }, [pending]);

View File

@ -23,6 +23,10 @@ function useRSFormDetails({ target, version }: { target?: string; version?: stri
setInnerSchema(schema); setInnerSchema(schema);
} }
function partialUpdate(data: Partial<IRSForm>) {
setInnerSchema(prev => (prev ? { ...prev, ...data } : prev));
}
const reload = useCallback( const reload = useCallback(
(setCustomLoading?: typeof setLoading, callback?: () => void) => { (setCustomLoading?: typeof setLoading, callback?: () => void) => {
setError(undefined); setError(undefined);
@ -51,7 +55,7 @@ function useRSFormDetails({ target, version }: { target?: string; version?: stri
} }
}, [reload, userLoading]); }, [reload, userLoading]);
return { schema, setSchema, reload, error, setError, loading }; return { schema, setSchema, partialUpdate, reload, error, setError, loading };
} }
export default useRSFormDetails; export default useRSFormDetails;

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
@ -107,14 +107,14 @@ function FormCreateItem() {
setBody(newValue.length > 3 ? newValue.substring(3) : ''); setBody(newValue.length > 3 ? newValue.substring(3) : '');
}, []); }, []);
useLayoutEffect(() => { useEffect(() => {
if (!options.location) { if (!options.location) {
return; return;
} }
handleSelectLocation(options.location); handleSelectLocation(options.location);
}, [options.location, handleSelectLocation]); }, [options.location, handleSelectLocation]);
useLayoutEffect(() => { useEffect(() => {
if (itemType !== LibraryItemType.RSFORM) { if (itemType !== LibraryItemType.RSFORM) {
setFile(undefined); setFile(undefined);
setFileName(''); setFileName('');

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useLayoutEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch'; import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useConceptOptions } from '@/context/ConceptOptionsContext';
@ -11,7 +11,7 @@ function DatabaseSchemaPage() {
const panelHeight = useMemo(() => calculateHeight('0px'), [calculateHeight]); const panelHeight = useMemo(() => calculateHeight('0px'), [calculateHeight]);
useLayoutEffect(() => { useEffect(() => {
setNoFooter(true); setNoFooter(true);
return () => setNoFooter(false); return () => setNoFooter(false);
}, [setNoFooter]); }, [setNoFooter]);

View File

@ -1,4 +1,4 @@
import { useLayoutEffect } from 'react'; import { useEffect } from 'react';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import Loader from '@/components/ui/Loader'; import Loader from '@/components/ui/Loader';
@ -10,7 +10,7 @@ function HomePage() {
const router = useConceptNavigation(); const router = useConceptNavigation();
const { user, loading } = useAuth(); const { user, loading } = useAuth();
useLayoutEffect(() => { useEffect(() => {
if (!loading) { if (!loading) {
if (!user) { if (!user) {
setTimeout(() => { setTimeout(() => {

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import fileDownload from 'js-file-download'; import fileDownload from 'js-file-download';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { IconCSV } from '@/components/Icons'; import { IconCSV } from '@/components/Icons';
@ -82,7 +82,7 @@ function LibraryPage() {
[filter] [filter]
); );
useLayoutEffect(() => { useEffect(() => {
setItems(library.applyFilter(filter)); setItems(library.applyFilter(filter));
}, [library, library.items.length, filter]); }, [library, library.items.length, filter]);

View File

@ -30,7 +30,6 @@ function ManualsPage() {
setTimeout(() => { setTimeout(() => {
router.push(urls.page404); router.push(urls.page404);
}, PARAMETER.refreshTimeout); }, PARAMETER.refreshTimeout);
console.log(1);
return null; return null;
} }

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useEffect, useLayoutEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { IconSave } from '@/components/Icons'; import { IconSave } from '@/components/Icons';
@ -25,11 +25,21 @@ function FormOSS({ id, isModified, setIsModified }: FormOSSProps) {
const { schema, update, processing } = useOSS(); const { schema, update, processing } = useOSS();
const controller = useOssEdit(); const controller = useOssEdit();
const [title, setTitle] = useState(''); const [title, setTitle] = useState(schema?.title ?? '');
const [alias, setAlias] = useState(''); const [alias, setAlias] = useState(schema?.alias ?? '');
const [comment, setComment] = useState(''); const [comment, setComment] = useState(schema?.comment ?? '');
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(schema?.visible ?? false);
const [readOnly, setReadOnly] = useState(false); const [readOnly, setReadOnly] = useState(schema?.read_only ?? false);
useEffect(() => {
if (schema) {
setTitle(schema.title);
setAlias(schema.alias);
setComment(schema.comment);
setVisible(schema.visible);
setReadOnly(schema.read_only);
}
}, [schema]);
useEffect(() => { useEffect(() => {
if (!schema) { if (!schema) {
@ -59,16 +69,6 @@ function FormOSS({ id, isModified, setIsModified }: FormOSSProps) {
setIsModified setIsModified
]); ]);
useLayoutEffect(() => {
if (schema) {
setTitle(schema.title);
setAlias(schema.alias);
setComment(schema.comment);
setVisible(schema.visible);
setReadOnly(schema.read_only);
}
}, [schema]);
const handleSubmit = (event?: React.FormEvent<HTMLFormElement>) => { const handleSubmit = (event?: React.FormEvent<HTMLFormElement>) => {
if (event) { if (event) {
event.preventDefault(); event.preventDefault();

View File

@ -50,7 +50,7 @@ function NodeContextMenu({
}: NodeContextMenuProps) { }: NodeContextMenuProps) {
const controller = useOssEdit(); const controller = useOssEdit();
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const ref = useRef(null); const ref = useRef<HTMLDivElement>(null);
const readyForSynthesis = useMemo(() => { const readyForSynthesis = useMemo(() => {
if (operation.operation_type !== OperationType.SYNTHESIS) { if (operation.operation_type !== OperationType.SYNTHESIS) {
return false; return false;

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import { toPng } from 'html-to-image'; import { toPng } from 'html-to-image';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { import {
Background, Background,
@ -69,7 +69,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
onChange: onSelectionChange onChange: onSelectionChange
}); });
useLayoutEffect(() => { useEffect(() => {
if (!model.schema) { if (!model.schema) {
setNodes([]); setNodes([]);
setEdges([]); setEdges([]);

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react'; import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
@ -126,7 +126,7 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
[model, targetOperationID] [model, targetOperationID]
); );
useLayoutEffect( useEffect(
() => () =>
setAccessLevel(prev => { setAccessLevel(prev => {
if ( if (

View File

@ -2,7 +2,7 @@
import axios from 'axios'; import axios from 'axios';
import clsx from 'clsx'; import clsx from 'clsx';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { TabList, TabPanel, Tabs } from 'react-tabs'; import { TabList, TabPanel, Tabs } from 'react-tabs';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@ -50,7 +50,7 @@ function OssTabs() {
(user.is_staff || user.id == schema.owner || schema.editors.includes(user.id)) (user.is_staff || user.id == schema.owner || schema.editors.includes(user.id))
); );
useLayoutEffect(() => { useEffect(() => {
if (schema) { if (schema) {
const oldTitle = document.title; const oldTitle = document.title;
document.title = schema.title; document.title = schema.title;
@ -60,7 +60,7 @@ function OssTabs() {
} }
}, [schema, schema?.title]); }, [schema, schema?.title]);
useLayoutEffect(() => { useEffect(() => {
setNoFooter(activeTab === OssTabID.GRAPH); setNoFooter(activeTab === OssTabID.GRAPH);
}, [activeTab, setNoFooter]); }, [activeTab, setNoFooter]);

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useEffect, useLayoutEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { IconChild, IconPredecessor, IconSave } from '@/components/Icons'; import { IconChild, IconPredecessor, IconSave } from '@/components/Icons';
@ -51,10 +51,10 @@ function FormConstituenta({
}: FormConstituentaProps) { }: FormConstituentaProps) {
const { schema, cstUpdate, processing } = useRSForm(); const { schema, cstUpdate, processing } = useRSForm();
const [term, setTerm] = useState(''); const [term, setTerm] = useState(state?.term_raw ?? '');
const [textDefinition, setTextDefinition] = useState(''); const [textDefinition, setTextDefinition] = useState(state?.definition_raw ?? '');
const [expression, setExpression] = useState(''); const [expression, setExpression] = useState(state?.definition_formal ?? '');
const [convention, setConvention] = useState(''); const [convention, setConvention] = useState(state?.convention ?? '');
const [typification, setTypification] = useState('N/A'); const [typification, setTypification] = useState('N/A');
const [showTypification, setShowTypification] = useState(false); const [showTypification, setShowTypification] = useState(false);
const [localParse, setLocalParse] = useState<IExpressionParse | undefined>(undefined); const [localParse, setLocalParse] = useState<IExpressionParse | undefined>(undefined);
@ -79,6 +79,18 @@ function FormConstituenta({
[state, forceComment, isBasic] [state, forceComment, isBasic]
); );
useEffect(() => {
if (state) {
setConvention(state.convention);
setTerm(state.term_raw);
setTextDefinition(state.definition_raw);
setExpression(state.definition_formal);
setTypification(state ? labelCstTypification(state) : 'N/A');
setForceComment(false);
setLocalParse(undefined);
}
}, [state, schema, toggleReset]);
useEffect(() => { useEffect(() => {
if (!state) { if (!state) {
setIsModified(false); setIsModified(false);
@ -104,18 +116,6 @@ function FormConstituenta({
setIsModified setIsModified
]); ]);
useLayoutEffect(() => {
if (state) {
setConvention(state.convention || '');
setTerm(state.term_raw || '');
setTextDefinition(state.definition_raw || '');
setExpression(state.definition_formal || '');
setTypification(state ? labelCstTypification(state) : 'N/A');
setForceComment(false);
setLocalParse(undefined);
}
}, [state, schema, toggleReset]);
function handleSubmit(event?: React.FormEvent<HTMLFormElement>) { function handleSubmit(event?: React.FormEvent<HTMLFormElement>) {
if (event) { if (event) {
event.preventDefault(); event.preventDefault();

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import { ReactCodeMirrorRef } from '@uiw/react-codemirror'; import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import BadgeHelp from '@/components/info/BadgeHelp'; import BadgeHelp from '@/components/info/BadgeHelp';
@ -69,7 +69,7 @@ function EditorRSExpression({
const [showAST, setShowAST] = useState(false); const [showAST, setShowAST] = useState(false);
const [showControls, setShowControls] = useLocalStorage(storage.rseditShowControls, true); const [showControls, setShowControls] = useLocalStorage(storage.rseditShowControls, true);
useLayoutEffect(() => { useEffect(() => {
setIsModified(false); setIsModified(false);
resetParse(); resetParse();
}, [activeCst, resetParse, toggleReset]); }, [activeCst, resetParse, toggleReset]);

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useEffect, useLayoutEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { IconSave } from '@/components/Icons'; import { IconSave } from '@/components/Icons';
import SelectVersion from '@/components/select/SelectVersion'; import SelectVersion from '@/components/select/SelectVersion';
@ -25,11 +25,21 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
const controller = useRSEdit(); const controller = useRSEdit();
const schema = controller.schema; const schema = controller.schema;
const [title, setTitle] = useState(''); const [title, setTitle] = useState(schema?.title ?? '');
const [alias, setAlias] = useState(''); const [alias, setAlias] = useState(schema?.alias ?? '');
const [comment, setComment] = useState(''); const [comment, setComment] = useState(schema?.comment ?? '');
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(schema?.visible ?? false);
const [readOnly, setReadOnly] = useState(false); const [readOnly, setReadOnly] = useState(schema?.read_only ?? false);
useEffect(() => {
if (schema) {
setTitle(schema.title);
setAlias(schema.alias);
setComment(schema.comment);
setVisible(schema.visible);
setReadOnly(schema.read_only);
}
}, [schema]);
useEffect(() => { useEffect(() => {
if (!schema) { if (!schema) {
@ -59,16 +69,6 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
setIsModified setIsModified
]); ]);
useLayoutEffect(() => {
if (schema) {
setTitle(schema.title);
setAlias(schema.alias);
setComment(schema.comment);
setVisible(schema.visible);
setReadOnly(schema.read_only);
}
}, [schema]);
const handleSubmit = (event?: React.FormEvent<HTMLFormElement>) => { const handleSubmit = (event?: React.FormEvent<HTMLFormElement>) => {
if (event) { if (event) {
event.preventDefault(); event.preventDefault();

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import fileDownload from 'js-file-download'; import fileDownload from 'js-file-download';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { IconCSV } from '@/components/Icons'; import { IconCSV } from '@/components/Icons';
@ -32,7 +32,7 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) {
const [filtered, setFiltered] = useState<IConstituenta[]>(controller.schema?.items ?? []); const [filtered, setFiltered] = useState<IConstituenta[]>(controller.schema?.items ?? []);
const [filterText, setFilterText] = useState(''); const [filterText, setFilterText] = useState('');
useLayoutEffect(() => { useEffect(() => {
if (filtered.length === 0) { if (filtered.length === 0) {
setRowSelection({}); setRowSelection({});
return; return;
@ -44,7 +44,7 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) {
setRowSelection(newRowSelection); setRowSelection(newRowSelection);
}, [filtered, setRowSelection, controller.selected]); }, [filtered, setRowSelection, controller.selected]);
useLayoutEffect(() => { useEffect(() => {
if (!controller.schema || controller.schema.items.length === 0) { if (!controller.schema || controller.schema.items.length === 0) {
setFiltered([]); setFiltered([]);
} else if (filterText) { } else if (filterText) {

View File

@ -2,7 +2,7 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { toPng } from 'html-to-image'; import { toPng } from 'html-to-image';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { import {
Edge, Edge,
@ -113,7 +113,7 @@ function TGFlow({ onOpenEdit }: TGFlowProps) {
onChange: onSelectionChange onChange: onSelectionChange
}); });
useLayoutEffect(() => { useEffect(() => {
if (!controller.schema) { if (!controller.schema) {
return; return;
} }
@ -127,7 +127,7 @@ function TGFlow({ onOpenEdit }: TGFlowProps) {
setHoverID(undefined); setHoverID(undefined);
}, [controller.schema, filteredGraph]); }, [controller.schema, filteredGraph]);
useLayoutEffect(() => { useEffect(() => {
if (!controller.schema) { if (!controller.schema) {
return; return;
} }
@ -176,10 +176,11 @@ function TGFlow({ onOpenEdit }: TGFlowProps) {
setNodes(newNodes); setNodes(newNodes);
setEdges(newEdges); setEdges(newEdges);
// NOTE: Do not rerender on controller.selected change because it is only needed during first load // NOTE: Do not rerender on controller.selected change because it is only needed during first load
// eslint-disable-next-line react-compiler/react-compiler
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [filteredGraph, setNodes, setEdges, controller.schema, filterParams.noText, focusCst, coloring, colors, flow]); }, [filteredGraph, setNodes, setEdges, controller.schema, filterParams.noText, focusCst, coloring, colors, flow]);
useLayoutEffect(() => { useEffect(() => {
setTimeout(() => { setTimeout(() => {
flow.fitView({ duration: PARAMETER.zoomDuration }); flow.fitView({ duration: PARAMETER.zoomDuration });
}, PARAMETER.minimalTimeout); }, PARAMETER.minimalTimeout);

View File

@ -1,4 +1,4 @@
import { useLayoutEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { Graph } from '@/models/Graph'; import { Graph } from '@/models/Graph';
import { GraphFilterParams } from '@/models/miscellaneous'; import { GraphFilterParams } from '@/models/miscellaneous';
@ -20,7 +20,7 @@ function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams,
return result; return result;
}, [params]); }, [params]);
useLayoutEffect(() => { useEffect(() => {
if (!schema) { if (!schema) {
setFiltered(new Graph()); setFiltered(new Graph());
return; return;

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import fileDownload from 'js-file-download'; import fileDownload from 'js-file-download';
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react'; import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
@ -194,7 +194,7 @@ export const RSEditState = ({
[model.schema] [model.schema]
); );
useLayoutEffect( useEffect(
() => () =>
setAccessLevel(prev => { setAccessLevel(prev => {
if ( if (

View File

@ -2,7 +2,7 @@
import axios from 'axios'; import axios from 'axios';
import clsx from 'clsx'; import clsx from 'clsx';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { TabList, TabPanel, Tabs } from 'react-tabs'; import { TabList, TabPanel, Tabs } from 'react-tabs';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@ -62,7 +62,7 @@ function RSTabs() {
} }
}, [schema, selected]); }, [schema, selected]);
useLayoutEffect(() => { useEffect(() => {
if (schema) { if (schema) {
const oldTitle = document.title; const oldTitle = document.title;
document.title = schema.title; document.title = schema.title;
@ -72,7 +72,7 @@ function RSTabs() {
} }
}, [schema, schema?.title]); }, [schema, schema?.title]);
useLayoutEffect(() => { useEffect(() => {
setNoFooter(activeTab !== RSTabID.CARD); setNoFooter(activeTab !== RSTabID.CARD);
setIsModified(false); setIsModified(false);
if (activeTab === RSTabID.CST_EDIT) { if (activeTab === RSTabID.CST_EDIT) {

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useLayoutEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { IconChild } from '@/components/Icons'; import { IconChild } from '@/components/Icons';
import SelectGraphFilter from '@/components/select/SelectGraphFilter'; import SelectGraphFilter from '@/components/select/SelectGraphFilter';
@ -28,7 +28,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt
const [filterText, setFilterText] = useState(''); const [filterText, setFilterText] = useState('');
const [showInherited, setShowInherited] = useLocalStorage(storage.cstFilterShowInherited, true); const [showInherited, setShowInherited] = useLocalStorage(storage.cstFilterShowInherited, true);
useLayoutEffect(() => { useEffect(() => {
if (!schema || schema.items.length === 0) { if (!schema || schema.items.length === 0) {
setFiltered([]); setFiltered([]);
return; return;

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useCallback, useLayoutEffect, useMemo } from 'react'; import { useCallback, useEffect, useMemo } from 'react';
import BadgeConstituenta from '@/components/info/BadgeConstituenta'; import BadgeConstituenta from '@/components/info/BadgeConstituenta';
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable'; import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable';
@ -32,7 +32,7 @@ function TableSideConstituents({
}: TableSideConstituentsProps) { }: TableSideConstituentsProps) {
const { colors } = useConceptOptions(); const { colors } = useConceptOptions();
useLayoutEffect(() => { useEffect(() => {
if (!activeCst) { if (!activeCst) {
return; return;
} }

View File

@ -1,8 +1,7 @@
'use client'; 'use client';
import axios from 'axios'; import axios from 'axios';
import clsx from 'clsx'; import { useEffect, useMemo, useState } from 'react';
import { useLayoutEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import InfoError, { ErrorData } from '@/components/info/InfoError'; import InfoError, { ErrorData } from '@/components/info/InfoError';
@ -16,10 +15,10 @@ import { information } from '@/utils/labels';
function EditorProfile() { function EditorProfile() {
const { updateUser, user, errorProcessing, processing } = useUserProfile(); const { updateUser, user, errorProcessing, processing } = useUserProfile();
const [username, setUsername] = useState(''); const [username, setUsername] = useState(user?.username ?? '');
const [email, setEmail] = useState(''); const [email, setEmail] = useState(user?.email ?? '');
const [first_name, setFirstName] = useState(''); const [first_name, setFirstName] = useState(user?.first_name ?? '');
const [last_name, setLastName] = useState(''); const [last_name, setLastName] = useState(user?.last_name ?? '');
const isModified: boolean = useMemo(() => { const isModified: boolean = useMemo(() => {
if (!user) { if (!user) {
@ -29,7 +28,7 @@ function EditorProfile() {
}, [user, email, first_name, last_name]); }, [user, email, first_name, last_name]);
useBlockNavigation(isModified); useBlockNavigation(isModified);
useLayoutEffect(() => { useEffect(() => {
if (user) { if (user) {
setUsername(user.username); setUsername(user.username);
setEmail(user.email); setEmail(user.email);
@ -50,7 +49,7 @@ function EditorProfile() {
} }
return ( return (
<form onSubmit={handleSubmit} className={clsx('cc-column', 'w-[18rem]', 'px-6 py-2')}> <form onSubmit={handleSubmit} className='cc-column w-[18rem] px-6 py-2'>
<TextInput <TextInput
id='username' id='username'
autoComplete='username' autoComplete='username'

View File

@ -11,8 +11,8 @@ function UserContents() {
return ( return (
<DataLoader isLoading={loading} error={error} hasNoData={!user}> <DataLoader isLoading={loading} error={error} hasNoData={!user}>
<div className='cc-fade-in flex gap-6 py-2 mx-auto w-fit'> <div className='cc-fade-in flex flex-col py-2 mx-auto w-fit'>
<h1 className='mb-4 select-none'>Учетные данные пользователя</h1> <h1 className='mb-2 select-none'>Учетные данные пользователя</h1>
<div className='flex py-2'> <div className='flex py-2'>
<EditorProfile /> <EditorProfile />
<EditorPassword /> <EditorPassword />