Portal/rsconcept/frontend/src/context/LibraryContext.tsx

356 lines
10 KiB
TypeScript
Raw Normal View History

2024-06-07 20:17:03 +03:00
'use client';
2024-06-19 22:09:31 +03:00
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
2024-06-07 20:17:03 +03:00
import { DataCallback } from '@/backend/apiTransport';
2024-06-07 20:17:03 +03:00
import {
deleteLibraryItem,
getAdminLibrary,
getLibrary,
getTemplates,
postCloneLibraryItem,
postCreateLibraryItem
} from '@/backend/library';
import { getRSFormDetails, postRSFormFromFile } from '@/backend/rsforms';
2024-06-07 20:17:03 +03:00
import { ErrorData } from '@/components/info/InfoError';
2024-07-27 22:50:10 +03:00
import useOssDetails from '@/hooks/useOssDetails';
2024-06-19 22:09:31 +03:00
import { FolderTree } from '@/models/FolderTree';
import { ILibraryItem, LibraryItemID, LocationHead } from '@/models/library';
2024-06-07 20:17:03 +03:00
import { ILibraryCreateData } from '@/models/library';
import { matchLibraryItem, matchLibraryItemLocation } from '@/models/libraryAPI';
import { ILibraryFilter } from '@/models/miscellaneous';
2024-07-27 22:50:10 +03:00
import { IOperationSchema, IOperationSchemaData } from '@/models/oss';
2024-06-07 20:17:03 +03:00
import { IRSForm, IRSFormCloneData, IRSFormData } from '@/models/rsform';
import { RSFormLoader } from '@/models/RSFormLoader';
import { contextOutsideScope } from '@/utils/labels';
import { useAuth } from './AuthContext';
import { useConceptOptions } from './ConceptOptionsContext';
2024-06-07 20:17:03 +03:00
interface ILibraryContext {
items: ILibraryItem[];
templates: ILibraryItem[];
2024-06-19 22:09:31 +03:00
folders: FolderTree;
2024-06-07 20:17:03 +03:00
loading: boolean;
2024-06-19 22:09:31 +03:00
loadingError: ErrorData;
setLoadingError: (error: ErrorData) => void;
2024-07-27 22:50:10 +03:00
globalOSS: IOperationSchema | undefined;
setGlobalID: (id: string | undefined) => void;
setGlobalOSS: (data: IOperationSchemaData) => void;
ossLoading: boolean;
ossError: ErrorData;
2024-06-07 20:17:03 +03:00
processing: boolean;
2024-06-19 22:09:31 +03:00
processingError: ErrorData;
setProcessingError: (error: ErrorData) => void;
reloadOSS: (callback?: () => void) => void;
2024-06-19 22:09:31 +03:00
reloadItems: (callback?: () => void) => void;
2024-06-07 20:17:03 +03:00
applyFilter: (params: ILibraryFilter) => ILibraryItem[];
retrieveTemplate: (templateID: LibraryItemID, callback: (schema: IRSForm) => void) => void;
createItem: (data: ILibraryCreateData, callback?: DataCallback<ILibraryItem>) => void;
cloneItem: (target: LibraryItemID, data: IRSFormCloneData, callback: DataCallback<IRSFormData>) => void;
destroyItem: (target: LibraryItemID, callback?: () => void) => void;
localUpdateItem: (data: ILibraryItem) => void;
localUpdateTimestamp: (target: LibraryItemID) => void;
}
const LibraryContext = createContext<ILibraryContext | null>(null);
export const useLibrary = (): ILibraryContext => {
const context = useContext(LibraryContext);
if (context === null) {
throw new Error(contextOutsideScope('useLibrary', 'LibraryState'));
}
return context;
};
interface LibraryStateProps {
children: React.ReactNode;
}
export const LibraryState = ({ children }: LibraryStateProps) => {
const { user, loading: userLoading } = useAuth();
2024-06-07 20:17:03 +03:00
const { adminMode } = useConceptOptions();
const [items, setItems] = useState<ILibraryItem[]>([]);
const [templates, setTemplates] = useState<ILibraryItem[]>([]);
const [loading, setLoading] = useState(true);
2024-06-07 20:17:03 +03:00
const [processing, setProcessing] = useState(false);
2024-06-19 22:09:31 +03:00
const [loadingError, setLoadingError] = useState<ErrorData>(undefined);
const [processingError, setProcessingError] = useState<ErrorData>(undefined);
2024-06-07 20:17:03 +03:00
const [cachedTemplates, setCachedTemplates] = useState<IRSForm[]>([]);
2024-07-27 22:50:10 +03:00
const [ossID, setGlobalID] = useState<string | undefined>(undefined);
const {
schema: globalOSS, // prettier: split lines
error: ossError,
setSchema: setGlobalOSS,
loading: ossLoading,
reload: reloadOssInternal
2024-07-27 22:50:10 +03:00
} = useOssDetails({ target: ossID });
const reloadOSS = useCallback(
(callback?: () => void) => {
reloadOssInternal(setProcessing, callback);
},
[reloadOssInternal]
);
2024-06-19 22:09:31 +03:00
const folders = useMemo(() => {
2024-06-20 11:06:47 +03:00
const result = new FolderTree();
2024-06-19 22:09:31 +03:00
result.addPath(LocationHead.USER, 0);
result.addPath(LocationHead.COMMON, 0);
result.addPath(LocationHead.LIBRARY, 0);
result.addPath(LocationHead.PROJECTS, 0);
2024-06-20 11:06:47 +03:00
items.forEach(item => result.addPath(item.location));
2024-06-19 22:09:31 +03:00
return result;
}, [items]);
2024-06-07 20:17:03 +03:00
const applyFilter = useCallback(
(filter: ILibraryFilter) => {
let result = items;
2024-06-19 22:09:31 +03:00
if (!filter.folderMode && filter.head) {
2024-06-07 20:17:03 +03:00
result = result.filter(item => item.location.startsWith(filter.head!));
}
2024-06-27 11:34:52 +03:00
if (filter.folderMode && filter.location) {
result = result.filter(item => item.location == filter.location);
2024-06-19 22:09:31 +03:00
}
if (filter.type) {
result = result.filter(item => item.item_type === filter.type);
}
2024-06-07 20:17:03 +03:00
if (filter.isVisible !== undefined) {
result = result.filter(item => filter.isVisible === item.visible);
}
if (filter.isOwned !== undefined) {
result = result.filter(item => filter.isOwned === (item.owner === user?.id));
}
if (filter.isSubscribed !== undefined) {
result = result.filter(item => filter.isSubscribed == user?.subscriptions.includes(item.id));
}
if (filter.isEditor !== undefined) {
result = result.filter(item => filter.isEditor == user?.editor.includes(item.id));
}
2024-06-19 22:09:31 +03:00
if (!filter.folderMode && filter.path) {
result = result.filter(item => matchLibraryItemLocation(item, filter.path!));
}
2024-06-07 20:17:03 +03:00
if (filter.query) {
result = result.filter(item => matchLibraryItem(item, filter.query!));
}
return result;
},
[items, user]
);
const retrieveTemplate = useCallback(
(templateID: LibraryItemID, callback: (schema: IRSForm) => void) => {
const cached = cachedTemplates.find(schema => schema.id == templateID);
if (cached) {
callback(cached);
return;
}
2024-06-19 22:09:31 +03:00
setProcessingError(undefined);
2024-06-07 20:17:03 +03:00
getRSFormDetails(String(templateID), '', {
showError: true,
setLoading: setProcessing,
2024-06-19 22:09:31 +03:00
onError: setProcessingError,
2024-06-07 20:17:03 +03:00
onSuccess: data => {
const schema = new RSFormLoader(data).produceRSForm();
setCachedTemplates(prev => [...prev, schema]);
callback(schema);
}
});
},
[cachedTemplates]
);
const reloadItems = useCallback(
(callback?: () => void) => {
setItems([]);
2024-06-19 22:09:31 +03:00
setLoadingError(undefined);
2024-06-07 20:17:03 +03:00
if (user?.is_staff && adminMode) {
getAdminLibrary({
setLoading: setLoading,
showError: true,
2024-06-19 22:09:31 +03:00
onError: setLoadingError,
2024-06-07 20:17:03 +03:00
onSuccess: newData => {
setItems(newData);
if (callback) callback();
}
});
} else {
getLibrary({
setLoading: setLoading,
showError: true,
2024-06-19 22:09:31 +03:00
onError: setLoadingError,
2024-06-07 20:17:03 +03:00
onSuccess: newData => {
setItems(newData);
if (callback) callback();
}
});
}
},
[user, adminMode]
);
const reloadTemplates = useCallback(() => {
setTemplates([]);
getTemplates({
setLoading: setProcessing,
onError: setProcessingError,
2024-06-07 20:17:03 +03:00
showError: true,
onSuccess: newData => setTemplates(newData)
});
}, []);
useEffect(() => {
if (!userLoading) {
reloadItems();
}
}, [reloadItems, userLoading]);
2024-06-07 20:17:03 +03:00
useEffect(() => {
reloadTemplates();
}, [reloadTemplates]);
const localUpdateItem = useCallback(
(data: ILibraryItem) => {
const libraryItem = items.find(item => item.id === data.id);
if (libraryItem) Object.assign(libraryItem, data);
},
[items]
);
const localUpdateTimestamp = useCallback(
(target: LibraryItemID) => {
const libraryItem = items.find(item => item.id === target);
if (libraryItem) {
libraryItem.time_update = Date();
}
},
[items]
);
const createItem = useCallback(
(data: ILibraryCreateData, callback?: DataCallback<ILibraryItem>) => {
const onSuccess = (newSchema: ILibraryItem) =>
reloadItems(() => {
if (user && !user.subscriptions.includes(newSchema.id)) {
user.subscriptions.push(newSchema.id);
}
if (callback) callback(newSchema);
});
2024-06-19 22:09:31 +03:00
setProcessingError(undefined);
2024-06-07 20:17:03 +03:00
if (data.file) {
postRSFormFromFile({
data: data,
showError: true,
setLoading: setProcessing,
2024-06-19 22:09:31 +03:00
onError: setProcessingError,
2024-06-07 20:17:03 +03:00
onSuccess: onSuccess
});
} else {
postCreateLibraryItem({
data: data,
showError: true,
setLoading: setProcessing,
2024-06-19 22:09:31 +03:00
onError: setProcessingError,
2024-06-07 20:17:03 +03:00
onSuccess: onSuccess
});
}
},
[reloadItems, user]
);
const destroyItem = useCallback(
(target: LibraryItemID, callback?: () => void) => {
2024-06-19 22:09:31 +03:00
setProcessingError(undefined);
2024-06-07 20:17:03 +03:00
deleteLibraryItem(String(target), {
showError: true,
setLoading: setProcessing,
2024-06-19 22:09:31 +03:00
onError: setProcessingError,
2024-06-07 20:17:03 +03:00
onSuccess: () =>
reloadItems(() => {
2024-08-06 14:38:10 +03:00
if (user?.subscriptions.includes(target)) {
2024-06-07 20:17:03 +03:00
user.subscriptions.splice(
user.subscriptions.findIndex(item => item === target),
1
);
}
if (globalOSS?.schemas.includes(target)) {
reloadOSS(() => {
if (callback) callback();
});
} else {
if (callback) callback();
}
2024-06-07 20:17:03 +03:00
})
});
},
[reloadItems, reloadOSS, user, globalOSS]
2024-06-07 20:17:03 +03:00
);
const cloneItem = useCallback(
(target: LibraryItemID, data: IRSFormCloneData, callback: DataCallback<IRSFormData>) => {
if (!user) {
return;
}
2024-06-19 22:09:31 +03:00
setProcessingError(undefined);
2024-06-07 20:17:03 +03:00
postCloneLibraryItem(String(target), {
data: data,
showError: true,
setLoading: setProcessing,
2024-06-19 22:09:31 +03:00
onError: setProcessingError,
2024-06-07 20:17:03 +03:00
onSuccess: newSchema =>
reloadItems(() => {
if (user && !user.subscriptions.includes(newSchema.id)) {
user.subscriptions.push(newSchema.id);
}
if (callback) callback(newSchema);
})
});
},
2024-06-19 22:09:31 +03:00
[reloadItems, user]
2024-06-07 20:17:03 +03:00
);
return (
<LibraryContext.Provider
value={{
items,
2024-06-19 22:09:31 +03:00
folders,
2024-06-07 20:17:03 +03:00
templates,
2024-06-19 22:09:31 +03:00
2024-06-07 20:17:03 +03:00
loading,
2024-06-19 22:09:31 +03:00
loadingError,
setLoadingError,
2024-06-07 20:17:03 +03:00
processing,
2024-06-19 22:09:31 +03:00
processingError,
setProcessingError,
2024-07-27 22:50:10 +03:00
globalOSS,
setGlobalID,
setGlobalOSS,
ossLoading,
ossError,
reloadOSS,
2024-07-27 22:50:10 +03:00
2024-06-19 22:09:31 +03:00
reloadItems,
2024-06-07 20:17:03 +03:00
applyFilter,
createItem,
cloneItem,
destroyItem,
retrieveTemplate,
localUpdateItem,
localUpdateTimestamp
}}
>
{children}
</LibraryContext.Provider>
);
};