mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-26 07:22:34 +00:00
63
frontend/src/hooks/file-operations/access-log.js
Normal file
63
frontend/src/hooks/file-operations/access-log.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import React, { useContext, useEffect, useCallback, useState, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import FileAccessLog from '../../components/dialog/file-access-log';
|
||||
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
||||
|
||||
// This hook provides content about access log
|
||||
const AccessLogContext = React.createContext(null);
|
||||
|
||||
export const AccessLogProvider = forwardRef(({ repoID, eventBus, children }, ref) => {
|
||||
const [isDialogShow, setDialogShow] = useState();
|
||||
|
||||
const pathRef = useRef('');
|
||||
const nameRef = useRef('');
|
||||
|
||||
const handleAccessLog = useCallback((path, name) => {
|
||||
pathRef.current = path;
|
||||
nameRef.current = name;
|
||||
setDialogShow(true);
|
||||
}, []);
|
||||
|
||||
const cancelAccessLog = useCallback(() => {
|
||||
setDialogShow(false);
|
||||
pathRef.current = '';
|
||||
nameRef.current = '';
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeCreateFile = eventBus.subscribe(EVENT_BUS_TYPE.ACCESS_LOG, handleAccessLog);
|
||||
return () => {
|
||||
unsubscribeCreateFile();
|
||||
};
|
||||
}, [eventBus, handleAccessLog]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleAccessLog,
|
||||
cancelAccessLog,
|
||||
}), [handleAccessLog, cancelAccessLog]);
|
||||
|
||||
return (
|
||||
<AccessLogContext.Provider value={{ eventBus, handleAccessLog }}>
|
||||
{children}
|
||||
{isDialogShow && (
|
||||
<ModalPortal>
|
||||
<FileAccessLog
|
||||
repoID={repoID}
|
||||
filePath={pathRef.current}
|
||||
fileName={nameRef.current}
|
||||
toggleDialog={cancelAccessLog}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
</AccessLogContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
export const useAccessLogContext = () => {
|
||||
const context = useContext(AccessLogContext);
|
||||
if (!context) {
|
||||
throw new Error('\'AccessLogContext\' is null');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
66
frontend/src/hooks/file-operations/copy.js
Normal file
66
frontend/src/hooks/file-operations/copy.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import React, { useContext, useEffect, useCallback, useState, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import CopyDirentDialog from '../../components/dialog/copy-dirent-dialog';
|
||||
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
||||
|
||||
// This hook provides content about copy file
|
||||
const CopyFileContext = React.createContext(null);
|
||||
|
||||
export const CopyFileProvider = forwardRef(({ eventBus, repoID, repoInfo, onCopy, onCopyItem, onCreateFolder, children }, ref) => {
|
||||
const [isDialogShow, setDialogShow] = useState();
|
||||
|
||||
const propsRef = useRef({});
|
||||
|
||||
const handleCopy = useCallback((path, direntData, isMultipleOperation = false, customCallback = null) => {
|
||||
propsRef.current = {
|
||||
path,
|
||||
isMultipleOperation,
|
||||
[isMultipleOperation ? 'selectedDirentList' : 'dirent']: direntData,
|
||||
[isMultipleOperation ? 'onItemsCopy' : 'onItemCopy']: customCallback || (isMultipleOperation ? onCopy : onCopyItem),
|
||||
};
|
||||
setDialogShow(true);
|
||||
}, [onCopy, onCopyItem]);
|
||||
|
||||
const cancelCopy = useCallback(() => {
|
||||
setDialogShow(false);
|
||||
propsRef.current = {};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeCopyFile = eventBus.subscribe(EVENT_BUS_TYPE.COPY_FILE, handleCopy);
|
||||
return () => {
|
||||
unsubscribeCopyFile();
|
||||
};
|
||||
}, [eventBus, handleCopy]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleCopy,
|
||||
cancelCopy,
|
||||
}), [handleCopy, cancelCopy]);
|
||||
|
||||
const renderDialog = useCallback(() => {
|
||||
if (!isDialogShow) return null;
|
||||
const { encrypted: repoEncrypted } = repoInfo || {};
|
||||
|
||||
return (
|
||||
<ModalPortal>
|
||||
<CopyDirentDialog repoID={repoID} repoEncrypted={repoEncrypted} onAddFolder={onCreateFolder} onCancelCopy={cancelCopy} { ...propsRef.current }/>
|
||||
</ModalPortal>
|
||||
);
|
||||
}, [repoID, repoInfo, isDialogShow, onCreateFolder, cancelCopy]);
|
||||
|
||||
return (
|
||||
<CopyFileContext.Provider value={{ eventBus, handleCopy }}>
|
||||
{children}
|
||||
{renderDialog()}
|
||||
</CopyFileContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
export const useCopyFile = () => {
|
||||
const context = useContext(CopyFileContext);
|
||||
if (!context) {
|
||||
throw new Error('\'CopyFileContext\' is null');
|
||||
}
|
||||
return context;
|
||||
};
|
71
frontend/src/hooks/file-operations/create-file.js
Normal file
71
frontend/src/hooks/file-operations/create-file.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import React, { useContext, useEffect, useCallback, useState, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import CreateFileDialog from '../../components/dialog/create-file-dialog';
|
||||
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
||||
|
||||
// This hook provides content about create file
|
||||
const CreateFileContext = React.createContext(null);
|
||||
|
||||
export const CreateFileProvider = forwardRef(({ eventBus, onCreateFile, children }, ref) => {
|
||||
const [isDialogShow, setDialogShow] = useState();
|
||||
|
||||
const pathRef = useRef('');
|
||||
const fileTypeRef = useRef('');
|
||||
const direntListRef = useRef([]);
|
||||
|
||||
const checkDuplicatedName = useCallback((newName) => {
|
||||
return direntListRef.current.some(object => object.name === newName);
|
||||
}, []);
|
||||
|
||||
const handleCreateFile = useCallback((path, direntList = [], fileType = '') => {
|
||||
pathRef.current = path;
|
||||
fileTypeRef.current = fileType;
|
||||
direntListRef.current = direntList;
|
||||
setDialogShow(true);
|
||||
}, []);
|
||||
|
||||
const cancelCreateFile = useCallback(() => {
|
||||
setDialogShow(false);
|
||||
pathRef.current = '';
|
||||
fileTypeRef.current = '';
|
||||
direntListRef.current = [];
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeCreateFile = eventBus.subscribe(EVENT_BUS_TYPE.CREATE_FILE, handleCreateFile);
|
||||
return () => {
|
||||
unsubscribeCreateFile();
|
||||
};
|
||||
}, [eventBus, handleCreateFile]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleCreateFile,
|
||||
cancelCreateFile,
|
||||
}), [handleCreateFile, cancelCreateFile]);
|
||||
|
||||
return (
|
||||
<CreateFileContext.Provider value={{ eventBus, handleCreateFile }}>
|
||||
{children}
|
||||
{isDialogShow && (
|
||||
<ModalPortal>
|
||||
<CreateFileDialog
|
||||
parentPath={pathRef.current}
|
||||
fileType={fileTypeRef.current}
|
||||
onAddFile={onCreateFile}
|
||||
checkDuplicatedName={checkDuplicatedName}
|
||||
toggleDialog={cancelCreateFile}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
</CreateFileContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
export const useCreateFile = () => {
|
||||
const context = useContext(CreateFileContext);
|
||||
if (!context) {
|
||||
throw new Error('\'CreateFileContext\' is null');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
67
frontend/src/hooks/file-operations/create-folder.js
Normal file
67
frontend/src/hooks/file-operations/create-folder.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import React, { useContext, useEffect, useCallback, useState, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import CreateFolderDialog from '../../components/dialog/create-folder-dialog';
|
||||
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
||||
|
||||
// This hook provides content about create folder
|
||||
const CreateFolderContext = React.createContext(null);
|
||||
|
||||
export const CreateFolderProvider = forwardRef(({ eventBus, onCreateFolder, children }, ref) => {
|
||||
const [isDialogShow, setDialogShow] = useState();
|
||||
|
||||
const pathRef = useRef('');
|
||||
const direntListRef = useRef([]);
|
||||
|
||||
const checkDuplicatedName = useCallback((newName) => {
|
||||
return direntListRef.current.some(object => object.name === newName);
|
||||
}, []);
|
||||
|
||||
const handleCreateFolder = useCallback((path, direntList = []) => {
|
||||
pathRef.current = path;
|
||||
direntListRef.current = direntList;
|
||||
setDialogShow(true);
|
||||
}, []);
|
||||
|
||||
const cancelCreateFolder = useCallback(() => {
|
||||
setDialogShow(false);
|
||||
pathRef.current = '';
|
||||
direntListRef.current = [];
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeCreateFolder = eventBus.subscribe(EVENT_BUS_TYPE.CREATE_FOLDER, handleCreateFolder);
|
||||
return () => {
|
||||
unsubscribeCreateFolder();
|
||||
};
|
||||
}, [eventBus, handleCreateFolder]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleCreateFolder,
|
||||
cancelCreateFolder,
|
||||
}), [handleCreateFolder, cancelCreateFolder]);
|
||||
|
||||
return (
|
||||
<CreateFolderContext.Provider value={{ eventBus, handleCreateFolder }}>
|
||||
{children}
|
||||
{isDialogShow && (
|
||||
<ModalPortal>
|
||||
<CreateFolderDialog
|
||||
parentPath={pathRef.current}
|
||||
onAddFolder={onCreateFolder}
|
||||
checkDuplicatedName={checkDuplicatedName}
|
||||
addFolderCancel={cancelCreateFolder}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
</CreateFolderContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
export const useCreateFolder = () => {
|
||||
const context = useContext(CreateFolderContext);
|
||||
if (!context) {
|
||||
throw new Error('\'CreateFolderContext\' is null');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import React, { useContext, useEffect, useCallback, useState, useRef } from 'react';
|
||||
import { useGoFileserver, fileServerRoot } from '../utils/constants';
|
||||
import { Utils } from '../utils/utils';
|
||||
import { seafileAPI } from '../utils/seafile-api';
|
||||
import URLDecorator from '../utils/url-decorator';
|
||||
import ModalPortal from '../components/modal-portal';
|
||||
import ZipDownloadDialog from '../components/dialog/zip-download-dialog';
|
||||
import toaster from '../components/toast';
|
||||
import { EVENT_BUS_TYPE } from '../components/common/event-bus-type';
|
||||
import React, { useContext, useEffect, useCallback, useState, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import { useGoFileserver, fileServerRoot } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import URLDecorator from '../../utils/url-decorator';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import ZipDownloadDialog from '../../components/dialog/zip-download-dialog';
|
||||
import toaster from '../../components/toast';
|
||||
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
||||
|
||||
// This hook provides content about download
|
||||
const DownloadFileContext = React.createContext(null);
|
||||
|
||||
export const DownloadFileProvider = ({ repoID, eventBus, children }) => {
|
||||
export const DownloadFileProvider = forwardRef(({ repoID, eventBus, children }, ref) => {
|
||||
const [isZipDialogOpen, setZipDialogOpen] = useState();
|
||||
|
||||
const pathRef = useRef('');
|
||||
@@ -56,6 +56,11 @@ export const DownloadFileProvider = ({ repoID, eventBus, children }) => {
|
||||
};
|
||||
}, [eventBus, handleDownload]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleDownload,
|
||||
cancelDownload,
|
||||
}), [handleDownload, cancelDownload]);
|
||||
|
||||
return (
|
||||
<DownloadFileContext.Provider value={{ eventBus, handleDownload }}>
|
||||
{children}
|
||||
@@ -71,7 +76,7 @@ export const DownloadFileProvider = ({ repoID, eventBus, children }) => {
|
||||
)}
|
||||
</DownloadFileContext.Provider>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export const useDownloadFile = () => {
|
||||
const context = useContext(DownloadFileContext);
|
100
frontend/src/hooks/file-operations/index.js
Normal file
100
frontend/src/hooks/file-operations/index.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import React, { useCallback, useContext, useRef } from 'react';
|
||||
import { DownloadFileProvider } from './download';
|
||||
import { CreateFileProvider } from './create-file';
|
||||
import { CreateFolderProvider } from './create-folder';
|
||||
import { RenameFileProvider } from './rename';
|
||||
import { MoveFileProvider } from './move';
|
||||
import { CopyFileProvider } from './copy';
|
||||
import { ShareFileProvider } from './share';
|
||||
import { LibSubFolderPermissionProvider } from './permission';
|
||||
import { AccessLogProvider } from './access-log';
|
||||
|
||||
// This hook provides content about file operations, like a middleware
|
||||
const FileOperationsContext = React.createContext(null);
|
||||
|
||||
export const FileOperationsProvider = ({
|
||||
repoID, repoInfo, eventBus, enableDirPrivateShare, isGroupOwnedRepo,
|
||||
onCreateFolder, onCreateFile,
|
||||
onRename,
|
||||
onMove, onMoveItem,
|
||||
onCopy, onCopyItem,
|
||||
children
|
||||
}) => {
|
||||
const createFileRef = useRef(null);
|
||||
const createFolderRef = useRef(null);
|
||||
const renameRef = useRef(null);
|
||||
const downloadRef = useRef(null);
|
||||
const moveRef = useRef(null);
|
||||
const copyRef = useRef(null);
|
||||
const shareRef = useRef(null);
|
||||
const permissionRef = useRef(null);
|
||||
const logRef = useRef(null);
|
||||
|
||||
const handleDownload = useCallback((...params) => {
|
||||
downloadRef.current.handleDownload(...params);
|
||||
}, []);
|
||||
|
||||
const handleCreateFolder = useCallback((...params) => {
|
||||
createFolderRef.current.handleCreateFolder(...params);
|
||||
}, []);
|
||||
|
||||
const handleCreateFile = useCallback((...params) => {
|
||||
createFileRef.current.handleCreateFile(...params);
|
||||
}, []);
|
||||
|
||||
const handleMove = useCallback((...params) => {
|
||||
moveRef.current.handleMove(...params);
|
||||
}, []);
|
||||
|
||||
const handleCopy = useCallback((...params) => {
|
||||
copyRef.current.handleCopy(...params);
|
||||
}, []);
|
||||
|
||||
const handleShare = useCallback((...params) => {
|
||||
shareRef.current.handleShare(...params);
|
||||
}, []);
|
||||
|
||||
const handleRename = useCallback((...params) => {
|
||||
renameRef.current.handleRename(...params);
|
||||
}, []);
|
||||
|
||||
const handleLibSubFolderPermission = useCallback((...params) => {
|
||||
permissionRef.current.handleLibSubFolderPermission(...params);
|
||||
}, []);
|
||||
|
||||
const handleAccessLog = useCallback((...params) => {
|
||||
logRef.current.handleAccessLog(...params);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<FileOperationsContext.Provider value={{ eventBus, handleDownload, handleCreateFolder, handleCreateFile, handleMove, handleCopy, handleShare, handleRename, handleLibSubFolderPermission, handleAccessLog }}>
|
||||
<CreateFolderProvider eventBus={eventBus} onCreateFolder={onCreateFolder} ref={createFolderRef}>
|
||||
<CreateFileProvider eventBus={eventBus} onCreateFile={onCreateFile} ref={createFileRef}>
|
||||
<RenameFileProvider eventBus={eventBus} onRename={onRename} ref={renameRef}>
|
||||
<MoveFileProvider eventBus={eventBus} repoID={repoID} repoInfo={repoInfo} onCreateFolder={onCreateFolder} onMove={onMove} onMoveItem={onMoveItem} ref={moveRef}>
|
||||
<CopyFileProvider eventBus={eventBus} repoID={repoID} repoInfo={repoInfo} onCreateFolder={onCreateFolder} onCopy={onCopy} onCopyItem={onCopyItem} ref={copyRef}>
|
||||
<DownloadFileProvider repoID={repoID} eventBus={eventBus} ref={downloadRef}>
|
||||
<ShareFileProvider repoID={repoID} eventBus={eventBus} repoInfo={repoInfo} isGroupOwnedRepo={isGroupOwnedRepo} enableDirPrivateShare={enableDirPrivateShare} ref={shareRef}>
|
||||
<LibSubFolderPermissionProvider repoID={repoID} eventBus={eventBus} isGroupOwnedRepo={isGroupOwnedRepo} ref={permissionRef}>
|
||||
<AccessLogProvider repoID={repoID} eventBus={eventBus} ref={logRef}>
|
||||
{children}
|
||||
</AccessLogProvider>
|
||||
</LibSubFolderPermissionProvider>
|
||||
</ShareFileProvider>
|
||||
</DownloadFileProvider>
|
||||
</CopyFileProvider>
|
||||
</MoveFileProvider>
|
||||
</RenameFileProvider>
|
||||
</CreateFileProvider>
|
||||
</CreateFolderProvider>
|
||||
</FileOperationsContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useFileOperations = () => {
|
||||
const context = useContext(FileOperationsContext);
|
||||
if (!context) {
|
||||
throw new Error('\'FileOperationsContext\' is null');
|
||||
}
|
||||
return context;
|
||||
};
|
65
frontend/src/hooks/file-operations/move.js
Normal file
65
frontend/src/hooks/file-operations/move.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import React, { useContext, useEffect, useCallback, useState, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import MoveDirentDialog from '../../components/dialog/move-dirent-dialog';
|
||||
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
||||
|
||||
// This hook provides content about move file
|
||||
const MoveFileContext = React.createContext(null);
|
||||
|
||||
export const MoveFileProvider = forwardRef(({ eventBus, repoID, repoInfo, onMove, onMoveItem, onCreateFolder, children }, ref) => {
|
||||
const [isDialogShow, setDialogShow] = useState();
|
||||
|
||||
const propsRef = useRef({});
|
||||
|
||||
const handleMove = useCallback((path, direntData, isMultipleOperation = false, customCallback) => {
|
||||
propsRef.current = {
|
||||
path,
|
||||
isMultipleOperation,
|
||||
[isMultipleOperation ? 'selectedDirentList' : 'dirent']: direntData,
|
||||
[isMultipleOperation ? 'onItemsMove' : 'onItemMove']: customCallback || (isMultipleOperation ? onMove : onMoveItem),
|
||||
};
|
||||
setDialogShow(true);
|
||||
}, [onMove, onMoveItem]);
|
||||
|
||||
const cancelMove = useCallback(() => {
|
||||
setDialogShow(false);
|
||||
propsRef.current = {};
|
||||
}, []);
|
||||
|
||||
const renderDialog = useCallback(() => {
|
||||
if (!isDialogShow) return null;
|
||||
const { encrypted: repoEncrypted } = repoInfo || {};
|
||||
return (
|
||||
<ModalPortal>
|
||||
<MoveDirentDialog repoID={repoID} repoEncrypted={repoEncrypted} onAddFolder={onCreateFolder} onCancelMove={cancelMove} { ...propsRef.current }/>
|
||||
</ModalPortal>
|
||||
);
|
||||
}, [repoID, repoInfo, isDialogShow, onCreateFolder, cancelMove]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeMoveFile = eventBus.subscribe(EVENT_BUS_TYPE.MOVE_FILE, handleMove);
|
||||
return () => {
|
||||
unsubscribeMoveFile();
|
||||
};
|
||||
}, [eventBus, handleMove]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleMove,
|
||||
cancelMove,
|
||||
}), [handleMove, cancelMove]);
|
||||
|
||||
return (
|
||||
<MoveFileContext.Provider value={{ eventBus, handleMove }}>
|
||||
{children}
|
||||
{renderDialog()}
|
||||
</MoveFileContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
export const useMoveFile = () => {
|
||||
const context = useContext(MoveFileContext);
|
||||
if (!context) {
|
||||
throw new Error('\'MoveFileContext\' is null');
|
||||
}
|
||||
return context;
|
||||
};
|
64
frontend/src/hooks/file-operations/permission.js
Normal file
64
frontend/src/hooks/file-operations/permission.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import React, { useContext, useEffect, useCallback, useState, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import LibSubFolderPermissionDialog from '../../components/dialog/lib-sub-folder-permission-dialog';
|
||||
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
||||
|
||||
// This hook provides content about lib sub-folder permission
|
||||
const LibSubFolderPermissionContext = React.createContext(null);
|
||||
|
||||
export const LibSubFolderPermissionProvider = forwardRef(({ repoID, eventBus, isGroupOwnedRepo, children }, ref) => {
|
||||
const [isDialogShow, setDialogShow] = useState();
|
||||
|
||||
const pathRef = useRef('');
|
||||
const nameRef = useRef('');
|
||||
|
||||
const handleLibSubFolderPermission = useCallback((path, name) => {
|
||||
pathRef.current = path;
|
||||
nameRef.current = name;
|
||||
setDialogShow(true);
|
||||
}, []);
|
||||
|
||||
const cancelLibSubFolderPermission = useCallback(() => {
|
||||
setDialogShow(false);
|
||||
pathRef.current = '';
|
||||
nameRef.current = '';
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeCreateFile = eventBus.subscribe(EVENT_BUS_TYPE.PERMISSION, handleLibSubFolderPermission);
|
||||
return () => {
|
||||
unsubscribeCreateFile();
|
||||
};
|
||||
}, [eventBus, handleLibSubFolderPermission]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleLibSubFolderPermission,
|
||||
cancelLibSubFolderPermission,
|
||||
}), [handleLibSubFolderPermission, cancelLibSubFolderPermission]);
|
||||
|
||||
return (
|
||||
<LibSubFolderPermissionContext.Provider value={{ eventBus, handleLibSubFolderPermission }}>
|
||||
{children}
|
||||
{isDialogShow && (
|
||||
<ModalPortal>
|
||||
<LibSubFolderPermissionDialog
|
||||
isDepartmentRepo={isGroupOwnedRepo}
|
||||
repoID={repoID}
|
||||
folderPath={pathRef.current}
|
||||
folderName={nameRef.current}
|
||||
toggleDialog={cancelLibSubFolderPermission}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
</LibSubFolderPermissionContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
export const useLibSubFolderPermissionContext = () => {
|
||||
const context = useContext(LibSubFolderPermissionContext);
|
||||
if (!context) {
|
||||
throw new Error('\'LibSubFolderPermissionContext\' is null');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
69
frontend/src/hooks/file-operations/rename.js
Normal file
69
frontend/src/hooks/file-operations/rename.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import React, { useContext, useEffect, useCallback, useState, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import RenameDialog from '../../components/dialog/rename-dialog';
|
||||
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
||||
|
||||
// This hook provides content about rename file
|
||||
const RenameFileContext = React.createContext(null);
|
||||
|
||||
export const RenameFileProvider = forwardRef(({ eventBus, onRename, children }, ref) => {
|
||||
const [isDialogShow, setDialogShow] = useState();
|
||||
|
||||
const direntRef = useRef('');
|
||||
const direntListRef = useRef([]);
|
||||
const callbackRef = useRef(() => {});
|
||||
|
||||
const checkDuplicatedName = useCallback((newName) => {
|
||||
return direntListRef.current.some(object => object.name === newName);
|
||||
}, []);
|
||||
|
||||
const handleRename = useCallback((dirent, direntList = [], callback) => {
|
||||
direntRef.current = dirent;
|
||||
direntListRef.current = direntList;
|
||||
callbackRef.current = callback || ((newName) => onRename(dirent, newName));
|
||||
setDialogShow(true);
|
||||
}, [onRename]);
|
||||
|
||||
const cancelRename = useCallback(() => {
|
||||
setDialogShow(false);
|
||||
direntRef.current = null;
|
||||
direntListRef.current = [];
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeCreateFile = eventBus.subscribe(EVENT_BUS_TYPE.RENAME_FILE, handleRename);
|
||||
return () => {
|
||||
unsubscribeCreateFile();
|
||||
};
|
||||
}, [eventBus, handleRename]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleRename,
|
||||
cancelRename,
|
||||
}), [handleRename, cancelRename]);
|
||||
|
||||
return (
|
||||
<RenameFileContext.Provider value={{ eventBus, handleRename }}>
|
||||
{children}
|
||||
{isDialogShow && (
|
||||
<ModalPortal>
|
||||
<RenameDialog
|
||||
dirent={direntRef.current}
|
||||
onRename={callbackRef.current}
|
||||
checkDuplicatedName={checkDuplicatedName}
|
||||
toggleCancel={cancelRename}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
</RenameFileContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
export const useRenameFile = () => {
|
||||
const context = useContext(RenameFileContext);
|
||||
if (!context) {
|
||||
throw new Error('\'RenameFileContext\' is null');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
68
frontend/src/hooks/file-operations/share.js
Normal file
68
frontend/src/hooks/file-operations/share.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import React, { useContext, useEffect, useCallback, useState, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import ShareDialog from '../../components/dialog/share-dialog';
|
||||
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
||||
|
||||
// This hook provides content about share
|
||||
const ShareFileContext = React.createContext(null);
|
||||
|
||||
export const ShareFileProvider = forwardRef(({ repoID, eventBus, repoInfo, enableDirPrivateShare, isGroupOwnedRepo, children }, ref) => {
|
||||
const [isDialogShow, setDialogShow] = useState();
|
||||
|
||||
const pathRef = useRef('');
|
||||
const direntRef = useRef([]);
|
||||
|
||||
const handleShare = useCallback((path, dirent) => {
|
||||
pathRef.current = path;
|
||||
direntRef.current = dirent;
|
||||
setDialogShow(true);
|
||||
}, []);
|
||||
|
||||
const cancelShare = useCallback(() => {
|
||||
setDialogShow(false);
|
||||
pathRef.current = '';
|
||||
direntRef.current = [];
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeShareFile = eventBus.subscribe(EVENT_BUS_TYPE.SHARE_FILE, handleShare);
|
||||
return () => {
|
||||
unsubscribeShareFile();
|
||||
};
|
||||
}, [eventBus, handleShare]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleShare,
|
||||
cancelShare,
|
||||
}), [handleShare, cancelShare]);
|
||||
|
||||
return (
|
||||
<ShareFileContext.Provider value={{ eventBus, handleShare }}>
|
||||
{children}
|
||||
{isDialogShow && (
|
||||
<ModalPortal>
|
||||
<ShareDialog
|
||||
itemType={direntRef.current.type}
|
||||
itemName={direntRef.current.name}
|
||||
itemPath={pathRef.current}
|
||||
userPerm={direntRef.current.permission || repoInfo.permission }
|
||||
repoID={repoID}
|
||||
repoEncrypted={false}
|
||||
enableDirPrivateShare={enableDirPrivateShare}
|
||||
isGroupOwnedRepo={isGroupOwnedRepo}
|
||||
toggleDialog={cancelShare}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
</ShareFileContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
export const useShareFile = () => {
|
||||
const context = useContext(ShareFileContext);
|
||||
if (!context) {
|
||||
throw new Error('\'ShareFileContext\' is null');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
@@ -1,3 +1,6 @@
|
||||
export { DownloadFileProvider } from './download-file';
|
||||
export { MetadataMiddlewareProvider } from './metadata-middleware';
|
||||
export { FileOperationsProvider, useFileOperations } from './file-operations';
|
||||
export { MetadataStatusProvider, useMetadataStatus } from './metadata-status';
|
||||
export {
|
||||
MetadataMiddlewareProvider,
|
||||
MetadataAIOperationsProvider, useMetadataAIOperations,
|
||||
} from '../metadata';
|
||||
|
@@ -1,174 +0,0 @@
|
||||
import React, { useContext, useCallback, useMemo, useState, useRef } from 'react';
|
||||
import metadataAPI from '../metadata/api';
|
||||
import { Utils } from '../utils/utils';
|
||||
import toaster from '../components/toast';
|
||||
import { PRIVATE_COLUMN_KEY, EVENT_BUS_TYPE } from '../metadata/constants';
|
||||
import { gettext, lang } from '../utils/constants';
|
||||
import { OCRResultPopover } from '../metadata/components/popover';
|
||||
import FileTagsDialog from '../metadata/components/dialog/file-tags-dialog';
|
||||
|
||||
// This hook provides content related to metadata ai operation
|
||||
const MetadataAIOperationsContext = React.createContext(null);
|
||||
|
||||
export const MetadataAIOperationsProvider = ({
|
||||
repoID,
|
||||
enableMetadata = false,
|
||||
enableOCR = false,
|
||||
enableTags = false,
|
||||
tagsLang,
|
||||
repoInfo,
|
||||
children
|
||||
}) => {
|
||||
const [isOcrResultDialogShow, setOcrResultDialogShow] = useState(false);
|
||||
const [isFileTagsDialogShow, setFileTagsDialogShow] = useState(false);
|
||||
|
||||
const recordRef = useRef(null);
|
||||
const targetRef = useRef(null);
|
||||
const opCallBack = useRef(null);
|
||||
|
||||
const permission = useMemo(() => repoInfo.permission !== 'admin' && repoInfo.permission !== 'rw' ? 'r' : 'rw', [repoInfo]);
|
||||
const canModify = useMemo(() => permission === 'rw', [permission]);
|
||||
|
||||
const closeFileTagsDialog = useCallback(() => {
|
||||
recordRef.current = null;
|
||||
opCallBack.current = null;
|
||||
setFileTagsDialogShow(false);
|
||||
}, []);
|
||||
|
||||
const closeOcrResultDialog = useCallback(() => {
|
||||
recordRef.current = null;
|
||||
targetRef.current = null;
|
||||
opCallBack.current = null;
|
||||
setOcrResultDialogShow(false);
|
||||
}, []);
|
||||
|
||||
const onOCR = useCallback((record, { success_callback }, target) => {
|
||||
targetRef.current = target;
|
||||
recordRef.current = record;
|
||||
opCallBack.current = success_callback;
|
||||
setOcrResultDialogShow(true);
|
||||
}, []);
|
||||
|
||||
const onOCRByImageDialog = useCallback(({ parentDir, fileName } = {}, target) => {
|
||||
recordRef.current = {
|
||||
[PRIVATE_COLUMN_KEY.PARENT_DIR]: parentDir,
|
||||
[PRIVATE_COLUMN_KEY.FILE_NAME]: fileName,
|
||||
};
|
||||
targetRef.current = target;
|
||||
|
||||
opCallBack.current = (description) => {
|
||||
const update = { [PRIVATE_COLUMN_KEY.FILE_DESCRIPTION]: description };
|
||||
metadataAPI.modifyRecord(repoID, { parentDir, fileName }, update).then(res => {
|
||||
const eventBus = window?.sfMetadataContext?.eventBus;
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.LOCAL_RECORD_CHANGED, { parentDir, fileName }, update);
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.LOCAL_RECORD_DETAIL_CHANGED, { parentDir, fileName }, update);
|
||||
});
|
||||
};
|
||||
setOcrResultDialogShow(true);
|
||||
}, [repoID]);
|
||||
|
||||
const generateDescription = useCallback(({ parentDir, fileName }, { success_callback, fail_callback } = {}) => {
|
||||
const filePath = Utils.joinPath(parentDir, fileName);
|
||||
const isImage = Utils.imageCheck(fileName);
|
||||
let APIName = '';
|
||||
if (Utils.isDescriptionSupportedFile(fileName)) {
|
||||
APIName = isImage ? 'imageCaption' : 'generateDescription';
|
||||
}
|
||||
if (!APIName) return;
|
||||
const inProgressToaster = toaster.notifyInProgress(gettext('Generating description by AI...'), { duration: null });
|
||||
metadataAPI[APIName](repoID, filePath, lang).then(res => {
|
||||
const description = res?.data?.summary || res.data.desc || '';
|
||||
inProgressToaster.close();
|
||||
toaster.success(gettext('Description generated'));
|
||||
success_callback && success_callback({ parentDir, fileName, description });
|
||||
}).catch(error => {
|
||||
inProgressToaster.close();
|
||||
const errorMessage = gettext('Failed to generate description');
|
||||
toaster.danger(errorMessage);
|
||||
fail_callback && fail_callback();
|
||||
});
|
||||
}, [repoID]);
|
||||
|
||||
const extractFilesDetails = useCallback((objIds, { success_callback, fail_callback } = {}) => {
|
||||
const inProgressToaster = toaster.notifyInProgress(gettext('Extracting file details by AI...'), { duration: null });
|
||||
metadataAPI.extractFileDetails(repoID, objIds).then(res => {
|
||||
const details = res?.data?.details || [];
|
||||
inProgressToaster.close();
|
||||
toaster.success(gettext('File details extracted'));
|
||||
success_callback && success_callback({ details });
|
||||
}).catch(error => {
|
||||
inProgressToaster.close();
|
||||
const errorMessage = gettext('Failed to extract file details');
|
||||
toaster.danger(errorMessage);
|
||||
fail_callback && fail_callback();
|
||||
});
|
||||
}, [repoID]);
|
||||
|
||||
const extractFileDetails = useCallback((objId, { success_callback, fail_callback } = {}) => {
|
||||
extractFilesDetails([objId], {
|
||||
success_callback: ({ details }) => {
|
||||
success_callback && success_callback({ detail: details[0] });
|
||||
},
|
||||
fail_callback
|
||||
});
|
||||
}, [extractFilesDetails]);
|
||||
|
||||
const faceRecognition = useCallback((objIds, { success_callback, fail_callback } = {}) => {
|
||||
const inProgressToaster = toaster.notifyInProgress(gettext('Detecting faces by AI...'), { duration: null });
|
||||
metadataAPI.recognizeFaces(repoID, objIds).then(res => {
|
||||
inProgressToaster.close();
|
||||
toaster.success(gettext('Faces detected'));
|
||||
success_callback && success_callback();
|
||||
}).catch(error => {
|
||||
inProgressToaster.close();
|
||||
const errorMessage = gettext('Failed to detect faces');
|
||||
toaster.danger(errorMessage);
|
||||
fail_callback && fail_callback();
|
||||
});
|
||||
}, [repoID]);
|
||||
|
||||
const generateFileTags = useCallback((record, { success_callback }) => {
|
||||
recordRef.current = record;
|
||||
opCallBack.current = success_callback;
|
||||
setFileTagsDialogShow(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<MetadataAIOperationsContext.Provider value={{
|
||||
enableMetadata,
|
||||
enableOCR,
|
||||
enableTags,
|
||||
tagsLang,
|
||||
canModify,
|
||||
onOCR,
|
||||
onOCRByImageDialog,
|
||||
generateDescription,
|
||||
extractFilesDetails,
|
||||
extractFileDetails,
|
||||
faceRecognition,
|
||||
generateFileTags,
|
||||
}}>
|
||||
{children}
|
||||
{isFileTagsDialogShow && (
|
||||
<FileTagsDialog record={recordRef.current} onToggle={closeFileTagsDialog} onSubmit={opCallBack.current} />
|
||||
)}
|
||||
{isOcrResultDialogShow && (
|
||||
<OCRResultPopover
|
||||
repoID={repoID}
|
||||
target={targetRef.current}
|
||||
record={recordRef.current}
|
||||
onToggle={closeOcrResultDialog}
|
||||
saveToDescription={opCallBack.current}
|
||||
/>
|
||||
)}
|
||||
</MetadataAIOperationsContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useMetadataAIOperations = () => {
|
||||
const context = useContext(MetadataAIOperationsContext);
|
||||
if (!context) {
|
||||
throw new Error('\'MetadataAIOperationsContext\' is null');
|
||||
}
|
||||
return context;
|
||||
};
|
@@ -1,38 +0,0 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { MetadataAIOperationsProvider } from './metadata-ai-operation';
|
||||
import { TagsProvider } from '../tag/hooks';
|
||||
import { CollaboratorsProvider } from '../metadata';
|
||||
import { useMetadataStatus } from './metadata-status';
|
||||
|
||||
const MetadataMiddlewareContext = React.createContext(null);
|
||||
|
||||
export const MetadataMiddlewareProvider = ({ repoID, currentPath, repoInfo, selectTagsView, tagsChangedCallback, children }) => {
|
||||
const { enableMetadata, enableOCR, enableTags, tagsLang } = useMetadataStatus();
|
||||
|
||||
return (
|
||||
<MetadataMiddlewareContext.Provider value={{}}>
|
||||
<CollaboratorsProvider repoID={repoID}>
|
||||
<TagsProvider repoID={repoID} currentPath={currentPath} repoInfo={repoInfo} selectTagsView={selectTagsView} tagsChangedCallback={tagsChangedCallback}>
|
||||
<MetadataAIOperationsProvider
|
||||
repoID={repoID}
|
||||
enableMetadata={enableMetadata}
|
||||
enableOCR={enableOCR}
|
||||
enableTags={enableTags}
|
||||
tagsLang={tagsLang}
|
||||
repoInfo={repoInfo}
|
||||
>
|
||||
{children}
|
||||
</MetadataAIOperationsProvider>
|
||||
</TagsProvider>
|
||||
</CollaboratorsProvider>
|
||||
</MetadataMiddlewareContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useMetadataMiddleware = () => {
|
||||
const context = useContext(MetadataMiddlewareContext);
|
||||
if (!context) {
|
||||
throw new Error('\'MetadataMiddlewareContext\' is null');
|
||||
}
|
||||
return context;
|
||||
};
|
Reference in New Issue
Block a user