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
|
|
|
|
2024-07-24 23:20:45 +03:00
|
|
|
import { DataCallback } from '@/backend/apiTransport';
|
2024-06-07 20:17:03 +03:00
|
|
|
import {
|
|
|
|
deleteLibraryItem,
|
|
|
|
getAdminLibrary,
|
|
|
|
getLibrary,
|
|
|
|
getTemplates,
|
|
|
|
postCloneLibraryItem,
|
2024-07-24 23:20:45 +03:00
|
|
|
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-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';
|
|
|
|
import { IRSForm, IRSFormCloneData, IRSFormData } from '@/models/rsform';
|
|
|
|
import { RSFormLoader } from '@/models/RSFormLoader';
|
|
|
|
import { contextOutsideScope } from '@/utils/labels';
|
|
|
|
|
|
|
|
import { useAuth } from './AuthContext';
|
2024-06-26 19:47:05 +03:00
|
|
|
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-06-07 20:17:03 +03:00
|
|
|
processing: boolean;
|
2024-06-19 22:09:31 +03:00
|
|
|
processingError: ErrorData;
|
|
|
|
setProcessingError: (error: ErrorData) => void;
|
|
|
|
|
|
|
|
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) => {
|
2024-06-26 21:49:35 +03:00
|
|
|
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[]>([]);
|
2024-06-23 19:42:54 +03:00
|
|
|
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-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({
|
2024-06-26 21:49:35 +03:00
|
|
|
setLoading: setProcessing,
|
|
|
|
onError: setProcessingError,
|
2024-06-07 20:17:03 +03:00
|
|
|
showError: true,
|
|
|
|
onSuccess: newData => setTemplates(newData)
|
|
|
|
});
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
useEffect(() => {
|
2024-06-26 21:49:35 +03:00
|
|
|
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(() => {
|
|
|
|
if (user && user.subscriptions.includes(target)) {
|
|
|
|
user.subscriptions.splice(
|
|
|
|
user.subscriptions.findIndex(item => item === target),
|
|
|
|
1
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (callback) callback();
|
|
|
|
})
|
|
|
|
});
|
|
|
|
},
|
2024-06-19 22:09:31 +03:00
|
|
|
[reloadItems, user]
|
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,
|
|
|
|
|
|
|
|
reloadItems,
|
|
|
|
|
2024-06-07 20:17:03 +03:00
|
|
|
applyFilter,
|
|
|
|
createItem,
|
|
|
|
cloneItem,
|
|
|
|
destroyItem,
|
|
|
|
retrieveTemplate,
|
|
|
|
localUpdateItem,
|
|
|
|
localUpdateTimestamp
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{children}
|
|
|
|
</LibraryContext.Provider>
|
|
|
|
);
|
|
|
|
};
|