Shared:Selectors:Files: init, fix usage, rewrite to typescript additional components
This commit is contained in:
parent
a32702c5a0
commit
e46207cf8b
@ -1,131 +1,8 @@
|
||||
export type Security = {
|
||||
Copy: boolean;
|
||||
CopyTo: boolean;
|
||||
Create: boolean;
|
||||
Delete: boolean;
|
||||
Duplicate: boolean;
|
||||
EditAccess: boolean;
|
||||
EditRoom: boolean;
|
||||
Move: boolean;
|
||||
MoveTo: boolean;
|
||||
Mute: boolean;
|
||||
Pin: boolean;
|
||||
Read: boolean;
|
||||
Rename: boolean;
|
||||
};
|
||||
|
||||
export type Item = {
|
||||
id: number | string;
|
||||
parentId: number | string;
|
||||
rootFolderType: number | string;
|
||||
title: string;
|
||||
label: string;
|
||||
filesCount?: number;
|
||||
foldersCount?: number;
|
||||
avatar?: string;
|
||||
icon?: string;
|
||||
isFolder: boolean;
|
||||
isDisabled?: boolean;
|
||||
security: Security;
|
||||
roomType: number;
|
||||
fileExst?: string;
|
||||
shared: boolean;
|
||||
};
|
||||
|
||||
export type BreadCrumb = {
|
||||
label: string;
|
||||
id: number | string;
|
||||
isRoom: boolean;
|
||||
shared: boolean;
|
||||
};
|
||||
|
||||
type setItems = (value: Item[] | null) => Item[];
|
||||
|
||||
export type useLoadersHelperProps = {
|
||||
items: Item[] | null;
|
||||
};
|
||||
|
||||
export type setItemsCallback = (value: Item[] | null) => Item[] | null;
|
||||
export type setBreadCrumbsCallback = (
|
||||
value: BreadCrumb[] | []
|
||||
) => BreadCrumb[] | [];
|
||||
export type setTotalCallback = (value: number) => number;
|
||||
|
||||
export type useSocketHelperProps = {
|
||||
socketHelper: any;
|
||||
socketSubscribers: Set<string>;
|
||||
setItems: (callback: setItemsCallback) => void;
|
||||
setBreadCrumbs: (callback: setBreadCrumbsCallback) => void;
|
||||
setTotal: (callback: setTotalCallback) => void;
|
||||
disabledItems: string[] | number[];
|
||||
filterParam?: string;
|
||||
getIcon: (size: number, fileExst: string) => string;
|
||||
};
|
||||
|
||||
export type useRootHelperProps = {
|
||||
setBreadCrumbs: (items: BreadCrumb[]) => void;
|
||||
setIsBreadCrumbsLoading: (value: boolean) => void;
|
||||
setTotal: (value: number) => void;
|
||||
setItems: (items: Item[] | setItems) => void;
|
||||
treeFolders?: Item[];
|
||||
setIsNextPageLoading: (value: boolean) => void;
|
||||
setHasNextPage: (value: boolean) => void;
|
||||
onSetBaseFolderPath?: (
|
||||
value: number | string | undefined | BreadCrumb[]
|
||||
) => void;
|
||||
isUserOnly?: boolean;
|
||||
};
|
||||
|
||||
export type useRoomsHelperProps = {
|
||||
setBreadCrumbs: (items: BreadCrumb[]) => void;
|
||||
setIsBreadCrumbsLoading: (value: boolean) => void;
|
||||
setIsNextPageLoading: (value: boolean) => void;
|
||||
setHasNextPage: (value: boolean) => void;
|
||||
setTotal: (value: number) => void;
|
||||
setItems: (items: Item[] | setItems) => void;
|
||||
isFirstLoad: boolean;
|
||||
setIsRoot: (value: boolean) => void;
|
||||
searchValue?: string;
|
||||
isRoomsOnly: boolean;
|
||||
onSetBaseFolderPath?: (
|
||||
value: number | string | undefined | BreadCrumb[]
|
||||
) => void;
|
||||
};
|
||||
|
||||
export type useFilesHelpersProps = {
|
||||
roomsFolderId?: number;
|
||||
setBreadCrumbs: (items: BreadCrumb[]) => void;
|
||||
setIsBreadCrumbsLoading: (value: boolean) => void;
|
||||
setIsSelectedParentFolder: (value: boolean) => void;
|
||||
setIsNextPageLoading: (value: boolean) => void;
|
||||
setHasNextPage: (value: boolean) => void;
|
||||
setTotal: (value: number) => void;
|
||||
setItems: (items: Item[] | setItems) => void;
|
||||
isFirstLoad: boolean;
|
||||
selectedItemId: string | number | undefined;
|
||||
setIsRoot: (value: boolean) => void;
|
||||
searchValue?: string;
|
||||
disabledItems: string[] | number[];
|
||||
setSelectedItemSecurity: (value: Security) => void;
|
||||
isThirdParty: boolean;
|
||||
onSelectTreeNode?: (treeNode: any) => void;
|
||||
setSelectedTreeNode: (treeNode: any) => void;
|
||||
filterParam?: string;
|
||||
getRootData?: () => Promise<void>;
|
||||
onSetBaseFolderPath?: (
|
||||
value: number | string | undefined | BreadCrumb[]
|
||||
) => void;
|
||||
isRoomsOnly: boolean;
|
||||
rootThirdPartyId?: string;
|
||||
getRoomList?: (
|
||||
startIndex: number,
|
||||
isInit?: boolean,
|
||||
search?: string | null,
|
||||
isErrorPath?: boolean
|
||||
) => void;
|
||||
getIcon: (size: number, fileExst: string) => string;
|
||||
t: any;
|
||||
};
|
||||
import { TFile, TFolder } from "@docspace/shared/api/files/types";
|
||||
import { TBreadCrumb } from "@docspace/shared/components/selector/Selector.types";
|
||||
import { DeviceType } from "@docspace/shared/enums";
|
||||
import { TTheme } from "@docspace/shared/themes";
|
||||
import SocketIOHelper from "@docspace/shared/utils/socket";
|
||||
|
||||
export type FilesSelectorProps = {
|
||||
isPanelVisible: boolean;
|
||||
@ -142,6 +19,7 @@ export type FilesSelectorProps = {
|
||||
|
||||
onClose?: () => void;
|
||||
|
||||
id?: string | number;
|
||||
isMove?: boolean;
|
||||
isCopy?: boolean;
|
||||
isRestore: boolean;
|
||||
@ -155,11 +33,11 @@ export type FilesSelectorProps = {
|
||||
parentId?: number;
|
||||
rootFolderType?: number;
|
||||
|
||||
treeFolders?: Item[];
|
||||
treeFolders?: TFolder[];
|
||||
|
||||
theme: any;
|
||||
theme: TTheme;
|
||||
|
||||
selection: any[];
|
||||
selection: (TFolder | TFile)[];
|
||||
disabledItems: string[] | number[];
|
||||
setMoveToPanelVisible: (value: boolean) => void;
|
||||
setRestorePanelVisible: (value: boolean) => void;
|
||||
@ -168,32 +46,32 @@ export type FilesSelectorProps = {
|
||||
setMovingInProgress: (value: boolean) => void;
|
||||
setIsDataReady?: (value: boolean) => void;
|
||||
setSelected: (selected: "close" | "none", clearBuffer?: boolean) => void;
|
||||
setConflictDialogData: (conflicts: any, operationData: any) => void;
|
||||
itemOperationToFolder: (operationData: any) => Promise<void>;
|
||||
setConflictDialogData: (conflicts: unknown, operationData: unknown) => void;
|
||||
itemOperationToFolder: (operationData: unknown) => Promise<void>;
|
||||
clearActiveOperations: (
|
||||
folderIds: string[] | number[],
|
||||
fileIds: string[] | number[]
|
||||
fileIds: string[] | number[],
|
||||
) => void;
|
||||
checkFileConflicts: (
|
||||
selectedItemId: string | number | undefined,
|
||||
folderIds: string[] | number[],
|
||||
fileIds: string[] | number[]
|
||||
) => Promise<any>;
|
||||
fileIds: string[] | number[],
|
||||
) => Promise<unknown>;
|
||||
|
||||
onSetBaseFolderPath?: (
|
||||
value: number | string | undefined | BreadCrumb[]
|
||||
value: number | string | undefined | TBreadCrumb[],
|
||||
) => void;
|
||||
onSetNewFolderPath?: (value: number | string | undefined) => void;
|
||||
onSelectFolder?: (
|
||||
value: number | string | undefined,
|
||||
breadCrumbs: BreadCrumb[]
|
||||
breadCrumbs: TBreadCrumb[],
|
||||
) => void;
|
||||
onSelectTreeNode?: (treeNode: any) => void;
|
||||
onSelectTreeNode?: (treeNode: TFolder) => void;
|
||||
onSave?: (
|
||||
e: any,
|
||||
e: unknown,
|
||||
folderId: string | number,
|
||||
fileTitle: string,
|
||||
openNewTab: boolean
|
||||
openNewTab: boolean,
|
||||
) => void;
|
||||
onSelectFile?: (
|
||||
fileInfo: {
|
||||
@ -203,7 +81,7 @@ export type FilesSelectorProps = {
|
||||
fileExst?: string;
|
||||
inPublic?: boolean;
|
||||
},
|
||||
breadCrumbs: BreadCrumb[]
|
||||
breadCrumbs: TBreadCrumb[],
|
||||
) => void;
|
||||
|
||||
setInfoPanelIsMobileHidden: (arg: boolean) => void;
|
||||
@ -219,14 +97,14 @@ export type FilesSelectorProps = {
|
||||
|
||||
includeFolder?: boolean;
|
||||
|
||||
socketHelper: any;
|
||||
socketHelper: SocketIOHelper;
|
||||
socketSubscribers: Set<string>;
|
||||
currentDeviceType: "mobile" | "tablet" | "desktop";
|
||||
currentDeviceType: DeviceType;
|
||||
|
||||
embedded: boolean;
|
||||
withHeader: boolean;
|
||||
withCancelButton: boolean;
|
||||
settings: any;
|
||||
settings: unknown;
|
||||
|
||||
roomsFolderId?: number;
|
||||
};
|
||||
|
@ -1,354 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
// @ts-ignore
|
||||
import { getFolder, getFolderInfo } from "@docspace/shared/api/files";
|
||||
// @ts-ignore
|
||||
import FilesFilter from "@docspace/shared/api/files/filter";
|
||||
// @ts-ignore
|
||||
import { iconSize32 } from "@docspace/shared/utils/image-helpers";
|
||||
|
||||
import { PAGE_COUNT, defaultBreadCrumb } from "../utils";
|
||||
|
||||
import {
|
||||
useFilesHelpersProps,
|
||||
Item,
|
||||
BreadCrumb,
|
||||
Security,
|
||||
} from "../FilesSelector.types";
|
||||
import {
|
||||
ApplyFilterOption,
|
||||
FilesSelectorFilterTypes,
|
||||
FilterType,
|
||||
FolderType,
|
||||
} from "@docspace/shared/enums";
|
||||
|
||||
import {
|
||||
getIconPathByFolderType,
|
||||
FolderTypeValueOf,
|
||||
} from "@docspace/shared/utils/common";
|
||||
|
||||
//@ts-ignore
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
|
||||
type Room = {
|
||||
id: number;
|
||||
title: string;
|
||||
roomType: number;
|
||||
filesCount: number;
|
||||
foldersCount: number;
|
||||
security: Security;
|
||||
parentId: number;
|
||||
rootFolderType: number;
|
||||
type: FolderTypeValueOf;
|
||||
};
|
||||
const DEFAULT_FILE_EXTS = "file";
|
||||
|
||||
export const convertFoldersToItems = (
|
||||
folders: any,
|
||||
disabledItems: any[],
|
||||
filterParam?: string
|
||||
) => {
|
||||
const items = folders.map((room: Room) => {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
roomType,
|
||||
filesCount,
|
||||
foldersCount,
|
||||
security,
|
||||
parentId,
|
||||
rootFolderType,
|
||||
type,
|
||||
} = room;
|
||||
|
||||
const folderIconPath = getIconPathByFolderType(type);
|
||||
const icon = iconSize32.get(folderIconPath);
|
||||
|
||||
return {
|
||||
id,
|
||||
label: title,
|
||||
title,
|
||||
icon,
|
||||
filesCount,
|
||||
foldersCount,
|
||||
security,
|
||||
parentId,
|
||||
rootFolderType,
|
||||
isFolder: true,
|
||||
roomType,
|
||||
isDisabled: !!filterParam ? false : disabledItems.includes(id),
|
||||
};
|
||||
});
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
export const convertFilesToItems = (
|
||||
files: any,
|
||||
getIcon: (size: number, fileExst: string) => string,
|
||||
filterParam?: string
|
||||
) => {
|
||||
const items = files.map((file: any) => {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
security,
|
||||
parentId,
|
||||
folderId,
|
||||
rootFolderType,
|
||||
fileExst,
|
||||
} = file;
|
||||
|
||||
const icon = getIcon(32, fileExst || DEFAULT_FILE_EXTS);
|
||||
const label = title.replace(fileExst, "") || fileExst;
|
||||
|
||||
return {
|
||||
id,
|
||||
label,
|
||||
title,
|
||||
icon,
|
||||
security,
|
||||
parentId: parentId || folderId,
|
||||
rootFolderType,
|
||||
isFolder: false,
|
||||
isDisabled: !filterParam,
|
||||
fileExst,
|
||||
};
|
||||
});
|
||||
return items;
|
||||
};
|
||||
|
||||
export const useFilesHelper = ({
|
||||
setIsNextPageLoading,
|
||||
setHasNextPage,
|
||||
setTotal,
|
||||
setItems,
|
||||
setBreadCrumbs,
|
||||
setIsBreadCrumbsLoading,
|
||||
isFirstLoad,
|
||||
selectedItemId,
|
||||
setIsRoot,
|
||||
searchValue,
|
||||
disabledItems,
|
||||
setSelectedItemSecurity,
|
||||
isThirdParty,
|
||||
onSelectTreeNode,
|
||||
setSelectedTreeNode,
|
||||
filterParam,
|
||||
getRootData,
|
||||
onSetBaseFolderPath,
|
||||
isRoomsOnly,
|
||||
rootThirdPartyId,
|
||||
getRoomList,
|
||||
getIcon,
|
||||
t,
|
||||
setIsSelectedParentFolder,
|
||||
roomsFolderId,
|
||||
}: useFilesHelpersProps) => {
|
||||
const getFileList = React.useCallback(
|
||||
async (
|
||||
startIndex: number,
|
||||
itemId: number | string | undefined,
|
||||
isInit?: boolean,
|
||||
search?: string | null
|
||||
) => {
|
||||
setIsNextPageLoading(true);
|
||||
|
||||
const currentSearch = search
|
||||
? search
|
||||
: search === null
|
||||
? ""
|
||||
: searchValue || "";
|
||||
|
||||
const page = startIndex / PAGE_COUNT;
|
||||
|
||||
const filter = FilesFilter.getDefault();
|
||||
|
||||
filter.page = page;
|
||||
filter.pageCount = PAGE_COUNT;
|
||||
filter.search = currentSearch;
|
||||
filter.applyFilterOption = null;
|
||||
filter.withSubfolders = false;
|
||||
if (filterParam) {
|
||||
filter.applyFilterOption = ApplyFilterOption.Files;
|
||||
switch (filterParam) {
|
||||
case FilesSelectorFilterTypes.DOCX:
|
||||
filter.extension = FilesSelectorFilterTypes.DOCX;
|
||||
break;
|
||||
|
||||
case FilesSelectorFilterTypes.IMG:
|
||||
filter.filterType = FilterType.ImagesOnly;
|
||||
break;
|
||||
|
||||
case FilesSelectorFilterTypes.BackupOnly:
|
||||
filter.extension = "gz,tar";
|
||||
break;
|
||||
|
||||
case FilesSelectorFilterTypes.DOCXF:
|
||||
filter.filterType = FilterType.OFormTemplateOnly;
|
||||
break;
|
||||
|
||||
case FilesSelectorFilterTypes.XLSX:
|
||||
filter.filterType = FilterType.SpreadsheetsOnly;
|
||||
break;
|
||||
|
||||
case FilesSelectorFilterTypes.ALL:
|
||||
filter.filterType = FilterType.FilesOnly;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const id = itemId ? itemId : selectedItemId || "";
|
||||
|
||||
filter.folder = id.toString();
|
||||
|
||||
const setSettings = async (
|
||||
folderId: string | number,
|
||||
isErrorPath = false
|
||||
) => {
|
||||
if (isInit && getRootData) {
|
||||
const folder = await getFolderInfo(folderId, true);
|
||||
|
||||
const isArchive = folder.rootFolderType === FolderType.Archive;
|
||||
|
||||
if (folder.rootFolderType === FolderType.TRASH || isArchive) {
|
||||
if (isRoomsOnly && getRoomList) {
|
||||
await getRoomList(0, true, null, true);
|
||||
toastr.error(
|
||||
t("Files:ArchivedRoomAction", { name: folder.title })
|
||||
);
|
||||
return;
|
||||
}
|
||||
await getRootData();
|
||||
|
||||
if (onSetBaseFolderPath && isArchive) {
|
||||
onSetBaseFolderPath && onSetBaseFolderPath([]);
|
||||
toastr.error(
|
||||
t("Files:ArchivedRoomAction", { name: folder.title })
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const currentFolder = await getFolder(folderId, filter);
|
||||
|
||||
const { folders, files, total, count, pathParts, current } =
|
||||
currentFolder;
|
||||
|
||||
setSelectedItemSecurity(current.security);
|
||||
|
||||
const foldersList: Item[] = convertFoldersToItems(
|
||||
folders,
|
||||
disabledItems,
|
||||
filterParam
|
||||
);
|
||||
|
||||
const filesList: Item[] = convertFilesToItems(
|
||||
files,
|
||||
getIcon,
|
||||
filterParam
|
||||
);
|
||||
|
||||
const itemList = [...foldersList, ...filesList];
|
||||
|
||||
setHasNextPage(count === PAGE_COUNT);
|
||||
|
||||
onSelectTreeNode &&
|
||||
setSelectedTreeNode({ ...current, path: pathParts });
|
||||
|
||||
if (isInit) {
|
||||
let foundParentId = false,
|
||||
currentFolderIndex = -1;
|
||||
|
||||
const breadCrumbs: BreadCrumb[] = pathParts.map(
|
||||
({
|
||||
id,
|
||||
title,
|
||||
roomType,
|
||||
}: {
|
||||
id: number | string;
|
||||
title: string;
|
||||
roomType?: number;
|
||||
}) => {
|
||||
// const folderInfo: any = await getFolderInfo(folderId);
|
||||
|
||||
// const { title, id, parentId, rootFolderType, roomType } =
|
||||
// folderInfo;
|
||||
|
||||
if (!foundParentId) {
|
||||
currentFolderIndex = disabledItems.findIndex((x) => x === id);
|
||||
}
|
||||
|
||||
if (!foundParentId && currentFolderIndex !== -1) {
|
||||
foundParentId = true;
|
||||
setIsSelectedParentFolder(true);
|
||||
}
|
||||
|
||||
return {
|
||||
label: title,
|
||||
id: id,
|
||||
isRoom: roomsFolderId === id,
|
||||
roomType,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
!isThirdParty &&
|
||||
!isRoomsOnly &&
|
||||
breadCrumbs.unshift({ ...defaultBreadCrumb });
|
||||
|
||||
onSetBaseFolderPath &&
|
||||
onSetBaseFolderPath(isErrorPath ? [] : breadCrumbs);
|
||||
|
||||
setBreadCrumbs(breadCrumbs);
|
||||
setIsBreadCrumbsLoading(false);
|
||||
}
|
||||
|
||||
if (isFirstLoad || startIndex === 0) {
|
||||
setTotal(total);
|
||||
setItems(itemList);
|
||||
} else {
|
||||
setItems((prevState: Item[] | null) => {
|
||||
if (prevState) return [...prevState, ...itemList];
|
||||
return [...itemList];
|
||||
});
|
||||
}
|
||||
setIsRoot(false);
|
||||
setIsNextPageLoading(false);
|
||||
};
|
||||
|
||||
try {
|
||||
await setSettings(id);
|
||||
} catch (e) {
|
||||
sessionStorage.removeItem("filesSelectorPath");
|
||||
if (isThirdParty && rootThirdPartyId) {
|
||||
await setSettings(rootThirdPartyId, true);
|
||||
|
||||
toastr.error(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRoomsOnly && getRoomList) {
|
||||
await getRoomList(0, true, null, true);
|
||||
|
||||
toastr.error(e);
|
||||
return;
|
||||
}
|
||||
|
||||
getRootData && getRootData();
|
||||
|
||||
if (onSetBaseFolderPath) {
|
||||
onSetBaseFolderPath([]);
|
||||
toastr.error(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
[selectedItemId, searchValue, isFirstLoad, disabledItems, roomsFolderId]
|
||||
);
|
||||
|
||||
return { getFileList };
|
||||
};
|
||||
|
||||
export default useFilesHelper;
|
@ -1,151 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
// @ts-ignore
|
||||
import { getRooms } from "@docspace/shared/api/rooms";
|
||||
// @ts-ignore
|
||||
import RoomsFilter from "@docspace/shared/api/rooms/filter";
|
||||
// @ts-ignore
|
||||
import { RoomsType } from "@docspace/shared/enums";
|
||||
// @ts-ignore
|
||||
import { iconSize32 } from "@docspace/shared/utils/image-helpers";
|
||||
|
||||
import { PAGE_COUNT, defaultBreadCrumb } from "../utils";
|
||||
|
||||
import { BreadCrumb, Item, useRoomsHelperProps } from "../FilesSelector.types";
|
||||
|
||||
const getRoomLogo = (roomType: number) => {
|
||||
let path = "";
|
||||
switch (roomType) {
|
||||
case RoomsType.CustomRoom:
|
||||
path = "custom.svg";
|
||||
break;
|
||||
|
||||
case RoomsType.EditingRoom:
|
||||
path = "editing.svg";
|
||||
break;
|
||||
|
||||
case RoomsType.PublicRoom:
|
||||
path = "public.svg";
|
||||
break;
|
||||
case RoomsType.FormRoom:
|
||||
path = "form.svg";
|
||||
break;
|
||||
}
|
||||
|
||||
return iconSize32.get(path);
|
||||
};
|
||||
|
||||
export const convertRoomsToItems = (rooms: any) => {
|
||||
const items = rooms.map((room: any) => {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
roomType,
|
||||
logo,
|
||||
filesCount,
|
||||
foldersCount,
|
||||
security,
|
||||
parentId,
|
||||
rootFolderType,
|
||||
} = room;
|
||||
|
||||
const icon = logo.medium ? logo.medium : getRoomLogo(roomType);
|
||||
|
||||
return {
|
||||
id,
|
||||
label: title,
|
||||
title,
|
||||
icon,
|
||||
filesCount,
|
||||
foldersCount,
|
||||
security,
|
||||
parentId,
|
||||
rootFolderType,
|
||||
isFolder: true,
|
||||
roomType,
|
||||
color: logo.color,
|
||||
};
|
||||
});
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
const useRoomsHelper = ({
|
||||
setIsNextPageLoading,
|
||||
setHasNextPage,
|
||||
setTotal,
|
||||
setItems,
|
||||
setBreadCrumbs,
|
||||
setIsRoot,
|
||||
isFirstLoad,
|
||||
setIsBreadCrumbsLoading,
|
||||
searchValue,
|
||||
isRoomsOnly,
|
||||
onSetBaseFolderPath,
|
||||
}: useRoomsHelperProps) => {
|
||||
const getRoomList = React.useCallback(
|
||||
async (
|
||||
startIndex: number,
|
||||
isInit?: boolean,
|
||||
search?: string | null,
|
||||
isErrorPath?: boolean
|
||||
) => {
|
||||
setIsNextPageLoading(true);
|
||||
|
||||
const filterValue = search
|
||||
? search
|
||||
: search === null
|
||||
? ""
|
||||
: searchValue || "";
|
||||
|
||||
const page = startIndex / PAGE_COUNT;
|
||||
|
||||
const filter = RoomsFilter.getDefault();
|
||||
|
||||
filter.page = page;
|
||||
filter.pageCount = PAGE_COUNT;
|
||||
|
||||
filter.filterValue = filterValue;
|
||||
|
||||
const rooms = await getRooms(filter);
|
||||
|
||||
const { folders, total, count, current } = rooms;
|
||||
|
||||
const { title, id } = current;
|
||||
|
||||
if (isInit) {
|
||||
const breadCrumbs: BreadCrumb[] = [{ label: title, id, isRoom: true }];
|
||||
|
||||
!isRoomsOnly && breadCrumbs.unshift({ ...defaultBreadCrumb });
|
||||
|
||||
onSetBaseFolderPath &&
|
||||
onSetBaseFolderPath(isErrorPath ? [] : breadCrumbs);
|
||||
|
||||
setBreadCrumbs(breadCrumbs);
|
||||
|
||||
setIsBreadCrumbsLoading(false);
|
||||
}
|
||||
|
||||
const itemList: Item[] = convertRoomsToItems(folders);
|
||||
|
||||
setHasNextPage(count === PAGE_COUNT);
|
||||
|
||||
if (isFirstLoad || startIndex === 0) {
|
||||
setTotal(total);
|
||||
setItems(itemList);
|
||||
} else {
|
||||
setItems((prevState: Item[] | null) => {
|
||||
if (prevState) return [...prevState, ...itemList];
|
||||
return [...itemList];
|
||||
});
|
||||
}
|
||||
|
||||
setIsNextPageLoading(false);
|
||||
setIsRoot(false);
|
||||
},
|
||||
[isFirstLoad, searchValue]
|
||||
);
|
||||
return { getRoomList };
|
||||
};
|
||||
|
||||
export default useRoomsHelper;
|
@ -1,236 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
import { convertFilesToItems, convertFoldersToItems } from "./useFilesHelper";
|
||||
|
||||
import {
|
||||
Item,
|
||||
setItemsCallback,
|
||||
useSocketHelperProps,
|
||||
} from "../FilesSelector.types";
|
||||
import { convertRoomsToItems } from "./useRoomsHelper";
|
||||
|
||||
const useSocketHelper = ({
|
||||
socketHelper,
|
||||
socketSubscribers,
|
||||
setItems,
|
||||
setBreadCrumbs,
|
||||
setTotal,
|
||||
disabledItems,
|
||||
filterParam,
|
||||
getIcon,
|
||||
}: useSocketHelperProps) => {
|
||||
const subscribedId = React.useRef<null | number>(null);
|
||||
|
||||
const subscribe = (id: number) => {
|
||||
const roomParts = `DIR-${id}`;
|
||||
|
||||
if (socketSubscribers.has(roomParts)) return (subscribedId.current = id);
|
||||
|
||||
if (subscribedId.current && !socketSubscribers.has(roomParts)) {
|
||||
unsubscribe(subscribedId.current, false);
|
||||
}
|
||||
|
||||
socketHelper.emit({
|
||||
command: "subscribe",
|
||||
data: {
|
||||
roomParts: `DIR-${id}`,
|
||||
individual: true,
|
||||
},
|
||||
});
|
||||
|
||||
subscribedId.current = id;
|
||||
};
|
||||
|
||||
const unsubscribe = (id: number, clear = true) => {
|
||||
if (clear) {
|
||||
subscribedId.current = null;
|
||||
}
|
||||
|
||||
if (id && !socketSubscribers.has(`DIR-${id}`)) {
|
||||
socketHelper.emit({
|
||||
command: "unsubscribe",
|
||||
data: {
|
||||
roomParts: `DIR-${id}`,
|
||||
individual: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const addItem = React.useCallback((opt: any) => {
|
||||
if (!opt?.data) return;
|
||||
|
||||
const data = JSON.parse(opt.data);
|
||||
|
||||
if (
|
||||
data.folderId
|
||||
? data.folderId !== subscribedId.current
|
||||
: data.parentId !== subscribedId.current
|
||||
)
|
||||
return;
|
||||
|
||||
let item: null | Item = null;
|
||||
|
||||
if (opt?.type === "file") {
|
||||
item = convertFilesToItems([data], getIcon, filterParam)[0];
|
||||
} else if (opt?.type === "folder") {
|
||||
item = !!data.roomType
|
||||
? convertRoomsToItems([data])[0]
|
||||
: convertFoldersToItems([data], disabledItems, filterParam)[0];
|
||||
}
|
||||
|
||||
const callback: setItemsCallback = (value: Item[] | null) => {
|
||||
if (!item || !value) return value;
|
||||
|
||||
if (opt.type === "folder") {
|
||||
setTotal((value) => value + 1);
|
||||
|
||||
return [item, ...value];
|
||||
}
|
||||
|
||||
if (opt.type === "file") {
|
||||
let idx = 0;
|
||||
|
||||
for (let i = 0; i < value.length - 1; i++) {
|
||||
if (!value[i].isFolder) break;
|
||||
|
||||
idx = i + 1;
|
||||
}
|
||||
|
||||
const newValue = [...value];
|
||||
|
||||
newValue.splice(idx, 0, item);
|
||||
|
||||
setTotal((value) => value + 1);
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
setItems(callback);
|
||||
}, []);
|
||||
|
||||
const updateItem = React.useCallback((opt: any) => {
|
||||
if (!opt?.data) return;
|
||||
|
||||
const data = JSON.parse(opt.data);
|
||||
|
||||
if (
|
||||
((data.folderId && data.folderId !== subscribedId.current) ||
|
||||
(data.parentId && data.parentId !== subscribedId.current)) &&
|
||||
data.id !== subscribedId.current
|
||||
)
|
||||
return;
|
||||
|
||||
let item: null | Item = null;
|
||||
|
||||
if (opt?.type === "file") {
|
||||
item = convertFilesToItems([data], getIcon, filterParam)[0];
|
||||
} else if (opt?.type === "folder") {
|
||||
item = !!data.roomType
|
||||
? convertRoomsToItems([data])[0]
|
||||
: convertFoldersToItems([data], disabledItems, filterParam)[0];
|
||||
}
|
||||
|
||||
if (item?.id === subscribedId.current) {
|
||||
return setBreadCrumbs((value) => {
|
||||
if (!value) return value;
|
||||
|
||||
const newValue = [...value];
|
||||
|
||||
if (newValue[newValue.length - 1].id === item?.id) {
|
||||
newValue[newValue.length - 1].label = item.label;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
|
||||
const callback: setItemsCallback = (value: Item[] | null) => {
|
||||
if (!item || !value) return value;
|
||||
|
||||
if (opt.type === "folder") {
|
||||
const idx = value.findIndex((v) => v.id === item?.id && v.isFolder);
|
||||
|
||||
if (idx > -1) {
|
||||
const newValue = [...value];
|
||||
|
||||
newValue.splice(idx, 1, item);
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
setBreadCrumbs((breadCrumbsValue) => {
|
||||
return breadCrumbsValue;
|
||||
});
|
||||
}
|
||||
|
||||
if (opt.type === "file") {
|
||||
const idx = value.findIndex((v) => v.id === item?.id && !v.isFolder);
|
||||
|
||||
if (idx > -1) {
|
||||
const newValue = [...value];
|
||||
|
||||
newValue.splice(idx, 1, item);
|
||||
|
||||
return [...newValue];
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
setItems(callback);
|
||||
}, []);
|
||||
|
||||
const deleteItem = React.useCallback((opt: any) => {
|
||||
const callback: setItemsCallback = (value: Item[] | null) => {
|
||||
if (!value) return value;
|
||||
|
||||
if (opt.type === "folder") {
|
||||
const newValue = value.filter((v) => +v.id !== +opt?.id || !v.isFolder);
|
||||
|
||||
if (newValue.length !== value.length) {
|
||||
setTotal((value) => value - 1);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
if (opt.type === "file") {
|
||||
const newValue = value.filter((v) => +v.id !== +opt?.id || v.isFolder);
|
||||
|
||||
if (newValue.length !== value.length) {
|
||||
setTotal((value) => value - 1);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
setItems(callback);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
socketHelper.on("s:modify-folder", async (opt: any) => {
|
||||
switch (opt?.cmd) {
|
||||
case "create":
|
||||
addItem(opt);
|
||||
break;
|
||||
case "update":
|
||||
updateItem(opt);
|
||||
break;
|
||||
case "delete":
|
||||
deleteItem(opt);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}, [addItem, updateItem, deleteItem]);
|
||||
|
||||
return { subscribe, unsubscribe };
|
||||
};
|
||||
|
||||
export default useSocketHelper;
|
@ -1,40 +1,35 @@
|
||||
import React, { useEffect } from "react";
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
// @ts-ignore
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
import { FolderType, RoomsType } from "@docspace/shared/enums";
|
||||
import { DeviceType } from "@docspace/shared/enums";
|
||||
|
||||
import { Selector } from "@docspace/shared/components/selector";
|
||||
import { Aside } from "@docspace/shared/components/aside";
|
||||
import { Backdrop } from "@docspace/shared/components/backdrop";
|
||||
import { Portal } from "@docspace/shared/components/portal";
|
||||
import { FolderType } from "@docspace/shared/enums";
|
||||
import FilesSelector from "@docspace/shared/selectors/Files";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
|
||||
import { RowLoader, SearchLoader } from "@docspace/shared/skeletons/selector";
|
||||
|
||||
import EmptyScreenFilterAltSvgUrl from "PUBLIC_DIR/images/empty_screen_filter_alt.svg?url";
|
||||
import EmptyScreenFilterAltDarkSvgUrl from "PUBLIC_DIR/images/empty_screen_filter_alt_dark.svg?url";
|
||||
import EmptyScreenAltSvgUrl from "PUBLIC_DIR/images/empty_screen_alt.svg?url";
|
||||
import EmptyScreenAltSvgDarkUrl from "PUBLIC_DIR/images/empty_screen_alt_dark.svg?url";
|
||||
|
||||
import { SettingsStore } from "@docspace/shared/store/SettingsStore";
|
||||
import {
|
||||
BreadCrumb,
|
||||
FilesSelectorProps,
|
||||
Item,
|
||||
Security,
|
||||
} from "./FilesSelector.types";
|
||||
TFileSecurity,
|
||||
TFolder,
|
||||
TFolderSecurity,
|
||||
} from "@docspace/shared/api/files/types";
|
||||
import { TBreadCrumb } from "@docspace/shared/components/selector/Selector.types";
|
||||
import { TData } from "@docspace/shared/components/toast/Toast.type";
|
||||
import { TSelectedFileInfo } from "@docspace/shared/selectors/Files/FilesSelector.types";
|
||||
import { TRoomSecurity } from "@docspace/shared/api/rooms/types";
|
||||
import { TTranslation } from "@docspace/shared/types";
|
||||
|
||||
import useRootHelper from "./helpers/useRootHelper";
|
||||
import useRoomsHelper from "./helpers/useRoomsHelper";
|
||||
import useLoadersHelper from "./helpers/useLoadersHelper";
|
||||
import useFilesHelper from "./helpers/useFilesHelper";
|
||||
import SelectedFolderStore from "SRC_DIR/store/SelectedFolderStore";
|
||||
import FilesActionStore from "SRC_DIR/store/FilesActionsStore";
|
||||
import UploadDataStore from "SRC_DIR/store/UploadDataStore";
|
||||
import TreeFoldersStore from "SRC_DIR/store/TreeFoldersStore";
|
||||
import DialogsStore from "SRC_DIR/store/DialogsStore";
|
||||
import FilesStore from "SRC_DIR/store/FilesStore";
|
||||
import InfoPanelStore from "SRC_DIR/store/InfoPanelStore";
|
||||
|
||||
import { FilesSelectorProps } from "./FilesSelector.types";
|
||||
import { getAcceptButtonLabel, getHeaderLabel, getIsDisabled } from "./utils";
|
||||
import useSocketHelper from "./helpers/useSocketHelper";
|
||||
|
||||
const FilesSelector = ({
|
||||
const FilesSelectorWrapper = ({
|
||||
isPanelVisible = false,
|
||||
// withoutImmediatelyClose = false,
|
||||
isThirdParty = false,
|
||||
@ -59,8 +54,6 @@ const FilesSelector = ({
|
||||
|
||||
treeFolders,
|
||||
|
||||
theme,
|
||||
|
||||
selection,
|
||||
disabledItems,
|
||||
setConflictDialogData,
|
||||
@ -101,256 +94,22 @@ const FilesSelector = ({
|
||||
currentDeviceType,
|
||||
|
||||
embedded,
|
||||
withHeader,
|
||||
withHeader = true,
|
||||
withCancelButton = true,
|
||||
getIcon,
|
||||
isRoomBackup,
|
||||
|
||||
roomsFolderId,
|
||||
}: FilesSelectorProps) => {
|
||||
const { t } = useTranslation(["Files", "Common", "Translations"]);
|
||||
|
||||
const [breadCrumbs, setBreadCrumbs] = React.useState<BreadCrumb[]>([]);
|
||||
const [items, setItems] = React.useState<Item[] | null>(null);
|
||||
|
||||
const [selectedItemType, setSelectedItemType] = React.useState<
|
||||
"rooms" | "files" | undefined
|
||||
>(undefined);
|
||||
const [selectedItemId, setSelectedItemId] = React.useState<
|
||||
number | string | undefined
|
||||
>(undefined);
|
||||
const [selectedItemSecurity, setSelectedItemSecurity] = React.useState<
|
||||
Security | undefined
|
||||
>(undefined);
|
||||
const [selectedTreeNode, setSelectedTreeNode] = React.useState(null);
|
||||
const [selectedFileInfo, setSelectedFileInfo] = React.useState<{
|
||||
id: number | string;
|
||||
title: string;
|
||||
path?: string[];
|
||||
fileExst?: string;
|
||||
inPublic?: boolean;
|
||||
} | null>(null);
|
||||
|
||||
const [total, setTotal] = React.useState<number>(0);
|
||||
const [hasNextPage, setHasNextPage] = React.useState<boolean>(false);
|
||||
const [isSelectedParentFolder, setIsSelectedParentFolder] =
|
||||
React.useState<boolean>(false);
|
||||
const [searchValue, setSearchValue] = React.useState<string>("");
|
||||
const { t }: { t: TTranslation } = useTranslation([
|
||||
"Files",
|
||||
"Common",
|
||||
"Translations",
|
||||
]);
|
||||
|
||||
const [isRequestRunning, setIsRequestRunning] =
|
||||
React.useState<boolean>(false);
|
||||
|
||||
const { subscribe, unsubscribe } = useSocketHelper({
|
||||
socketHelper,
|
||||
socketSubscribers,
|
||||
setItems,
|
||||
setBreadCrumbs,
|
||||
setTotal,
|
||||
disabledItems,
|
||||
filterParam,
|
||||
getIcon,
|
||||
});
|
||||
|
||||
const {
|
||||
setIsBreadCrumbsLoading,
|
||||
isNextPageLoading,
|
||||
setIsNextPageLoading,
|
||||
isFirstLoad,
|
||||
setIsFirstLoad,
|
||||
showBreadCrumbsLoader,
|
||||
showLoader,
|
||||
} = useLoadersHelper({ items });
|
||||
|
||||
useEffect(() => {
|
||||
setIsDataReady?.(!showLoader);
|
||||
}, [showLoader, setIsDataReady]);
|
||||
|
||||
const { isRoot, setIsRoot, getRootData } = useRootHelper({
|
||||
setIsBreadCrumbsLoading,
|
||||
setBreadCrumbs,
|
||||
setTotal,
|
||||
setItems,
|
||||
treeFolders,
|
||||
setHasNextPage,
|
||||
setIsNextPageLoading,
|
||||
onSetBaseFolderPath,
|
||||
isUserOnly,
|
||||
});
|
||||
|
||||
const { getRoomList } = useRoomsHelper({
|
||||
setIsBreadCrumbsLoading,
|
||||
setBreadCrumbs,
|
||||
setIsNextPageLoading,
|
||||
setHasNextPage,
|
||||
setTotal,
|
||||
setItems,
|
||||
isFirstLoad,
|
||||
setIsRoot,
|
||||
searchValue,
|
||||
isRoomsOnly,
|
||||
onSetBaseFolderPath,
|
||||
});
|
||||
|
||||
const { getFileList } = useFilesHelper({
|
||||
setIsBreadCrumbsLoading,
|
||||
setBreadCrumbs,
|
||||
setIsNextPageLoading,
|
||||
setHasNextPage,
|
||||
setTotal,
|
||||
setItems,
|
||||
selectedItemId,
|
||||
isFirstLoad,
|
||||
setIsRoot,
|
||||
searchValue,
|
||||
disabledItems,
|
||||
setSelectedItemSecurity,
|
||||
isThirdParty,
|
||||
onSelectTreeNode,
|
||||
setSelectedTreeNode,
|
||||
filterParam,
|
||||
getRootData,
|
||||
onSetBaseFolderPath,
|
||||
isRoomsOnly,
|
||||
rootThirdPartyId,
|
||||
getRoomList,
|
||||
getIcon,
|
||||
t,
|
||||
setIsSelectedParentFolder,
|
||||
roomsFolderId,
|
||||
});
|
||||
|
||||
const onSelectAction = (item: Item) => {
|
||||
const inPublic =
|
||||
breadCrumbs.findIndex((f: any) => f.roomType === RoomsType.PublicRoom) >
|
||||
-1;
|
||||
if (item.isFolder) {
|
||||
setIsFirstLoad(true);
|
||||
setItems(null);
|
||||
setBreadCrumbs((value) => [
|
||||
...value,
|
||||
{
|
||||
label: item.label,
|
||||
id: item.id,
|
||||
isRoom:
|
||||
item.parentId === 0 && item.rootFolderType === FolderType.Rooms,
|
||||
roomType: item.roomType,
|
||||
shared: item.shared,
|
||||
},
|
||||
]);
|
||||
setSelectedItemId(item.id);
|
||||
setSearchValue("");
|
||||
|
||||
if (item.parentId === 0 && item.rootFolderType === FolderType.Rooms) {
|
||||
setSelectedItemType("rooms");
|
||||
getRoomList(0, false, null);
|
||||
} else {
|
||||
setSelectedItemType("files");
|
||||
getFileList(0, item.id, false, null);
|
||||
}
|
||||
} else {
|
||||
setSelectedFileInfo({
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
fileExst: item.fileExst,
|
||||
inPublic: inPublic,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!selectedItemId) return;
|
||||
if (selectedItemId && isRoot) return unsubscribe(+selectedItemId);
|
||||
|
||||
subscribe(+selectedItemId);
|
||||
}, [selectedItemId, isRoot]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const getRoomSettings = () => {
|
||||
setSelectedItemType("rooms");
|
||||
getRoomList(0, true);
|
||||
};
|
||||
|
||||
const needRoomList = isRoomsOnly && !currentFolderId;
|
||||
|
||||
if (needRoomList) {
|
||||
getRoomSettings();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentFolderId) {
|
||||
getRootData();
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedItemId(currentFolderId);
|
||||
|
||||
if (
|
||||
needRoomList ||
|
||||
(!isThirdParty &&
|
||||
parentId === roomsFolderId &&
|
||||
rootFolderType === FolderType.Rooms)
|
||||
) {
|
||||
getRoomSettings();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedItemType("files");
|
||||
getFileList(0, currentFolderId, true);
|
||||
}, []);
|
||||
|
||||
const onClickBreadCrumb = (item: BreadCrumb) => {
|
||||
if (!isFirstLoad) {
|
||||
setSearchValue("");
|
||||
setIsFirstLoad(true);
|
||||
|
||||
if (+item.id === 0) {
|
||||
setSelectedItemSecurity(undefined);
|
||||
setSelectedItemType(undefined);
|
||||
getRootData();
|
||||
} else {
|
||||
setItems(null);
|
||||
|
||||
const idx = breadCrumbs.findIndex(
|
||||
(value) => value.id.toString() === item.id.toString(),
|
||||
);
|
||||
|
||||
const maxLength = breadCrumbs.length - 1;
|
||||
let foundParentId = false,
|
||||
currentFolderIndex = -1;
|
||||
const newBreadCrumbs = breadCrumbs.map((item, index) => {
|
||||
if (!foundParentId) {
|
||||
currentFolderIndex = disabledItems.findIndex(
|
||||
(id) => id === item?.id,
|
||||
);
|
||||
}
|
||||
|
||||
if (index !== maxLength && currentFolderIndex !== -1) {
|
||||
foundParentId = true;
|
||||
!isSelectedParentFolder && setIsSelectedParentFolder(true);
|
||||
}
|
||||
|
||||
if (index === maxLength && !foundParentId && isSelectedParentFolder)
|
||||
setIsSelectedParentFolder(false);
|
||||
|
||||
return { ...item };
|
||||
});
|
||||
|
||||
newBreadCrumbs.splice(idx + 1, newBreadCrumbs.length - idx - 1);
|
||||
|
||||
setBreadCrumbs(newBreadCrumbs);
|
||||
setSelectedItemId(item.id);
|
||||
if (item.isRoom) {
|
||||
setSelectedItemType("rooms");
|
||||
getRoomList(0, false, null);
|
||||
} else {
|
||||
setSelectedItemType("files");
|
||||
getFileList(0, item.id, false, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onCloseAction = () => {
|
||||
setInfoPanelIsMobileHidden(false);
|
||||
|
||||
@ -375,50 +134,28 @@ const FilesSelector = ({
|
||||
onCloseAction();
|
||||
};
|
||||
|
||||
const onSearchAction = (value: string, callback?: Function) => {
|
||||
setIsFirstLoad(true);
|
||||
setItems(null);
|
||||
if (selectedItemType === "rooms") {
|
||||
getRoomList(0, false, value === "" ? null : value);
|
||||
} else {
|
||||
getFileList(0, selectedItemId, false, value === "" ? null : value);
|
||||
}
|
||||
const getFilesArchiveError = (name: string) =>
|
||||
t("Files:ArchivedRoomAction", { name });
|
||||
|
||||
setSearchValue(value);
|
||||
callback?.();
|
||||
};
|
||||
|
||||
const onClearSearchAction = (callback?: Function) => {
|
||||
setIsFirstLoad(true);
|
||||
setItems(null);
|
||||
if (selectedItemType === "rooms") {
|
||||
getRoomList(0, false, null);
|
||||
} else {
|
||||
getFileList(0, selectedItemId, false, null);
|
||||
}
|
||||
|
||||
setSearchValue("");
|
||||
callback?.();
|
||||
};
|
||||
|
||||
const onAcceptAction = (
|
||||
items: any,
|
||||
accessRights: any,
|
||||
const onAccept = async (
|
||||
selectedItemId: string | number | undefined,
|
||||
folderTitle: string,
|
||||
isPublic: boolean,
|
||||
breadCrumbs: TBreadCrumb[],
|
||||
fileName: string,
|
||||
isChecked: boolean,
|
||||
selectedTreeNode: TFolder,
|
||||
selectedFileInfo: TSelectedFileInfo,
|
||||
) => {
|
||||
const isPublic =
|
||||
breadCrumbs.findIndex((f: any) => f.roomType === RoomsType.PublicRoom) >
|
||||
-1;
|
||||
|
||||
if ((isMove || isCopy || isRestore || isRestoreAll) && !isEditorDialog) {
|
||||
const folderTitle = breadCrumbs[breadCrumbs.length - 1].label;
|
||||
const fileIds: number[] = [];
|
||||
const folderIds: number[] = [];
|
||||
|
||||
let fileIds: any[] = [];
|
||||
let folderIds: any[] = [];
|
||||
|
||||
for (let item of selection) {
|
||||
if (item.fileExst || item.contentLength) {
|
||||
for (const item of selection) {
|
||||
if (
|
||||
("fileExst" in item && item.fileExst) ||
|
||||
("contentLength" in item && item.contentLength)
|
||||
) {
|
||||
fileIds.push(item.id);
|
||||
} else if (item.id === selectedItemId) {
|
||||
toastr.error(t("MoveToFolderMessage"));
|
||||
@ -446,27 +183,30 @@ const FilesSelector = ({
|
||||
return;
|
||||
}
|
||||
|
||||
setIsRequestRunning(true);
|
||||
setSelectedItems();
|
||||
checkFileConflicts(selectedItemId, folderIds, fileIds)
|
||||
.then(async (conflicts: any) => {
|
||||
if (conflicts.length) {
|
||||
setConflictDialogData(conflicts, operationData);
|
||||
setIsRequestRunning(false);
|
||||
} else {
|
||||
setIsRequestRunning(false);
|
||||
onCloseAndDeselectAction();
|
||||
const move = !isCopy;
|
||||
if (move) setMovingInProgress(move);
|
||||
sessionStorage.setItem("filesSelectorPath", `${selectedItemId}`);
|
||||
await itemOperationToFolder(operationData);
|
||||
}
|
||||
})
|
||||
.catch((e: any) => {
|
||||
toastr.error(e);
|
||||
try {
|
||||
const conflicts = (await checkFileConflicts(
|
||||
selectedItemId,
|
||||
folderIds,
|
||||
fileIds,
|
||||
)) as [];
|
||||
|
||||
if (conflicts.length) {
|
||||
setConflictDialogData(conflicts, operationData);
|
||||
setIsRequestRunning(false);
|
||||
clearActiveOperations(fileIds, folderIds);
|
||||
});
|
||||
} else {
|
||||
setIsRequestRunning(false);
|
||||
onCloseAndDeselectAction();
|
||||
const move = !isCopy;
|
||||
if (move) setMovingInProgress(move);
|
||||
sessionStorage.setItem("filesSelectorPath", `${selectedItemId}`);
|
||||
await itemOperationToFolder(operationData);
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
toastr.error(e as TData);
|
||||
setIsRequestRunning(false);
|
||||
clearActiveOperations(fileIds, folderIds);
|
||||
}
|
||||
} else {
|
||||
toastr.error(t("Common:ErrorEmptyList"));
|
||||
}
|
||||
@ -481,16 +221,14 @@ const FilesSelector = ({
|
||||
|
||||
return;
|
||||
}
|
||||
//setIsRequestRunning(true);
|
||||
//onSetNewFolderPath && onSetNewFolderPath(breadCrumbs);
|
||||
onSelectFolder && onSelectFolder(selectedItemId, breadCrumbs);
|
||||
onSave &&
|
||||
selectedItemId &&
|
||||
|
||||
if (onSelectFolder) onSelectFolder(selectedItemId, breadCrumbs);
|
||||
if (onSave && selectedItemId)
|
||||
onSave(null, selectedItemId, fileName, isChecked);
|
||||
onSelectTreeNode && onSelectTreeNode(selectedTreeNode);
|
||||
onSelectFile && onSelectFile(selectedFileInfo!, breadCrumbs);
|
||||
!embedded && onCloseAndDeselectAction();
|
||||
//!withoutImmediatelyClose && onCloseAction();
|
||||
if (onSelectTreeNode) onSelectTreeNode(selectedTreeNode);
|
||||
if (onSelectFile && selectedFileInfo)
|
||||
onSelectFile(selectedFileInfo, breadCrumbs);
|
||||
if (!embedded) onCloseAndDeselectAction();
|
||||
}
|
||||
};
|
||||
|
||||
@ -516,120 +254,84 @@ const FilesSelector = ({
|
||||
isRestore,
|
||||
);
|
||||
|
||||
const isDisabled = getIsDisabled(
|
||||
isFirstLoad,
|
||||
isSelectedParentFolder,
|
||||
fromFolderId == selectedItemId,
|
||||
selectedItemType === "rooms",
|
||||
isRoot,
|
||||
isCopy,
|
||||
isMove,
|
||||
isRestoreAll,
|
||||
isRequestRunning,
|
||||
selectedItemSecurity,
|
||||
filterParam,
|
||||
!!selectedFileInfo,
|
||||
includeFolder,
|
||||
isRestore,
|
||||
);
|
||||
const getIsDisabledAction = (
|
||||
isFirstLoad: boolean,
|
||||
isSelectedParentFolder: boolean,
|
||||
selectedItemId: string | number | undefined,
|
||||
selectedItemType: "rooms" | "files" | undefined,
|
||||
isRoot: boolean,
|
||||
selectedItemSecurity:
|
||||
| TFileSecurity
|
||||
| TFolderSecurity
|
||||
| TRoomSecurity
|
||||
| undefined,
|
||||
selectedFileInfo: TSelectedFileInfo,
|
||||
) => {
|
||||
return getIsDisabled(
|
||||
isFirstLoad,
|
||||
isSelectedParentFolder,
|
||||
fromFolderId === selectedItemId,
|
||||
selectedItemType === "rooms",
|
||||
isRoot,
|
||||
isCopy,
|
||||
isMove,
|
||||
isRestoreAll,
|
||||
isRequestRunning,
|
||||
selectedItemSecurity,
|
||||
filterParam,
|
||||
!!selectedFileInfo,
|
||||
includeFolder,
|
||||
isRestore,
|
||||
);
|
||||
};
|
||||
|
||||
const SelectorBody = (
|
||||
<Selector
|
||||
return (
|
||||
<FilesSelector
|
||||
socketHelper={socketHelper}
|
||||
socketSubscribers={socketSubscribers}
|
||||
disabledItems={disabledItems}
|
||||
filterParam={filterParam}
|
||||
getIcon={getIcon}
|
||||
setIsDataReady={setIsDataReady}
|
||||
treeFolders={treeFolders}
|
||||
onSetBaseFolderPath={onSetBaseFolderPath}
|
||||
isUserOnly={isUserOnly}
|
||||
isRoomsOnly={isRoomsOnly}
|
||||
isThirdParty={isThirdParty}
|
||||
rootThirdPartyId={rootThirdPartyId}
|
||||
roomsFolderId={roomsFolderId}
|
||||
currentFolderId={currentFolderId || 0}
|
||||
parentId={parentId}
|
||||
rootFolderType={rootFolderType || FolderType.Rooms}
|
||||
currentDeviceType={currentDeviceType}
|
||||
onCancel={onCloseAction}
|
||||
onSubmit={onAccept}
|
||||
getIsDisabled={getIsDisabledAction}
|
||||
withHeader={withHeader}
|
||||
headerLabel={headerLabel}
|
||||
withoutBackButton
|
||||
searchPlaceholder={t("Common:Search")}
|
||||
searchValue={searchValue}
|
||||
onSearch={onSearchAction}
|
||||
onClearSearch={onClearSearchAction}
|
||||
items={items ? items : []}
|
||||
onSelect={onSelectAction}
|
||||
acceptButtonLabel={acceptButtonLabel}
|
||||
onAccept={onAcceptAction}
|
||||
submitButtonLabel={acceptButtonLabel}
|
||||
withCancelButton={withCancelButton}
|
||||
cancelButtonLabel={t("Common:CancelButton")}
|
||||
onCancel={onCloseAction}
|
||||
emptyScreenImage={
|
||||
theme.isBase ? EmptyScreenAltSvgUrl : EmptyScreenAltSvgDarkUrl
|
||||
}
|
||||
emptyScreenHeader={t("SelectorEmptyScreenHeader")}
|
||||
emptyScreenDescription=""
|
||||
searchEmptyScreenImage={
|
||||
theme.isBase
|
||||
? EmptyScreenFilterAltSvgUrl
|
||||
: EmptyScreenFilterAltDarkSvgUrl
|
||||
}
|
||||
searchEmptyScreenHeader={t("Common:NotFoundTitle")}
|
||||
searchEmptyScreenDescription={t("EmptyFilterDescriptionText")}
|
||||
withBreadCrumbs
|
||||
breadCrumbs={breadCrumbs}
|
||||
onSelectBreadCrumb={onClickBreadCrumb}
|
||||
isLoading={showLoader}
|
||||
isBreadCrumbsLoading={showBreadCrumbsLoader}
|
||||
withSearch={!isRoot && items ? items.length > 0 : !isRoot && isFirstLoad}
|
||||
rowLoader={
|
||||
<RowLoader
|
||||
isMultiSelect={false}
|
||||
isUser={isRoot}
|
||||
isContainer={showLoader}
|
||||
/>
|
||||
}
|
||||
searchLoader={<SearchLoader />}
|
||||
breadCrumbsLoader={<Loaders.SelectorBreadCrumbsLoader />}
|
||||
alwaysShowFooter={true}
|
||||
isNextPageLoading={isNextPageLoading}
|
||||
hasNextPage={hasNextPage}
|
||||
totalItems={total}
|
||||
loadNextPage={
|
||||
isRoot ? null : selectedItemType === "rooms" ? getRoomList : getFileList
|
||||
}
|
||||
disableAcceptButton={isDisabled}
|
||||
withFooterInput={withFooterInput}
|
||||
withFooterCheckbox={withFooterCheckbox}
|
||||
footerInputHeader={footerInputHeader}
|
||||
currentFooterInputValue={currentFooterInputValue}
|
||||
footerCheckboxLabel={footerCheckboxLabel}
|
||||
isPanelVisible={isPanelVisible}
|
||||
embedded={embedded}
|
||||
withFooterInput={withFooterInput || false}
|
||||
withFooterCheckbox={withFooterCheckbox || false}
|
||||
footerInputHeader={footerInputHeader || ""}
|
||||
currentFooterInputValue={currentFooterInputValue || ""}
|
||||
footerCheckboxLabel={footerCheckboxLabel || ""}
|
||||
descriptionText={
|
||||
!filterParam || filterParam === "ALL"
|
||||
? ""
|
||||
: descriptionText ?? t("Common:SelectDOCXFormat")
|
||||
}
|
||||
acceptButtonId={
|
||||
submitButtonId={
|
||||
isMove || isCopy || isRestore ? "select-file-modal-submit" : ""
|
||||
}
|
||||
cancelButtonId={
|
||||
isMove || isCopy || isRestore ? "select-file-modal-cancel" : ""
|
||||
}
|
||||
getFilesArchiveError={getFilesArchiveError}
|
||||
/>
|
||||
);
|
||||
|
||||
const selectorComponent = embedded ? (
|
||||
SelectorBody
|
||||
) : (
|
||||
<>
|
||||
<Backdrop
|
||||
visible={isPanelVisible}
|
||||
isAside
|
||||
withBackground
|
||||
zIndex={309}
|
||||
onClick={onCloseAction}
|
||||
/>
|
||||
<Aside
|
||||
visible={isPanelVisible}
|
||||
withoutBodyScroll
|
||||
zIndex={310}
|
||||
onClose={onCloseAction}
|
||||
>
|
||||
{SelectorBody}
|
||||
</Aside>
|
||||
</>
|
||||
);
|
||||
|
||||
return currentDeviceType === DeviceType.mobile && !embedded ? (
|
||||
<Portal visible={isPanelVisible} element={<div>{selectorComponent}</div>} />
|
||||
) : (
|
||||
selectorComponent
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(
|
||||
@ -643,8 +345,24 @@ export default inject(
|
||||
dialogsStore,
|
||||
filesStore,
|
||||
infoPanelStore,
|
||||
}: any,
|
||||
{ isCopy, isRestoreAll, isMove, isRestore, isPanelVisible, id }: any,
|
||||
}: {
|
||||
settingsStore: SettingsStore;
|
||||
selectedFolderStore: SelectedFolderStore;
|
||||
filesActionsStore: FilesActionStore;
|
||||
uploadDataStore: UploadDataStore;
|
||||
treeFoldersStore: TreeFoldersStore;
|
||||
dialogsStore: DialogsStore;
|
||||
filesStore: FilesStore;
|
||||
infoPanelStore: InfoPanelStore;
|
||||
},
|
||||
{
|
||||
isCopy,
|
||||
isRestoreAll,
|
||||
isMove,
|
||||
isRestore,
|
||||
isPanelVisible,
|
||||
id,
|
||||
}: FilesSelectorProps,
|
||||
) => {
|
||||
const { id: selectedId, parentId, rootFolderType } = selectedFolderStore;
|
||||
|
||||
@ -672,7 +390,7 @@ export default inject(
|
||||
|
||||
const { setIsMobileHidden: setInfoPanelIsMobileHidden } = infoPanelStore;
|
||||
|
||||
const { theme, socketHelper, currentDeviceType } = settingsStore;
|
||||
const { socketHelper, currentDeviceType } = settingsStore;
|
||||
|
||||
const socketSubscribesId = socketHelper.socketSubscribers;
|
||||
|
||||
@ -685,7 +403,7 @@ export default inject(
|
||||
filesSettingsStore,
|
||||
} = filesStore;
|
||||
const { getIcon } = filesSettingsStore;
|
||||
const { isVisible: infoPanelIsVisible, selection: infoPanelSelection } =
|
||||
const { isVisible: infoPanelIsVisible, infoPanelSelection } =
|
||||
infoPanelStore;
|
||||
|
||||
const selections =
|
||||
@ -705,27 +423,27 @@ export default inject(
|
||||
? filesList
|
||||
: isCopy
|
||||
? selections
|
||||
: selections.filter((f: any) => f && !f?.isEditing);
|
||||
: selections.filter((f) => f && !f?.isEditing);
|
||||
|
||||
const disabledItems: any[] = [];
|
||||
const disabledItems: (string | number)[] = [];
|
||||
|
||||
selectionsWithoutEditing.forEach((item: any) => {
|
||||
selectionsWithoutEditing.forEach((item) => {
|
||||
if ((item?.isFolder || item?.parentId) && item?.id) {
|
||||
disabledItems.push(item.id);
|
||||
}
|
||||
});
|
||||
|
||||
const includeFolder =
|
||||
selectionsWithoutEditing.filter((i: any) => i.isFolder).length > 0;
|
||||
selectionsWithoutEditing.filter((i) => i.isFolder).length > 0;
|
||||
|
||||
const fromFolderId = id
|
||||
? id
|
||||
: rootFolderType === FolderType.Archive ||
|
||||
rootFolderType === FolderType.TRASH
|
||||
const fromFolderId =
|
||||
id ||
|
||||
(rootFolderType === FolderType.Archive ||
|
||||
rootFolderType === FolderType.TRASH
|
||||
? undefined
|
||||
: selectedId === selectionsWithoutEditing[0]?.id
|
||||
? parentId
|
||||
: selectedId;
|
||||
: selectedId);
|
||||
|
||||
const currentFolderId =
|
||||
sessionPath && (isMove || isCopy || isRestore || isRestoreAll)
|
||||
@ -738,16 +456,16 @@ export default inject(
|
||||
parentId,
|
||||
rootFolderType,
|
||||
treeFolders,
|
||||
isPanelVisible: isPanelVisible
|
||||
? isPanelVisible
|
||||
: (moveToPanelVisible ||
|
||||
copyPanelVisible ||
|
||||
restorePanelVisible ||
|
||||
restoreAllPanelVisible) &&
|
||||
!conflictResolveDialogVisible,
|
||||
isPanelVisible:
|
||||
isPanelVisible ||
|
||||
((moveToPanelVisible ||
|
||||
copyPanelVisible ||
|
||||
restorePanelVisible ||
|
||||
restoreAllPanelVisible) &&
|
||||
!conflictResolveDialogVisible),
|
||||
setMoveToPanelVisible,
|
||||
setRestorePanelVisible,
|
||||
theme,
|
||||
|
||||
selection: selectionsWithoutEditing,
|
||||
disabledItems,
|
||||
setConflictDialogData,
|
||||
@ -771,4 +489,4 @@ export default inject(
|
||||
roomsFolderId,
|
||||
};
|
||||
},
|
||||
)(observer(FilesSelector));
|
||||
)(observer(FilesSelectorWrapper));
|
||||
|
@ -89,7 +89,10 @@ export async function getReferenceData(data: {
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function getFolderInfo(folderId: number, skipRedirect = false) {
|
||||
export async function getFolderInfo(
|
||||
folderId: number | string,
|
||||
skipRedirect = false,
|
||||
) {
|
||||
const options: AxiosRequestConfig = {
|
||||
method: "get",
|
||||
url: `/files/folder/${folderId}`,
|
||||
@ -114,7 +117,7 @@ export async function getFolderPath(folderId: number) {
|
||||
export async function getFolder(
|
||||
folderId: string | number,
|
||||
filter: FilesFilter,
|
||||
signal: AbortSignal,
|
||||
signal?: AbortSignal,
|
||||
) {
|
||||
let params = folderId;
|
||||
|
||||
@ -163,6 +166,7 @@ export async function getFoldersTree() {
|
||||
const name = getFolderClassNameByType(type);
|
||||
|
||||
return {
|
||||
...current,
|
||||
id,
|
||||
key: `0-${index}`,
|
||||
parentId,
|
||||
@ -175,7 +179,7 @@ export async function getFoldersTree() {
|
||||
filesCount,
|
||||
newItems,
|
||||
security,
|
||||
};
|
||||
} as TFolder;
|
||||
});
|
||||
}
|
||||
|
||||
@ -1328,4 +1332,3 @@ export function deleteFilesFromRecent(fileIds: number[]) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -137,6 +137,7 @@ export type TFolder = {
|
||||
rootFolderType: FolderType;
|
||||
isArchive?: boolean;
|
||||
roomType?: RoomsType;
|
||||
path?: TPathParts[];
|
||||
};
|
||||
|
||||
export type TGetFolderPath = TFolder[];
|
||||
@ -345,4 +346,3 @@ export type TFileLink = {
|
||||
title: string;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -378,7 +378,10 @@ export function getUsersByQuery(query) {
|
||||
});
|
||||
}
|
||||
|
||||
export function getMembersList(roomId, filter = Filter.getDefault()) {
|
||||
export async function getMembersList(
|
||||
roomId: string | number,
|
||||
filter = Filter.getDefault(),
|
||||
) {
|
||||
let params = "";
|
||||
|
||||
if (filter) {
|
||||
@ -397,16 +400,16 @@ export function getMembersList(roomId, filter = Filter.getDefault()) {
|
||||
params = `excludeShared=${excludeShared}`;
|
||||
}
|
||||
|
||||
return request({
|
||||
const res = (await request({
|
||||
method: "get",
|
||||
url: `people/room/${roomId}${params}`,
|
||||
}).then((res) => {
|
||||
res.items = res.items.map((user) => {
|
||||
if (user && user.displayName) {
|
||||
user.displayName = Encoder.htmlDecode(user.displayName);
|
||||
}
|
||||
return user;
|
||||
});
|
||||
return res;
|
||||
})) as { items: TUser[]; total: number };
|
||||
res.items = res.items.map((user) => {
|
||||
if (user && user.displayName) {
|
||||
user.displayName = Encoder.htmlDecode(user.displayName);
|
||||
}
|
||||
return user;
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ export type TRoom = {
|
||||
rootFolderType: FolderType;
|
||||
updatedBy: TCreatedBy;
|
||||
isArchive?: boolean;
|
||||
security: TRoomSecurity;
|
||||
};
|
||||
|
||||
export type TGetRooms = {
|
||||
|
@ -26,7 +26,7 @@ const Selector = ({
|
||||
withHeader,
|
||||
headerProps,
|
||||
|
||||
isBreadCrumbsLoading,
|
||||
isBreadCrumbsLoading = false,
|
||||
breadCrumbsLoader,
|
||||
withBreadCrumbs,
|
||||
breadCrumbs,
|
||||
@ -89,6 +89,7 @@ const Selector = ({
|
||||
loadNextPage,
|
||||
totalItems,
|
||||
isLoading,
|
||||
disableFirstFetch,
|
||||
|
||||
alwaysShowFooter,
|
||||
|
||||
@ -109,55 +110,19 @@ const Selector = ({
|
||||
);
|
||||
const [isFooterCheckboxChecked, setIsFooterCheckboxChecked] =
|
||||
React.useState<boolean>(isChecked || false);
|
||||
|
||||
const [selectedAccess, setSelectedAccess] =
|
||||
React.useState<TAccessRight | null>(null);
|
||||
|
||||
const compareSelectedItems = React.useCallback(
|
||||
(newList: TSelectorItem[]) => {
|
||||
let isEqual = true;
|
||||
|
||||
if (selectedItems?.length !== newList.length) {
|
||||
return setFooterVisible(true);
|
||||
}
|
||||
|
||||
if (newList.length === 0 && selectedItems?.length === 0) {
|
||||
return setFooterVisible(false);
|
||||
}
|
||||
|
||||
newList.forEach((item) => {
|
||||
isEqual = selectedItems.some((x) => x.id === item.id);
|
||||
});
|
||||
|
||||
return setFooterVisible(!isEqual);
|
||||
},
|
||||
[selectedItems],
|
||||
);
|
||||
|
||||
const onSelectAction = (item: TSelectorItem) => {
|
||||
onSelect?.({
|
||||
...item,
|
||||
});
|
||||
|
||||
if (isMultiSelect) {
|
||||
setRenderedItems((value) => {
|
||||
const idx = value.findIndex((x) => item.id === x.id);
|
||||
|
||||
const newValue = value.map((i: TSelectorItem) => ({ ...i }));
|
||||
|
||||
if (idx === -1) return newValue;
|
||||
|
||||
newValue[idx].isSelected = !value[idx].isSelected;
|
||||
|
||||
return newValue;
|
||||
});
|
||||
|
||||
if (item.isSelected) {
|
||||
setNewSelectedItems((value) => {
|
||||
const newValue = value
|
||||
.filter((x) => x.id !== item.id)
|
||||
.map((x) => ({ ...x }));
|
||||
compareSelectedItems(newValue);
|
||||
const newValue = value.filter((x) => x.id !== item.id);
|
||||
|
||||
return newValue;
|
||||
});
|
||||
} else {
|
||||
@ -166,11 +131,18 @@ const Selector = ({
|
||||
...item,
|
||||
});
|
||||
|
||||
compareSelectedItems(value);
|
||||
|
||||
return value;
|
||||
return [...value];
|
||||
});
|
||||
}
|
||||
setRenderedItems((value) => {
|
||||
const idx = value.findIndex((x) => item.id === x.id);
|
||||
|
||||
if (idx === -1) return value;
|
||||
|
||||
value[idx] = { ...value[idx], isSelected: !value[idx].isSelected };
|
||||
|
||||
return value;
|
||||
});
|
||||
} else {
|
||||
setRenderedItems((value) => {
|
||||
const idx = value.findIndex((x) => item.id === x.id);
|
||||
@ -184,19 +156,13 @@ const Selector = ({
|
||||
|
||||
newValue[idx].isSelected = !item.isSelected;
|
||||
|
||||
return newValue;
|
||||
return [...newValue];
|
||||
});
|
||||
|
||||
const newItem = {
|
||||
...item,
|
||||
};
|
||||
|
||||
if (item.isSelected) {
|
||||
setNewSelectedItems([]);
|
||||
compareSelectedItems([]);
|
||||
} else {
|
||||
setNewSelectedItems([newItem]);
|
||||
compareSelectedItems([newItem]);
|
||||
setNewSelectedItems([item]);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -212,22 +178,27 @@ const Selector = ({
|
||||
) {
|
||||
const cloneItems = items.map((x) => ({ ...x }));
|
||||
|
||||
const cloneRenderedItems = items.map((x) => ({ ...x, isSelected: true }));
|
||||
setRenderedItems((i) => {
|
||||
const cloneRenderedItems = i.map((x) => ({
|
||||
...x,
|
||||
isSelected: true,
|
||||
}));
|
||||
|
||||
setRenderedItems(cloneRenderedItems);
|
||||
return cloneRenderedItems;
|
||||
});
|
||||
setNewSelectedItems(cloneItems);
|
||||
compareSelectedItems(cloneItems);
|
||||
} else {
|
||||
const cloneRenderedItems = items.map((x) => ({
|
||||
...x,
|
||||
isSelected: false,
|
||||
}));
|
||||
setRenderedItems((i) => {
|
||||
const cloneRenderedItems = i.map((x) => ({
|
||||
...x,
|
||||
isSelected: false,
|
||||
}));
|
||||
|
||||
setRenderedItems(cloneRenderedItems);
|
||||
return cloneRenderedItems;
|
||||
});
|
||||
setNewSelectedItems([]);
|
||||
compareSelectedItems([]);
|
||||
}
|
||||
}, [compareSelectedItems, items, newSelectedItems.length, onSelectAll]);
|
||||
}, [items, newSelectedItems.length, onSelectAll]);
|
||||
|
||||
const onSubmitAction = () => {
|
||||
onSubmit(
|
||||
@ -248,19 +219,45 @@ const Selector = ({
|
||||
|
||||
const loadMoreItems = React.useCallback(
|
||||
(startIndex: number) => {
|
||||
if (startIndex === 1) return; // fix double fetch of the first page
|
||||
if (!isNextPageLoading) loadNextPage?.(startIndex - 1);
|
||||
if (!isNextPageLoading) loadNextPage(startIndex - 1);
|
||||
},
|
||||
[isNextPageLoading, loadNextPage],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (disableFirstFetch) return;
|
||||
loadNextPage(0);
|
||||
}, [disableFirstFetch, loadNextPage]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (selectedAccessRight) setSelectedAccess({ ...selectedAccessRight });
|
||||
}, [selectedAccessRight]);
|
||||
|
||||
React.useEffect(() => {
|
||||
let isEqual = true;
|
||||
|
||||
if (selectedItems?.length !== newSelectedItems.length) {
|
||||
return setFooterVisible(true);
|
||||
}
|
||||
|
||||
if (newSelectedItems.length === 0 && selectedItems?.length === 0) {
|
||||
return setFooterVisible(false);
|
||||
}
|
||||
|
||||
newSelectedItems.forEach((item) => {
|
||||
isEqual = selectedItems.some((x) => x.id === item.id);
|
||||
});
|
||||
|
||||
setFooterVisible(!isEqual);
|
||||
}, [selectedItems, newSelectedItems]);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
if (items && selectedItems) {
|
||||
if (selectedItems.length === 0 || !isMultiSelect) {
|
||||
if (items) {
|
||||
if (
|
||||
!selectedItems ||
|
||||
(selectedItems && selectedItems.length === 0) ||
|
||||
!isMultiSelect
|
||||
) {
|
||||
const cloneItems = items.map((x) => ({ ...x, isSelected: false }));
|
||||
return setRenderedItems(cloneItems);
|
||||
}
|
||||
@ -279,9 +276,8 @@ const Selector = ({
|
||||
|
||||
setRenderedItems(newItems);
|
||||
setNewSelectedItems(cloneSelectedItems);
|
||||
compareSelectedItems(cloneSelectedItems);
|
||||
}
|
||||
}, [items, selectedItems, isMultiSelect, compareSelectedItems]);
|
||||
}, [items, selectedItems, isMultiSelect]);
|
||||
|
||||
const breadCrumbsProps: TSelectorBreadCrumbs = withBreadCrumbs
|
||||
? {
|
||||
@ -382,7 +378,7 @@ const Selector = ({
|
||||
<Body
|
||||
withHeader={withHeader}
|
||||
footerVisible={footerVisible || !!alwaysShowFooter}
|
||||
items={renderedItems}
|
||||
items={[...renderedItems]}
|
||||
isMultiSelect={isMultiSelect}
|
||||
onSelect={onSelectAction}
|
||||
// empty screen
|
||||
@ -432,20 +428,4 @@ const Selector = ({
|
||||
);
|
||||
};
|
||||
|
||||
Selector.defaultProps = {
|
||||
isMultiSelect: false,
|
||||
withSelectAll: false,
|
||||
withAccessRights: false,
|
||||
withCancelButton: false,
|
||||
withoutBackButton: false,
|
||||
isBreadCrumbsLoading: false,
|
||||
withSearch: true,
|
||||
withFooterInput: false,
|
||||
alwaysShowFooter: false,
|
||||
disableAcceptButton: false,
|
||||
withHeader: true,
|
||||
|
||||
selectedItems: [],
|
||||
};
|
||||
|
||||
export { Selector };
|
||||
|
@ -1,5 +1,7 @@
|
||||
import React from "react";
|
||||
import { RoomsType } from "../../enums";
|
||||
import { TFileSecurity, TFolderSecurity } from "../../api/files/types";
|
||||
import { TRoomSecurity } from "../../api/rooms/types";
|
||||
import { RoomsType, ShareAccessRights } from "../../enums";
|
||||
import { AvatarRole } from "../avatar";
|
||||
|
||||
// header
|
||||
@ -18,7 +20,7 @@ export type HeaderProps = {
|
||||
headerLabel: string;
|
||||
} & (THeaderBackButton | THeaderNonBackButton);
|
||||
|
||||
type TSelectorHeader =
|
||||
export type TSelectorHeader =
|
||||
| {
|
||||
withHeader: true;
|
||||
headerProps: HeaderProps;
|
||||
@ -33,6 +35,7 @@ export type TBreadCrumb = {
|
||||
isRoom?: boolean;
|
||||
minWidth?: string;
|
||||
onClick?: (e: React.MouseEvent, open: boolean, item: TBreadCrumb) => void;
|
||||
roomType?: RoomsType;
|
||||
};
|
||||
|
||||
export interface BreadCrumbsProps {
|
||||
@ -92,7 +95,7 @@ export interface SearchProps {
|
||||
setIsSearch: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
type TSelectorSearch =
|
||||
export type TSelectorSearch =
|
||||
| {
|
||||
withSearch: true;
|
||||
searchLoader: React.ReactNode;
|
||||
@ -141,7 +144,7 @@ type TSelectorEmptyScreen = {
|
||||
};
|
||||
|
||||
// submit button
|
||||
type TSelectorSubmitButton = {
|
||||
export type TSelectorSubmitButton = {
|
||||
submitButtonLabel: string;
|
||||
disableSubmitButton: boolean;
|
||||
onSubmit: (
|
||||
@ -226,7 +229,7 @@ export type TSelectorCheckbox =
|
||||
| {
|
||||
withFooterCheckbox?: undefined;
|
||||
footerCheckboxLabel?: undefined;
|
||||
isChecked: boolean;
|
||||
isChecked?: boolean;
|
||||
setIsChecked?: undefined;
|
||||
};
|
||||
|
||||
@ -259,13 +262,8 @@ export type SelectorProps = TSelectorHeader &
|
||||
selectedAccessRight?: TAccessRight;
|
||||
onAccessRightsChange?: (access: TAccessRight) => void;
|
||||
|
||||
loadNextPage:
|
||||
| ((
|
||||
startIndex: number,
|
||||
search?: string,
|
||||
...rest: unknown[]
|
||||
) => Promise<void>)
|
||||
| null;
|
||||
disableFirstFetch?: boolean;
|
||||
loadNextPage: (startIndex: number) => Promise<void>;
|
||||
hasNextPage: boolean;
|
||||
isNextPageLoading: boolean;
|
||||
totalItems: number;
|
||||
@ -273,7 +271,11 @@ export type SelectorProps = TSelectorHeader &
|
||||
|
||||
rowLoader: React.ReactNode;
|
||||
|
||||
renderCustomItem?: (...args: unknown[]) => React.ReactNode | null;
|
||||
renderCustomItem?: (
|
||||
label: string,
|
||||
role?: AvatarRole,
|
||||
email?: string,
|
||||
) => React.ReactNode | null;
|
||||
|
||||
alwaysShowFooter?: boolean;
|
||||
descriptionText?: string;
|
||||
@ -292,7 +294,11 @@ export type BodyProps = TSelectorBreadCrumbs &
|
||||
isMultiSelect: boolean;
|
||||
|
||||
items: TSelectorItem[];
|
||||
renderCustomItem?: (...args: unknown[]) => React.ReactNode | null;
|
||||
renderCustomItem?: (
|
||||
label: string,
|
||||
role?: AvatarRole,
|
||||
email?: string,
|
||||
) => React.ReactNode | null;
|
||||
onSelect: (item: TSelectorItem) => void;
|
||||
|
||||
loadMoreItems: (startIndex: number) => void;
|
||||
@ -323,9 +329,22 @@ type TSelectorItemLogo =
|
||||
icon?: undefined;
|
||||
avatar: string;
|
||||
role?: AvatarRole;
|
||||
hasAvatar?: boolean;
|
||||
}
|
||||
| { color: string; icon: undefined; avatar?: string; role?: undefined }
|
||||
| { color?: undefined; icon: string; avatar?: undefined; role?: undefined };
|
||||
| {
|
||||
hasAvatar?: undefined;
|
||||
color: string;
|
||||
icon?: undefined;
|
||||
avatar?: undefined;
|
||||
role?: undefined;
|
||||
}
|
||||
| {
|
||||
hasAvatar?: undefined;
|
||||
color?: undefined;
|
||||
icon: string;
|
||||
avatar?: undefined;
|
||||
role?: undefined;
|
||||
};
|
||||
|
||||
type TSelectorItemType =
|
||||
| {
|
||||
@ -333,24 +352,68 @@ type TSelectorItemType =
|
||||
fileExst?: undefined;
|
||||
roomType?: undefined;
|
||||
shared?: undefined;
|
||||
isOwner: boolean;
|
||||
isAdmin: boolean;
|
||||
isVisitor: boolean;
|
||||
isCollaborator: boolean;
|
||||
access: ShareAccessRights | string | number;
|
||||
isFolder?: undefined;
|
||||
parentId?: undefined;
|
||||
rootFolderType?: undefined;
|
||||
filesCount?: undefined;
|
||||
foldersCount?: undefined;
|
||||
security?: undefined;
|
||||
}
|
||||
| {
|
||||
email?: undefined;
|
||||
fileExst: string;
|
||||
roomType?: undefined;
|
||||
shared?: boolean;
|
||||
isOwner?: undefined;
|
||||
isAdmin?: undefined;
|
||||
isVisitor?: undefined;
|
||||
isCollaborator?: undefined;
|
||||
access?: undefined;
|
||||
isFolder?: undefined;
|
||||
parentId?: string | number;
|
||||
rootFolderType?: string | number;
|
||||
filesCount?: undefined;
|
||||
foldersCount?: undefined;
|
||||
security?: TFileSecurity;
|
||||
}
|
||||
| {
|
||||
email?: undefined;
|
||||
fileExst?: undefined;
|
||||
roomType: RoomsType;
|
||||
shared?: boolean;
|
||||
isOwner?: undefined;
|
||||
isAdmin?: undefined;
|
||||
isVisitor?: undefined;
|
||||
isCollaborator?: undefined;
|
||||
access?: undefined;
|
||||
isFolder: boolean;
|
||||
parentId?: string | number;
|
||||
rootFolderType?: string | number;
|
||||
filesCount?: number;
|
||||
foldersCount?: number;
|
||||
security?: TRoomSecurity;
|
||||
}
|
||||
| {
|
||||
email?: undefined;
|
||||
fileExst?: undefined;
|
||||
roomType?: undefined;
|
||||
shared?: boolean;
|
||||
isOwner?: undefined;
|
||||
isAdmin?: undefined;
|
||||
isVisitor?: undefined;
|
||||
isCollaborator?: undefined;
|
||||
access?: undefined;
|
||||
isFolder: boolean;
|
||||
parentId?: string | number;
|
||||
rootFolderType?: string | number;
|
||||
filesCount?: number;
|
||||
foldersCount?: number;
|
||||
security?: TFolderSecurity;
|
||||
};
|
||||
|
||||
export type TSelectorItem = TSelectorItemLogo &
|
||||
@ -358,9 +421,9 @@ export type TSelectorItem = TSelectorItemLogo &
|
||||
key?: string;
|
||||
id?: string | number;
|
||||
label: string;
|
||||
displayName?: string;
|
||||
|
||||
isSelected?: boolean;
|
||||
|
||||
isDisabled?: boolean;
|
||||
};
|
||||
|
||||
@ -370,7 +433,11 @@ export type Data = {
|
||||
isMultiSelect: boolean;
|
||||
isItemLoaded: (index: number) => boolean;
|
||||
rowLoader: React.ReactNode;
|
||||
renderCustomItem?: (...args: unknown[]) => React.ReactNode | null;
|
||||
renderCustomItem?: (
|
||||
label: string,
|
||||
role?: AvatarRole,
|
||||
email?: string,
|
||||
) => React.ReactNode | null;
|
||||
};
|
||||
|
||||
export interface ItemProps {
|
||||
|
@ -12,7 +12,7 @@ const Header = React.memo(
|
||||
({ onBackClick, withoutBackButton, headerLabel }: HeaderProps) => {
|
||||
return (
|
||||
<StyledHeader>
|
||||
{!withoutBackButton && (
|
||||
{!withoutBackButton && typeof withoutBackButton === "boolean" && (
|
||||
<IconButton
|
||||
className="arrow-button"
|
||||
iconName={ArrowPathReactSvgUrl}
|
||||
|
@ -135,3 +135,5 @@ export const RTL_LANGUAGES = Object.freeze([
|
||||
"ur",
|
||||
"yi",
|
||||
]);
|
||||
|
||||
export const HTML_EXST = [".htm", ".mht", ".html"];
|
||||
|
14
packages/shared/selectors/Files/FilesSelector.constants.ts
Normal file
14
packages/shared/selectors/Files/FilesSelector.constants.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { TBreadCrumb } from "../../components/selector/Selector.types";
|
||||
|
||||
export const DEFAULT_BREAD_CRUMB: TBreadCrumb = {
|
||||
label: "DocSpace",
|
||||
id: 0,
|
||||
isRoom: false,
|
||||
};
|
||||
|
||||
export const SHOW_LOADER_TIMER = 500;
|
||||
export const MIN_LOADER_TIMER = 500;
|
||||
|
||||
export const DEFAULT_FILE_EXTS = "file";
|
||||
|
||||
export const PAGE_COUNT = 100;
|
158
packages/shared/selectors/Files/FilesSelector.types.ts
Normal file
158
packages/shared/selectors/Files/FilesSelector.types.ts
Normal file
@ -0,0 +1,158 @@
|
||||
import { TSelectorItem } from "../../components/selector";
|
||||
import { TBreadCrumb } from "../../components/selector/Selector.types";
|
||||
import { TFileSecurity, TFolder, TFolderSecurity } from "../../api/files/types";
|
||||
import SocketIOHelper from "../../utils/socket";
|
||||
import { DeviceType, FolderType } from "../../enums";
|
||||
import { TRoomSecurity } from "../../api/rooms/types";
|
||||
|
||||
export interface UseRootHelperProps {
|
||||
setBreadCrumbs: React.Dispatch<React.SetStateAction<TBreadCrumb[]>>;
|
||||
setIsBreadCrumbsLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setTotal: React.Dispatch<React.SetStateAction<number>>;
|
||||
setItems: React.Dispatch<React.SetStateAction<TSelectorItem[]>>;
|
||||
|
||||
setIsNextPageLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setHasNextPage: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
|
||||
setIsInit: (value: boolean) => void;
|
||||
treeFolders?: TFolder[];
|
||||
isUserOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface UseLoadersHelperProps {
|
||||
items: TSelectorItem[];
|
||||
}
|
||||
|
||||
export type UseSocketHelperProps = {
|
||||
socketHelper: SocketIOHelper;
|
||||
socketSubscribers: Set<string>;
|
||||
setItems: React.Dispatch<React.SetStateAction<TSelectorItem[]>>;
|
||||
setBreadCrumbs: React.Dispatch<React.SetStateAction<TBreadCrumb[]>>;
|
||||
setTotal: React.Dispatch<React.SetStateAction<number>>;
|
||||
disabledItems: (string | number)[];
|
||||
filterParam?: string;
|
||||
getIcon: (fileExst: string) => string;
|
||||
};
|
||||
|
||||
export type UseRoomsHelperProps = {
|
||||
setBreadCrumbs: (items: TBreadCrumb[]) => void;
|
||||
setIsBreadCrumbsLoading: (value: boolean) => void;
|
||||
setIsNextPageLoading: (value: boolean) => void;
|
||||
setHasNextPage: (value: boolean) => void;
|
||||
setTotal: (value: number) => void;
|
||||
setItems: React.Dispatch<React.SetStateAction<TSelectorItem[]>>;
|
||||
isFirstLoad: boolean;
|
||||
setIsRoot: (value: boolean) => void;
|
||||
searchValue?: string;
|
||||
isRoomsOnly: boolean;
|
||||
onSetBaseFolderPath?: (
|
||||
value: number | string | undefined | TBreadCrumb[],
|
||||
) => void;
|
||||
isInit: boolean;
|
||||
setIsInit: (value: boolean) => void;
|
||||
};
|
||||
|
||||
export type UseFilesHelpersProps = {
|
||||
roomsFolderId?: number;
|
||||
setBreadCrumbs: (items: TBreadCrumb[]) => void;
|
||||
setIsBreadCrumbsLoading: (value: boolean) => void;
|
||||
setIsSelectedParentFolder: (value: boolean) => void;
|
||||
setIsNextPageLoading: (value: boolean) => void;
|
||||
setHasNextPage: (value: boolean) => void;
|
||||
setTotal: (value: number) => void;
|
||||
setItems: React.Dispatch<React.SetStateAction<TSelectorItem[]>>;
|
||||
isFirstLoad: boolean;
|
||||
selectedItemId: string | number | undefined;
|
||||
setIsRoot: (value: boolean) => void;
|
||||
setIsInit: (value: boolean) => void;
|
||||
searchValue?: string;
|
||||
disabledItems: string[] | number[];
|
||||
setSelectedItemSecurity: (value: TFileSecurity | TFolderSecurity) => void;
|
||||
isThirdParty: boolean;
|
||||
setSelectedTreeNode: (treeNode: TFolder) => void;
|
||||
filterParam?: string;
|
||||
getRootData?: () => Promise<void>;
|
||||
onSetBaseFolderPath?: (
|
||||
value: number | string | undefined | TBreadCrumb[],
|
||||
) => void;
|
||||
isRoomsOnly: boolean;
|
||||
rootThirdPartyId?: string;
|
||||
getRoomList?: (
|
||||
startIndex: number,
|
||||
search?: string | null,
|
||||
isInit?: boolean,
|
||||
isErrorPath?: boolean,
|
||||
) => Promise<void>;
|
||||
getIcon: (fileExst: string) => string;
|
||||
getFilesArchiveError: (name: string) => string;
|
||||
isInit: boolean;
|
||||
};
|
||||
|
||||
export type TSelectedFileInfo = {
|
||||
id: number | string;
|
||||
title: string;
|
||||
path?: string[] | undefined;
|
||||
fileExst?: string | undefined;
|
||||
inPublic?: boolean | undefined;
|
||||
} | null;
|
||||
|
||||
export interface FilesSelectorProps {
|
||||
socketHelper: SocketIOHelper;
|
||||
socketSubscribers: Set<string>;
|
||||
disabledItems: string[] | number[];
|
||||
filterParam?: string;
|
||||
getIcon?: (size: number, fileExst: string) => string;
|
||||
treeFolders?: TFolder[];
|
||||
onSetBaseFolderPath?: (
|
||||
value: number | string | undefined | TBreadCrumb[],
|
||||
) => void;
|
||||
isUserOnly?: boolean;
|
||||
isRoomsOnly: boolean;
|
||||
isThirdParty: boolean;
|
||||
rootThirdPartyId?: string;
|
||||
roomsFolderId?: number;
|
||||
currentFolderId: number | string;
|
||||
parentId?: number | string;
|
||||
rootFolderType: FolderType;
|
||||
onCancel: () => void;
|
||||
onSubmit: (
|
||||
selectedItemId: string | number | undefined,
|
||||
folderTitle: string,
|
||||
isPublic: boolean,
|
||||
breadCrumbs: TBreadCrumb[],
|
||||
fileName: string,
|
||||
isChecked: boolean,
|
||||
selectedTreeNode: TFolder,
|
||||
selectedFileInfo: TSelectedFileInfo,
|
||||
) => void;
|
||||
getIsDisabled: (
|
||||
isFirstLoad: boolean,
|
||||
isSelectedParentFolder: boolean,
|
||||
selectedItemId: string | number | undefined,
|
||||
selectedItemType: "rooms" | "files" | undefined,
|
||||
isRoot: boolean,
|
||||
selectedItemSecurity:
|
||||
| TFileSecurity
|
||||
| TFolderSecurity
|
||||
| TRoomSecurity
|
||||
| undefined,
|
||||
selectedFileInfo: TSelectedFileInfo,
|
||||
) => boolean;
|
||||
setIsDataReady?: (value: boolean) => void;
|
||||
withHeader: boolean;
|
||||
headerLabel: string;
|
||||
submitButtonLabel: string;
|
||||
withCancelButton: boolean;
|
||||
withFooterInput: boolean;
|
||||
withFooterCheckbox: boolean;
|
||||
footerInputHeader: string;
|
||||
currentFooterInputValue: string;
|
||||
footerCheckboxLabel: string;
|
||||
descriptionText: string;
|
||||
submitButtonId?: string;
|
||||
cancelButtonId?: string;
|
||||
embedded?: boolean;
|
||||
isPanelVisible: boolean;
|
||||
currentDeviceType: DeviceType;
|
||||
getFilesArchiveError: (name: string) => string;
|
||||
}
|
118
packages/shared/selectors/Files/FilesSelector.utils.ts
Normal file
118
packages/shared/selectors/Files/FilesSelector.utils.ts
Normal file
@ -0,0 +1,118 @@
|
||||
import { TSelectorItem } from "../../components/selector";
|
||||
import { TFile, TFolder } from "../../api/files/types";
|
||||
import { TRoom } from "../../api/rooms/types";
|
||||
import { getIconPathByFolderType } from "../../utils/common";
|
||||
import { iconSize32 } from "../../utils/image-helpers";
|
||||
|
||||
import { DEFAULT_FILE_EXTS } from "./FilesSelector.constants";
|
||||
|
||||
export const convertFoldersToItems: (
|
||||
folders: TFolder[],
|
||||
disabledItems: (number | string)[],
|
||||
filterParam?: string,
|
||||
) => TSelectorItem[] = (
|
||||
folders: TFolder[],
|
||||
disabledItems: (number | string)[],
|
||||
filterParam?: string,
|
||||
) => {
|
||||
const items = folders.map((folder: TFolder) => {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
// roomType,
|
||||
filesCount,
|
||||
foldersCount,
|
||||
security,
|
||||
parentId,
|
||||
rootFolderType,
|
||||
} = folder;
|
||||
|
||||
const folderIconPath = getIconPathByFolderType(rootFolderType);
|
||||
const icon = iconSize32.get(folderIconPath) as string;
|
||||
|
||||
return {
|
||||
id,
|
||||
label: title,
|
||||
title,
|
||||
icon,
|
||||
filesCount,
|
||||
foldersCount,
|
||||
security,
|
||||
parentId,
|
||||
rootFolderType,
|
||||
isFolder: true,
|
||||
// roomType,
|
||||
isDisabled: filterParam ? false : disabledItems?.includes(id),
|
||||
};
|
||||
});
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
export const convertFilesToItems: (
|
||||
files: TFile[],
|
||||
getIcon: (fileExst: string) => string,
|
||||
filterParam?: string,
|
||||
) => TSelectorItem[] = (
|
||||
files: TFile[],
|
||||
getIcon: (fileExst: string) => string,
|
||||
filterParam?: string,
|
||||
) => {
|
||||
const items = files.map((file) => {
|
||||
const { id, title, security, folderId, rootFolderType, fileExst } = file;
|
||||
|
||||
const icon = getIcon(fileExst || DEFAULT_FILE_EXTS);
|
||||
const label = title.replace(fileExst, "") || fileExst;
|
||||
|
||||
return {
|
||||
id,
|
||||
label,
|
||||
title,
|
||||
icon,
|
||||
security,
|
||||
parentId: folderId,
|
||||
rootFolderType,
|
||||
isDisabled: !filterParam,
|
||||
fileExst,
|
||||
};
|
||||
});
|
||||
return items;
|
||||
};
|
||||
|
||||
export const convertRoomsToItems: (rooms: TRoom[]) => TSelectorItem[] = (
|
||||
rooms: TRoom[],
|
||||
) => {
|
||||
const items = rooms.map((room) => {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
roomType,
|
||||
logo,
|
||||
filesCount,
|
||||
foldersCount,
|
||||
security,
|
||||
parentId,
|
||||
rootFolderType,
|
||||
} = room;
|
||||
|
||||
const icon = logo.medium || "";
|
||||
|
||||
const iconProp = icon ? { icon } : { color: logo.color as string };
|
||||
|
||||
return {
|
||||
id,
|
||||
label: title,
|
||||
title,
|
||||
filesCount,
|
||||
foldersCount,
|
||||
security,
|
||||
parentId,
|
||||
rootFolderType,
|
||||
isFolder: true,
|
||||
roomType,
|
||||
...iconProp,
|
||||
};
|
||||
});
|
||||
|
||||
return items;
|
||||
};
|
304
packages/shared/selectors/Files/hooks/useFilesHelper.ts
Normal file
304
packages/shared/selectors/Files/hooks/useFilesHelper.ts
Normal file
@ -0,0 +1,304 @@
|
||||
import React from "react";
|
||||
|
||||
import { getFolder, getFolderInfo } from "../../../api/files";
|
||||
import FilesFilter from "../../../api/files/filter";
|
||||
import {
|
||||
ApplyFilterOption,
|
||||
FilesSelectorFilterTypes,
|
||||
FilterType,
|
||||
FolderType,
|
||||
} from "../../../enums";
|
||||
import { toastr } from "../../../components/toast";
|
||||
import { TSelectorItem } from "../../../components/selector";
|
||||
import { TData } from "../../../components/toast/Toast.type";
|
||||
import { TBreadCrumb } from "../../../components/selector/Selector.types";
|
||||
|
||||
import { PAGE_COUNT, DEFAULT_BREAD_CRUMB } from "../FilesSelector.constants";
|
||||
import { UseFilesHelpersProps } from "../FilesSelector.types";
|
||||
import {
|
||||
convertFilesToItems,
|
||||
convertFoldersToItems,
|
||||
} from "../FilesSelector.utils";
|
||||
|
||||
const useFilesHelper = ({
|
||||
setIsNextPageLoading,
|
||||
setHasNextPage,
|
||||
setTotal,
|
||||
setItems,
|
||||
setBreadCrumbs,
|
||||
setIsBreadCrumbsLoading,
|
||||
isFirstLoad,
|
||||
selectedItemId,
|
||||
setIsRoot,
|
||||
searchValue,
|
||||
disabledItems,
|
||||
setSelectedItemSecurity,
|
||||
isThirdParty,
|
||||
setSelectedTreeNode,
|
||||
filterParam,
|
||||
getRootData,
|
||||
onSetBaseFolderPath,
|
||||
isRoomsOnly,
|
||||
rootThirdPartyId,
|
||||
getRoomList,
|
||||
getIcon,
|
||||
setIsSelectedParentFolder,
|
||||
roomsFolderId,
|
||||
getFilesArchiveError,
|
||||
isInit,
|
||||
setIsInit,
|
||||
}: UseFilesHelpersProps) => {
|
||||
const requestRunning = React.useRef(false);
|
||||
const initRef = React.useRef(isInit);
|
||||
const firstLoadRef = React.useRef(isFirstLoad);
|
||||
const disabledItemsRef = React.useRef(disabledItems);
|
||||
|
||||
React.useEffect(() => {
|
||||
disabledItemsRef.current = disabledItems;
|
||||
}, [disabledItems]);
|
||||
|
||||
React.useEffect(() => {
|
||||
firstLoadRef.current = isFirstLoad;
|
||||
}, [isFirstLoad]);
|
||||
|
||||
React.useEffect(() => {
|
||||
initRef.current = isInit;
|
||||
}, [isInit]);
|
||||
|
||||
const getFileList = React.useCallback(
|
||||
async (startIndex: number) => {
|
||||
if (requestRunning.current) return;
|
||||
|
||||
requestRunning.current = true;
|
||||
setIsNextPageLoading(true);
|
||||
|
||||
const currentSearch = searchValue || "";
|
||||
|
||||
const page = startIndex / PAGE_COUNT;
|
||||
|
||||
const filter = FilesFilter.getDefault();
|
||||
|
||||
filter.page = page;
|
||||
filter.pageCount = PAGE_COUNT;
|
||||
filter.search = currentSearch;
|
||||
filter.applyFilterOption = null;
|
||||
filter.withSubfolders = false;
|
||||
if (filterParam) {
|
||||
filter.applyFilterOption = ApplyFilterOption.Files;
|
||||
switch (filterParam) {
|
||||
case FilesSelectorFilterTypes.DOCX:
|
||||
filter.extension = FilesSelectorFilterTypes.DOCX;
|
||||
break;
|
||||
|
||||
case FilesSelectorFilterTypes.IMG:
|
||||
filter.filterType = FilterType.ImagesOnly;
|
||||
break;
|
||||
|
||||
case FilesSelectorFilterTypes.BackupOnly:
|
||||
filter.extension = "gz,tar";
|
||||
break;
|
||||
|
||||
case FilesSelectorFilterTypes.DOCXF:
|
||||
filter.filterType = FilterType.OFormTemplateOnly;
|
||||
break;
|
||||
|
||||
case FilesSelectorFilterTypes.XLSX:
|
||||
filter.filterType = FilterType.SpreadsheetsOnly;
|
||||
break;
|
||||
|
||||
case FilesSelectorFilterTypes.ALL:
|
||||
filter.filterType = FilterType.FilesOnly;
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
const id = selectedItemId || "";
|
||||
|
||||
filter.folder = id.toString();
|
||||
|
||||
const setSettings = async (
|
||||
folderId: string | number,
|
||||
isErrorPath = false,
|
||||
) => {
|
||||
if (initRef.current && getRootData) {
|
||||
const folder = await getFolderInfo(folderId, true);
|
||||
|
||||
const isArchive = folder.rootFolderType === FolderType.Archive;
|
||||
|
||||
if (folder.rootFolderType === FolderType.TRASH || isArchive) {
|
||||
if (isRoomsOnly && getRoomList) {
|
||||
await getRoomList(0);
|
||||
onSetBaseFolderPath?.([]);
|
||||
const error = getFilesArchiveError(folder.title);
|
||||
toastr.error(error);
|
||||
|
||||
requestRunning.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
await getRootData();
|
||||
|
||||
if (onSetBaseFolderPath && isArchive) {
|
||||
onSetBaseFolderPath?.([]);
|
||||
const error = getFilesArchiveError(folder.title);
|
||||
toastr.error(error);
|
||||
}
|
||||
requestRunning.current = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const currentFolder = await getFolder(folderId, filter);
|
||||
|
||||
const { folders, files, total, count, pathParts, current } =
|
||||
currentFolder;
|
||||
|
||||
setSelectedItemSecurity(current.security);
|
||||
|
||||
const foldersList: TSelectorItem[] = convertFoldersToItems(
|
||||
folders,
|
||||
disabledItemsRef.current,
|
||||
filterParam,
|
||||
);
|
||||
|
||||
const filesList: TSelectorItem[] = convertFilesToItems(
|
||||
files,
|
||||
getIcon,
|
||||
filterParam,
|
||||
);
|
||||
|
||||
const itemList = [...foldersList, ...filesList];
|
||||
|
||||
setHasNextPage(count === PAGE_COUNT);
|
||||
|
||||
setSelectedTreeNode?.({ ...current, path: pathParts });
|
||||
|
||||
if (initRef.current) {
|
||||
let foundParentId = false;
|
||||
let currentFolderIndex = -1;
|
||||
|
||||
const breadCrumbs: TBreadCrumb[] = pathParts.map(
|
||||
(
|
||||
{
|
||||
id: breadCrumbId,
|
||||
title,
|
||||
roomType,
|
||||
}: {
|
||||
id: number | string;
|
||||
title: string;
|
||||
roomType?: number;
|
||||
},
|
||||
index,
|
||||
) => {
|
||||
if (!foundParentId && disabledItemsRef.current) {
|
||||
currentFolderIndex = disabledItemsRef.current.findIndex(
|
||||
(x) => x === id,
|
||||
);
|
||||
}
|
||||
|
||||
if (!foundParentId && currentFolderIndex !== -1) {
|
||||
foundParentId = true;
|
||||
setIsSelectedParentFolder(true);
|
||||
}
|
||||
|
||||
return {
|
||||
label: title,
|
||||
id: breadCrumbId,
|
||||
isRoom: roomsFolderId === id || index === 0,
|
||||
roomType,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
breadCrumbs.forEach((item, idx) => {
|
||||
if (item.roomType) breadCrumbs[idx].isRoom = true;
|
||||
});
|
||||
|
||||
if (!isThirdParty && !isRoomsOnly)
|
||||
breadCrumbs.unshift({ ...DEFAULT_BREAD_CRUMB });
|
||||
|
||||
onSetBaseFolderPath?.(isErrorPath ? [] : breadCrumbs);
|
||||
|
||||
setBreadCrumbs(breadCrumbs);
|
||||
setIsBreadCrumbsLoading(false);
|
||||
}
|
||||
|
||||
if (firstLoadRef.current || startIndex === 0) {
|
||||
setTotal(total);
|
||||
setItems(itemList);
|
||||
} else {
|
||||
setItems((prevState) => {
|
||||
if (prevState) return [...prevState, ...itemList];
|
||||
return [...itemList];
|
||||
});
|
||||
}
|
||||
setIsRoot(false);
|
||||
setIsInit(false);
|
||||
setIsNextPageLoading(false);
|
||||
};
|
||||
|
||||
try {
|
||||
await setSettings(id);
|
||||
|
||||
requestRunning.current = false;
|
||||
} catch (e) {
|
||||
sessionStorage.removeItem("filesSelectorPath");
|
||||
if (isThirdParty && rootThirdPartyId) {
|
||||
await setSettings(rootThirdPartyId, true);
|
||||
|
||||
toastr.error(e as TData);
|
||||
requestRunning.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRoomsOnly && getRoomList) {
|
||||
await getRoomList(0, null, true, true);
|
||||
|
||||
toastr.error(e as TData);
|
||||
requestRunning.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
requestRunning.current = false;
|
||||
|
||||
getRootData?.();
|
||||
|
||||
if (onSetBaseFolderPath) {
|
||||
onSetBaseFolderPath([]);
|
||||
toastr.error(e as TData);
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
setIsNextPageLoading,
|
||||
searchValue,
|
||||
filterParam,
|
||||
selectedItemId,
|
||||
getRootData,
|
||||
setSelectedItemSecurity,
|
||||
getIcon,
|
||||
setHasNextPage,
|
||||
setSelectedTreeNode,
|
||||
setIsRoot,
|
||||
setIsInit,
|
||||
isRoomsOnly,
|
||||
getRoomList,
|
||||
onSetBaseFolderPath,
|
||||
getFilesArchiveError,
|
||||
isThirdParty,
|
||||
setBreadCrumbs,
|
||||
setIsBreadCrumbsLoading,
|
||||
roomsFolderId,
|
||||
setIsSelectedParentFolder,
|
||||
setTotal,
|
||||
setItems,
|
||||
rootThirdPartyId,
|
||||
],
|
||||
);
|
||||
|
||||
return { getFileList };
|
||||
};
|
||||
|
||||
export default useFilesHelper;
|
253
packages/shared/selectors/Files/hooks/useFilesSettings.ts
Normal file
253
packages/shared/selectors/Files/hooks/useFilesSettings.ts
Normal file
@ -0,0 +1,253 @@
|
||||
import React from "react";
|
||||
import { TFilesSettings } from "../../../api/files/types";
|
||||
import { getSettingsFiles } from "../../../api/files";
|
||||
import { presentInArray } from "../../../utils";
|
||||
import { iconSize32 } from "../../../utils/image-helpers";
|
||||
import { HTML_EXST } from "../../../constants";
|
||||
|
||||
const useFilesSettings = (
|
||||
getIconProp?: (size: number, fileExst: string) => string,
|
||||
) => {
|
||||
const [settings, setSettings] = React.useState({} as TFilesSettings);
|
||||
|
||||
const requestRunning = React.useRef(false);
|
||||
|
||||
const initSettings = React.useCallback(async () => {
|
||||
if (requestRunning.current) return;
|
||||
|
||||
requestRunning.current = true;
|
||||
if (getIconProp) return;
|
||||
const res = await getSettingsFiles();
|
||||
|
||||
setSettings(res);
|
||||
requestRunning.current = false;
|
||||
}, [getIconProp]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (settings.extsArchive) return;
|
||||
initSettings();
|
||||
}, [initSettings, settings.extsArchive]);
|
||||
|
||||
const isArchive = React.useCallback(
|
||||
(extension: string) => presentInArray(settings.extsArchive, extension),
|
||||
[settings.extsArchive],
|
||||
);
|
||||
|
||||
const isImage = React.useCallback(
|
||||
(extension: string) => presentInArray(settings.extsImage, extension),
|
||||
[settings.extsImage],
|
||||
);
|
||||
|
||||
const isSound = React.useCallback(
|
||||
(extension: string) => presentInArray(settings.extsAudio, extension),
|
||||
[settings.extsAudio],
|
||||
);
|
||||
|
||||
const isHtml = React.useCallback(
|
||||
(extension: string) => presentInArray(HTML_EXST, extension),
|
||||
[],
|
||||
);
|
||||
|
||||
const getIcon = React.useCallback(
|
||||
(fileExst: string) => {
|
||||
if (getIconProp) return getIconProp(32, fileExst);
|
||||
|
||||
const isArchiveItem = isArchive(fileExst);
|
||||
const isImageItem = isImage(fileExst);
|
||||
const isSoundItem = isSound(fileExst);
|
||||
const isHtmlItem = isHtml(fileExst);
|
||||
|
||||
let path = "";
|
||||
|
||||
if (isArchiveItem) path = "file_archive.svg";
|
||||
|
||||
if (isImageItem) path = "image.svg";
|
||||
|
||||
if (isSoundItem) path = "sound.svg";
|
||||
|
||||
if (isHtmlItem) path = "html.svg";
|
||||
|
||||
if (path) return iconSize32.get(path) ?? "";
|
||||
|
||||
switch (fileExst) {
|
||||
case ".avi":
|
||||
path = "avi.svg";
|
||||
break;
|
||||
case ".csv":
|
||||
path = "csv.svg";
|
||||
break;
|
||||
case ".djvu":
|
||||
path = "djvu.svg";
|
||||
break;
|
||||
case ".doc":
|
||||
path = "doc.svg";
|
||||
break;
|
||||
case ".docm":
|
||||
path = "docm.svg";
|
||||
break;
|
||||
case ".docx":
|
||||
path = "docx.svg";
|
||||
break;
|
||||
case ".dotx":
|
||||
path = "dotx.svg";
|
||||
break;
|
||||
case ".dvd":
|
||||
path = "dvd.svg";
|
||||
break;
|
||||
case ".epub":
|
||||
path = "epub.svg";
|
||||
break;
|
||||
case ".pb2":
|
||||
case ".fb2":
|
||||
path = "fb2.svg";
|
||||
break;
|
||||
case ".flv":
|
||||
path = "flv.svg";
|
||||
break;
|
||||
case ".fodt":
|
||||
path = "fodt.svg";
|
||||
break;
|
||||
case ".iaf":
|
||||
path = "iaf.svg";
|
||||
break;
|
||||
case ".ics":
|
||||
path = "ics.svg";
|
||||
break;
|
||||
case ".m2ts":
|
||||
path = "m2ts.svg";
|
||||
break;
|
||||
case ".mht":
|
||||
path = "mht.svg";
|
||||
break;
|
||||
case ".mkv":
|
||||
path = "mkv.svg";
|
||||
break;
|
||||
case ".mov":
|
||||
path = "mov.svg";
|
||||
break;
|
||||
case ".mp4":
|
||||
path = "mp4.svg";
|
||||
break;
|
||||
case ".mpg":
|
||||
path = "mpg.svg";
|
||||
break;
|
||||
case ".odp":
|
||||
path = "odp.svg";
|
||||
break;
|
||||
case ".ods":
|
||||
path = "ods.svg";
|
||||
break;
|
||||
case ".odt":
|
||||
path = "odt.svg";
|
||||
break;
|
||||
case ".otp":
|
||||
path = "otp.svg";
|
||||
break;
|
||||
case ".ots":
|
||||
path = "ots.svg";
|
||||
break;
|
||||
case ".ott":
|
||||
path = "ott.svg";
|
||||
break;
|
||||
case ".pdf":
|
||||
path = "pdf.svg";
|
||||
break;
|
||||
case ".pot":
|
||||
path = "pot.svg";
|
||||
break;
|
||||
case ".pps":
|
||||
path = "pps.svg";
|
||||
break;
|
||||
case ".ppsx":
|
||||
path = "ppsx.svg";
|
||||
break;
|
||||
case ".ppt":
|
||||
path = "ppt.svg";
|
||||
break;
|
||||
case ".pptm":
|
||||
path = "pptm.svg";
|
||||
break;
|
||||
case ".pptx":
|
||||
path = "pptx.svg";
|
||||
break;
|
||||
case ".rtf":
|
||||
path = "rtf.svg";
|
||||
break;
|
||||
case ".svg":
|
||||
path = "svg.svg";
|
||||
break;
|
||||
case ".txt":
|
||||
path = "txt.svg";
|
||||
break;
|
||||
case ".webm":
|
||||
path = "webm.svg";
|
||||
break;
|
||||
case ".xls":
|
||||
path = "xls.svg";
|
||||
break;
|
||||
case ".xlsm":
|
||||
path = "xlsm.svg";
|
||||
break;
|
||||
case ".xlsx":
|
||||
path = "xlsx.svg";
|
||||
break;
|
||||
case ".xps":
|
||||
path = "xps.svg";
|
||||
break;
|
||||
case ".xml":
|
||||
path = "xml.svg";
|
||||
break;
|
||||
case ".oform":
|
||||
path = "oform.svg";
|
||||
break;
|
||||
case ".docxf":
|
||||
path = "docxf.svg";
|
||||
break;
|
||||
case ".sxc":
|
||||
path = "sxc.svg";
|
||||
break;
|
||||
case ".et":
|
||||
path = "et.svg";
|
||||
break;
|
||||
case ".ett":
|
||||
path = "ett.svg";
|
||||
break;
|
||||
case ".sxw":
|
||||
path = "sxw.svg";
|
||||
break;
|
||||
case ".stw":
|
||||
path = "stw.svg";
|
||||
break;
|
||||
case ".wps":
|
||||
path = "wps.svg";
|
||||
break;
|
||||
case ".wpt":
|
||||
path = "wpt.svg";
|
||||
break;
|
||||
case ".mhtml":
|
||||
path = "mhtml.svg";
|
||||
break;
|
||||
case ".dps":
|
||||
path = "dps.svg";
|
||||
break;
|
||||
case ".dpt":
|
||||
path = "dpt.svg";
|
||||
break;
|
||||
case ".sxi":
|
||||
path = "sxi.svg";
|
||||
break;
|
||||
default:
|
||||
path = "file.svg";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return iconSize32.get(path) ?? "";
|
||||
},
|
||||
[getIconProp, isArchive, isHtml, isImage, isSound],
|
||||
);
|
||||
|
||||
return { getIcon };
|
||||
};
|
||||
|
||||
export default useFilesSettings;
|
@ -1,20 +1,23 @@
|
||||
import React from "react";
|
||||
|
||||
import { useLoadersHelperProps } from "../FilesSelector.types";
|
||||
import { MIN_LOADER_TIMER, SHOW_LOADER_TIMER } from "../utils";
|
||||
import { UseLoadersHelperProps } from "../FilesSelector.types";
|
||||
import {
|
||||
MIN_LOADER_TIMER,
|
||||
SHOW_LOADER_TIMER,
|
||||
} from "../FilesSelector.constants";
|
||||
|
||||
const useLoadersHelper = ({ items }: useLoadersHelperProps) => {
|
||||
const useLoadersHelper = ({ items }: UseLoadersHelperProps) => {
|
||||
const [isBreadCrumbsLoading, setIsBreadCrumbsLoading] =
|
||||
React.useState<boolean>(true);
|
||||
const [isNextPageLoading, setIsNextPageLoading] =
|
||||
React.useState<boolean>(false);
|
||||
const [isFirstLoad, setIsFirstLoad] = React.useState<boolean>(true);
|
||||
|
||||
const [showBreadCrumbsLoader, setShowBreadCrumbsLoader] =
|
||||
React.useState<boolean>(true);
|
||||
const [showLoader, setShowLoader] = React.useState<boolean>(true);
|
||||
|
||||
const loaderTimeout = React.useRef<NodeJS.Timeout | null>(null);
|
||||
const [isFirstLoad, setIsFirstLoad] = React.useState(true);
|
||||
|
||||
const startLoader = React.useRef<Date | null>(new Date());
|
||||
|
||||
const breadCrumbsLoaderTimeout = React.useRef<NodeJS.Timeout | null>(null);
|
||||
@ -23,6 +26,7 @@ const useLoadersHelper = ({ items }: useLoadersHelperProps) => {
|
||||
const isMount = React.useRef<boolean>(true);
|
||||
|
||||
React.useEffect(() => {
|
||||
isMount.current = true;
|
||||
return () => {
|
||||
isMount.current = false;
|
||||
};
|
||||
@ -33,26 +37,24 @@ const useLoadersHelper = ({ items }: useLoadersHelperProps) => {
|
||||
setShowLoader(true);
|
||||
|
||||
startLoader.current = new Date();
|
||||
} else {
|
||||
if (startLoader.current) {
|
||||
const currentDate = new Date();
|
||||
} else if (startLoader.current) {
|
||||
const currentDate = new Date();
|
||||
|
||||
const ms = Math.abs(
|
||||
startLoader.current.getTime() - currentDate.getTime()
|
||||
);
|
||||
const ms = Math.abs(
|
||||
startLoader.current.getTime() - currentDate.getTime(),
|
||||
);
|
||||
|
||||
if (ms >= MIN_LOADER_TIMER) {
|
||||
startLoader.current = null;
|
||||
return setShowLoader(false);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (isMount.current) {
|
||||
startLoader.current = null;
|
||||
setShowLoader(false);
|
||||
}
|
||||
}, MIN_LOADER_TIMER - ms);
|
||||
if (ms >= MIN_LOADER_TIMER) {
|
||||
startLoader.current = null;
|
||||
return setShowLoader(false);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (isMount.current) {
|
||||
startLoader.current = null;
|
||||
setShowLoader(false);
|
||||
}
|
||||
}, MIN_LOADER_TIMER - ms);
|
||||
}
|
||||
}, [isFirstLoad]);
|
||||
|
||||
@ -63,7 +65,7 @@ const useLoadersHelper = ({ items }: useLoadersHelperProps) => {
|
||||
}
|
||||
breadCrumbsStartLoader.current = new Date();
|
||||
breadCrumbsLoaderTimeout.current = setTimeout(() => {
|
||||
isMount.current && setShowBreadCrumbsLoader(true);
|
||||
if (isMount.current) setShowBreadCrumbsLoader(true);
|
||||
}, SHOW_LOADER_TIMER);
|
||||
} else {
|
||||
if (breadCrumbsLoaderTimeout.current) {
|
||||
@ -77,7 +79,7 @@ const useLoadersHelper = ({ items }: useLoadersHelperProps) => {
|
||||
const currentDate = new Date();
|
||||
|
||||
const ms = Math.abs(
|
||||
breadCrumbsStartLoader.current.getTime() - currentDate.getTime()
|
||||
breadCrumbsStartLoader.current.getTime() - currentDate.getTime(),
|
||||
);
|
||||
|
||||
if (ms >= MIN_LOADER_TIMER) {
|
||||
@ -96,14 +98,14 @@ const useLoadersHelper = ({ items }: useLoadersHelperProps) => {
|
||||
}, [isBreadCrumbsLoading]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isFirstLoad && items) {
|
||||
if (items.length && isFirstLoad) {
|
||||
setIsFirstLoad(false);
|
||||
}
|
||||
}, [isFirstLoad, items]);
|
||||
|
||||
React.useEffect(() => {
|
||||
calculateLoader();
|
||||
}, [isFirstLoad, calculateLoader]);
|
||||
}, [calculateLoader]);
|
||||
|
||||
React.useEffect(() => {
|
||||
calculateBreadCrumbsLoader();
|
||||
@ -114,8 +116,10 @@ const useLoadersHelper = ({ items }: useLoadersHelperProps) => {
|
||||
setIsBreadCrumbsLoading,
|
||||
isNextPageLoading,
|
||||
setIsNextPageLoading,
|
||||
|
||||
isFirstLoad,
|
||||
setIsFirstLoad,
|
||||
|
||||
showBreadCrumbsLoader,
|
||||
showLoader,
|
||||
};
|
110
packages/shared/selectors/Files/hooks/useRoomsHelper.ts
Normal file
110
packages/shared/selectors/Files/hooks/useRoomsHelper.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import React from "react";
|
||||
|
||||
import { getRooms } from "../../../api/rooms";
|
||||
import RoomsFilter from "../../../api/rooms/filter";
|
||||
import { TSelectorItem } from "../../../components/selector";
|
||||
import { TBreadCrumb } from "../../../components/selector/Selector.types";
|
||||
|
||||
import { PAGE_COUNT, DEFAULT_BREAD_CRUMB } from "../FilesSelector.constants";
|
||||
import { UseRoomsHelperProps } from "../FilesSelector.types";
|
||||
import { convertRoomsToItems } from "../FilesSelector.utils";
|
||||
|
||||
const useRoomsHelper = ({
|
||||
setIsNextPageLoading,
|
||||
setHasNextPage,
|
||||
setTotal,
|
||||
setItems,
|
||||
setBreadCrumbs,
|
||||
setIsRoot,
|
||||
onSetBaseFolderPath,
|
||||
setIsBreadCrumbsLoading,
|
||||
searchValue,
|
||||
isRoomsOnly,
|
||||
isFirstLoad,
|
||||
isInit,
|
||||
setIsInit,
|
||||
}: UseRoomsHelperProps) => {
|
||||
const requestRunning = React.useRef(false);
|
||||
const initRef = React.useRef(isInit);
|
||||
const firstLoadRef = React.useRef(isFirstLoad);
|
||||
|
||||
React.useEffect(() => {
|
||||
firstLoadRef.current = isFirstLoad;
|
||||
}, [isFirstLoad]);
|
||||
|
||||
React.useEffect(() => {
|
||||
initRef.current = isInit;
|
||||
}, [isInit]);
|
||||
|
||||
const getRoomList = React.useCallback(
|
||||
async (startIndex: number) => {
|
||||
if (requestRunning.current) return;
|
||||
|
||||
requestRunning.current = true;
|
||||
setIsNextPageLoading(true);
|
||||
|
||||
const filterValue = searchValue || "";
|
||||
|
||||
const page = startIndex / PAGE_COUNT;
|
||||
|
||||
const filter = RoomsFilter.getDefault();
|
||||
|
||||
filter.page = page;
|
||||
filter.pageCount = PAGE_COUNT;
|
||||
|
||||
filter.filterValue = filterValue;
|
||||
|
||||
const rooms = await getRooms(filter);
|
||||
|
||||
const { folders, total, count, current } = rooms;
|
||||
|
||||
if (initRef.current) {
|
||||
const { title, id } = current;
|
||||
|
||||
const breadCrumbs: TBreadCrumb[] = [{ label: title, id, isRoom: true }];
|
||||
|
||||
if (!isRoomsOnly) breadCrumbs.unshift({ ...DEFAULT_BREAD_CRUMB });
|
||||
|
||||
onSetBaseFolderPath?.(breadCrumbs);
|
||||
|
||||
setBreadCrumbs(breadCrumbs);
|
||||
|
||||
setIsBreadCrumbsLoading(false);
|
||||
}
|
||||
const itemList: TSelectorItem[] = convertRoomsToItems(folders);
|
||||
|
||||
setHasNextPage(count === PAGE_COUNT);
|
||||
|
||||
if (firstLoadRef.current || startIndex === 0) {
|
||||
setTotal(total);
|
||||
setItems(itemList);
|
||||
} else {
|
||||
setItems((prevState) => {
|
||||
if (prevState) return [...prevState, ...itemList];
|
||||
return [...itemList];
|
||||
});
|
||||
}
|
||||
|
||||
requestRunning.current = false;
|
||||
setIsNextPageLoading(false);
|
||||
setIsRoot(false);
|
||||
setIsInit(false);
|
||||
},
|
||||
[
|
||||
setIsNextPageLoading,
|
||||
searchValue,
|
||||
setHasNextPage,
|
||||
setIsRoot,
|
||||
setIsInit,
|
||||
isRoomsOnly,
|
||||
onSetBaseFolderPath,
|
||||
setBreadCrumbs,
|
||||
setIsBreadCrumbsLoading,
|
||||
setTotal,
|
||||
setItems,
|
||||
],
|
||||
);
|
||||
return { getRoomList };
|
||||
};
|
||||
|
||||
export default useRoomsHelper;
|
@ -1,16 +1,13 @@
|
||||
import React from "react";
|
||||
|
||||
import { FolderType } from "@docspace/shared/enums";
|
||||
// @ts-ignore
|
||||
import { getFoldersTree } from "@docspace/shared/api/files";
|
||||
import { FolderType } from "../../../enums";
|
||||
import { getFoldersTree } from "../../../api/files";
|
||||
import { TFolder } from "../../../api/files/types";
|
||||
import { getCatalogIconUrlByType } from "../../../utils/catalogIconHelper";
|
||||
import { TSelectorItem } from "../../../components/selector";
|
||||
|
||||
import CatalogFolderReactSvgUrl from "PUBLIC_DIR/images/catalog.folder.react.svg?url";
|
||||
import CatalogUserReactSvgUrl from "PUBLIC_DIR/images/catalog.user.react.svg?url";
|
||||
|
||||
import { useRootHelperProps, Item } from "../FilesSelector.types";
|
||||
|
||||
import { defaultBreadCrumb } from "../utils";
|
||||
import { getCatalogIconUrlByType } from "@docspace/shared/utils/catalogIconHelper";
|
||||
import { UseRootHelperProps } from "../FilesSelector.types";
|
||||
import { DEFAULT_BREAD_CRUMB } from "../FilesSelector.constants";
|
||||
|
||||
const useRootHelper = ({
|
||||
setBreadCrumbs,
|
||||
@ -21,25 +18,34 @@ const useRootHelper = ({
|
||||
setTotal,
|
||||
setHasNextPage,
|
||||
isUserOnly,
|
||||
}: useRootHelperProps) => {
|
||||
setIsInit,
|
||||
}: UseRootHelperProps) => {
|
||||
const [isRoot, setIsRoot] = React.useState<boolean>(false);
|
||||
const requestRunning = React.useRef(false);
|
||||
|
||||
const getRootData = React.useCallback(async () => {
|
||||
setBreadCrumbs([defaultBreadCrumb]);
|
||||
setIsRoot(true);
|
||||
setIsBreadCrumbsLoading(false);
|
||||
const newItems: Item[] = [];
|
||||
if (requestRunning.current) return;
|
||||
|
||||
let currentTree: Item[] | null = null;
|
||||
requestRunning.current = true;
|
||||
setBreadCrumbs([DEFAULT_BREAD_CRUMB]);
|
||||
setIsRoot(true);
|
||||
setIsNextPageLoading(true);
|
||||
setIsBreadCrumbsLoading(false);
|
||||
const newItems: TSelectorItem[] = [];
|
||||
|
||||
let currentTree: TFolder[] | null = null;
|
||||
|
||||
if (treeFolders && treeFolders?.length > 0) {
|
||||
currentTree = treeFolders;
|
||||
} else {
|
||||
currentTree = await getFoldersTree();
|
||||
const folders = await getFoldersTree();
|
||||
currentTree = folders;
|
||||
}
|
||||
|
||||
currentTree?.forEach((folder) => {
|
||||
const avatar = getCatalogIconUrlByType(folder.rootFolderType);
|
||||
let avatar = "";
|
||||
if (folder.rootFolderType)
|
||||
avatar = getCatalogIconUrlByType(folder.rootFolderType);
|
||||
|
||||
if (
|
||||
(!isUserOnly && folder.rootFolderType === FolderType.Rooms) ||
|
||||
@ -47,7 +53,6 @@ const useRootHelper = ({
|
||||
) {
|
||||
newItems.push({
|
||||
label: folder.title,
|
||||
title: folder.title,
|
||||
id: folder.id,
|
||||
parentId: folder.parentId,
|
||||
rootFolderType: folder.rootFolderType,
|
||||
@ -64,7 +69,19 @@ const useRootHelper = ({
|
||||
setTotal(newItems.length);
|
||||
setHasNextPage(false);
|
||||
setIsNextPageLoading(false);
|
||||
}, [treeFolders]);
|
||||
setIsInit(false);
|
||||
requestRunning.current = false;
|
||||
}, [
|
||||
isUserOnly,
|
||||
setBreadCrumbs,
|
||||
setHasNextPage,
|
||||
setIsBreadCrumbsLoading,
|
||||
setIsInit,
|
||||
setIsNextPageLoading,
|
||||
setItems,
|
||||
setTotal,
|
||||
treeFolders,
|
||||
]);
|
||||
|
||||
return { isRoot, setIsRoot, getRootData };
|
||||
};
|
257
packages/shared/selectors/Files/hooks/useSocketHelper.ts
Normal file
257
packages/shared/selectors/Files/hooks/useSocketHelper.ts
Normal file
@ -0,0 +1,257 @@
|
||||
import React from "react";
|
||||
|
||||
import { TSelectorItem } from "../../../components/selector";
|
||||
import { TFile, TFolder } from "../../../api/files/types";
|
||||
import { TRoom } from "../../../api/rooms/types";
|
||||
import { TOptSocket } from "../../../utils/socket";
|
||||
|
||||
import {
|
||||
convertFilesToItems,
|
||||
convertFoldersToItems,
|
||||
convertRoomsToItems,
|
||||
} from "../FilesSelector.utils";
|
||||
import { UseSocketHelperProps } from "../FilesSelector.types";
|
||||
|
||||
const useSocketHelper = ({
|
||||
socketHelper,
|
||||
socketSubscribers,
|
||||
disabledItems,
|
||||
filterParam,
|
||||
setItems,
|
||||
setBreadCrumbs,
|
||||
setTotal,
|
||||
getIcon,
|
||||
}: UseSocketHelperProps) => {
|
||||
const subscribedId = React.useRef<null | number>(null);
|
||||
|
||||
const unsubscribe = React.useCallback(
|
||||
(id: number, clear = true) => {
|
||||
if (clear) {
|
||||
subscribedId.current = null;
|
||||
}
|
||||
|
||||
if (id && !socketSubscribers.has(`DIR-${id}`)) {
|
||||
socketHelper.emit({
|
||||
command: "unsubscribe",
|
||||
data: {
|
||||
roomParts: `DIR-${id}`,
|
||||
individual: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
[socketHelper, socketSubscribers],
|
||||
);
|
||||
|
||||
const subscribe = React.useCallback(
|
||||
(id: number) => {
|
||||
const roomParts = `DIR-${id}`;
|
||||
|
||||
if (socketSubscribers.has(roomParts)) return (subscribedId.current = id);
|
||||
|
||||
if (subscribedId.current && !socketSubscribers.has(roomParts)) {
|
||||
unsubscribe(subscribedId.current, false);
|
||||
}
|
||||
|
||||
socketHelper.emit({
|
||||
command: "subscribe",
|
||||
data: {
|
||||
roomParts: `DIR-${id}`,
|
||||
individual: true,
|
||||
},
|
||||
});
|
||||
|
||||
subscribedId.current = id;
|
||||
},
|
||||
[socketHelper, socketSubscribers, unsubscribe],
|
||||
);
|
||||
|
||||
const addItem = React.useCallback(
|
||||
(opt: TOptSocket) => {
|
||||
if (!opt?.data) return;
|
||||
|
||||
const data: TFile | TFolder | TRoom = JSON.parse(opt.data);
|
||||
|
||||
if (
|
||||
"folderId" in data && data.folderId
|
||||
? data.folderId !== subscribedId.current
|
||||
: "parentId" in data && data.parentId !== subscribedId.current
|
||||
)
|
||||
return;
|
||||
|
||||
let item: TSelectorItem = {} as TSelectorItem;
|
||||
|
||||
if (opt?.type === "file" && "folderId" in data) {
|
||||
item = convertFilesToItems([data], getIcon, filterParam)[0];
|
||||
} else if (opt?.type === "folder" && "roomType" in data) {
|
||||
item =
|
||||
data.roomType && "tags" in data
|
||||
? convertRoomsToItems([data])[0]
|
||||
: convertFoldersToItems([data], disabledItems, filterParam)[0];
|
||||
}
|
||||
|
||||
setItems((value) => {
|
||||
if (!item || !value) return value;
|
||||
|
||||
if (opt.type === "folder") {
|
||||
setTotal((v) => v + 1);
|
||||
|
||||
return [item, ...value];
|
||||
}
|
||||
|
||||
if (opt.type === "file") {
|
||||
let idx = 0;
|
||||
|
||||
for (let i = 0; i < value.length - 1; i += 1) {
|
||||
if (!value[i].isFolder) break;
|
||||
|
||||
idx = i + 1;
|
||||
}
|
||||
|
||||
const newValue = [...value];
|
||||
|
||||
newValue.splice(idx, 0, item);
|
||||
|
||||
setTotal((v) => v + 1);
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
},
|
||||
[disabledItems, filterParam, getIcon, setItems, setTotal],
|
||||
);
|
||||
|
||||
const updateItem = React.useCallback(
|
||||
(opt: TOptSocket) => {
|
||||
if (!opt?.data) return;
|
||||
|
||||
const data: TFile | TFolder | TRoom = JSON.parse(opt.data);
|
||||
|
||||
if (
|
||||
(("folderId" in data &&
|
||||
data.folderId &&
|
||||
data.folderId !== subscribedId.current) ||
|
||||
("parentId" in data &&
|
||||
data.parentId &&
|
||||
data.parentId !== subscribedId.current)) &&
|
||||
data.id !== subscribedId.current
|
||||
)
|
||||
return;
|
||||
|
||||
let item: TSelectorItem = {} as TSelectorItem;
|
||||
|
||||
if (opt?.type === "file" && "folderId" in data) {
|
||||
item = convertFilesToItems([data], getIcon, filterParam)[0];
|
||||
} else if (opt?.type === "folder" && "roomType" in data) {
|
||||
item =
|
||||
data.roomType && "tags" in data
|
||||
? convertRoomsToItems([data])[0]
|
||||
: convertFoldersToItems([data], disabledItems, filterParam)[0];
|
||||
}
|
||||
|
||||
if (item?.id === subscribedId.current) {
|
||||
return setBreadCrumbs((value) => {
|
||||
if (!value) return value;
|
||||
|
||||
const newValue = [...value];
|
||||
|
||||
if (newValue[newValue.length - 1].id === item?.id) {
|
||||
newValue[newValue.length - 1].label = item.label;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
|
||||
setItems((value) => {
|
||||
if (!item || !value) return value;
|
||||
|
||||
if (opt.type === "folder") {
|
||||
const idx = value.findIndex((v) => v.id === item?.id && v.isFolder);
|
||||
|
||||
if (idx > -1) {
|
||||
const newValue = [...value];
|
||||
|
||||
newValue.splice(idx, 1, item);
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
setBreadCrumbs((breadCrumbsValue) => {
|
||||
return breadCrumbsValue;
|
||||
});
|
||||
}
|
||||
|
||||
if (opt.type === "file") {
|
||||
const idx = value.findIndex((v) => v.id === item?.id && !v.isFolder);
|
||||
|
||||
if (idx > -1) {
|
||||
const newValue = [...value];
|
||||
|
||||
newValue.splice(idx, 1, item);
|
||||
|
||||
return [...newValue];
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
},
|
||||
[disabledItems, filterParam, getIcon, setBreadCrumbs, setItems],
|
||||
);
|
||||
|
||||
const deleteItem = React.useCallback(
|
||||
(opt: TOptSocket) => {
|
||||
setItems((value) => {
|
||||
if (!value) return value;
|
||||
|
||||
if (opt.type === "folder") {
|
||||
const newValue = value.filter(
|
||||
(v) => v?.id !== opt?.id || !v.isFolder,
|
||||
);
|
||||
|
||||
if (newValue.length !== value.length) {
|
||||
setTotal((v) => v - 1);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
if (opt.type === "file") {
|
||||
const newValue = value.filter((v) => v?.id !== opt?.id || v.isFolder);
|
||||
|
||||
if (newValue.length !== value.length) {
|
||||
setTotal((v) => v - 1);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
},
|
||||
[setItems, setTotal],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
socketHelper.on("s:modify-folder", (opt?: TOptSocket) => {
|
||||
switch (opt?.cmd) {
|
||||
case "create":
|
||||
addItem(opt);
|
||||
break;
|
||||
case "update":
|
||||
updateItem(opt);
|
||||
break;
|
||||
case "delete":
|
||||
deleteItem(opt);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
}, [addItem, updateItem, deleteItem, socketHelper]);
|
||||
|
||||
return { subscribe, unsubscribe };
|
||||
};
|
||||
|
||||
export default useSocketHelper;
|
544
packages/shared/selectors/Files/index.tsx
Normal file
544
packages/shared/selectors/Files/index.tsx
Normal file
@ -0,0 +1,544 @@
|
||||
import React from "react";
|
||||
import { useTheme } from "styled-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import EmptyScreenFilterAltSvgUrl from "PUBLIC_DIR/images/empty_screen_filter_alt.svg?url";
|
||||
import EmptyScreenFilterAltDarkSvgUrl from "PUBLIC_DIR/images/empty_screen_filter_alt_dark.svg?url";
|
||||
import EmptyScreenAltSvgUrl from "PUBLIC_DIR/images/empty_screen_alt.svg?url";
|
||||
import EmptyScreenAltSvgDarkUrl from "PUBLIC_DIR/images/empty_screen_alt_dark.svg?url";
|
||||
|
||||
import { TRoomSecurity } from "../../api/rooms/types";
|
||||
import { TFileSecurity, TFolder, TFolderSecurity } from "../../api/files/types";
|
||||
|
||||
import { FolderType, RoomsType, DeviceType } from "../../enums";
|
||||
|
||||
import { Selector, TSelectorItem } from "../../components/selector";
|
||||
import { Aside } from "../../components/aside";
|
||||
import { Backdrop } from "../../components/backdrop";
|
||||
import { Portal } from "../../components/portal";
|
||||
import {
|
||||
RowLoader,
|
||||
SearchLoader,
|
||||
BreadCrumbsLoader,
|
||||
} from "../../skeletons/selector";
|
||||
import {
|
||||
TBreadCrumb,
|
||||
TSelectorBreadCrumbs,
|
||||
TSelectorCancelButton,
|
||||
TSelectorCheckbox,
|
||||
TSelectorHeader,
|
||||
TSelectorInput,
|
||||
TSelectorSearch,
|
||||
TSelectorSubmitButton,
|
||||
} from "../../components/selector/Selector.types";
|
||||
|
||||
import useFilesHelper from "./hooks/useFilesHelper";
|
||||
import useLoadersHelper from "./hooks/useLoadersHelper";
|
||||
import useRoomsHelper from "./hooks/useRoomsHelper";
|
||||
import useRootHelper from "./hooks/useRootHelper";
|
||||
import useSocketHelper from "./hooks/useSocketHelper";
|
||||
import { FilesSelectorProps } from "./FilesSelector.types";
|
||||
import useFilesSettings from "./hooks/useFilesSettings";
|
||||
|
||||
const FilesSelector = ({
|
||||
socketHelper,
|
||||
socketSubscribers,
|
||||
disabledItems,
|
||||
filterParam,
|
||||
getIcon: getIconProp,
|
||||
treeFolders,
|
||||
onSetBaseFolderPath,
|
||||
isUserOnly,
|
||||
isRoomsOnly,
|
||||
isThirdParty,
|
||||
rootThirdPartyId,
|
||||
roomsFolderId,
|
||||
currentFolderId,
|
||||
parentId,
|
||||
rootFolderType,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
getIsDisabled,
|
||||
withHeader,
|
||||
headerLabel,
|
||||
submitButtonLabel,
|
||||
withCancelButton,
|
||||
withFooterInput,
|
||||
withFooterCheckbox,
|
||||
footerInputHeader,
|
||||
currentFooterInputValue,
|
||||
footerCheckboxLabel,
|
||||
descriptionText,
|
||||
submitButtonId,
|
||||
cancelButtonId,
|
||||
embedded,
|
||||
isPanelVisible,
|
||||
currentDeviceType,
|
||||
getFilesArchiveError,
|
||||
setIsDataReady,
|
||||
}: FilesSelectorProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation(["Common"]);
|
||||
|
||||
const [breadCrumbs, setBreadCrumbs] = React.useState<TBreadCrumb[]>([]);
|
||||
const [items, setItems] = React.useState<TSelectorItem[]>([]);
|
||||
|
||||
const [selectedItemType, setSelectedItemType] = React.useState<
|
||||
"rooms" | "files" | undefined
|
||||
>(undefined);
|
||||
const [selectedItemId, setSelectedItemId] = React.useState<
|
||||
number | string | undefined
|
||||
>(undefined);
|
||||
const [selectedItemSecurity, setSelectedItemSecurity] = React.useState<
|
||||
TRoomSecurity | TFolderSecurity | TFileSecurity | undefined
|
||||
>(undefined);
|
||||
const [selectedTreeNode, setSelectedTreeNode] = React.useState({} as TFolder);
|
||||
const [selectedFileInfo, setSelectedFileInfo] = React.useState<{
|
||||
id: number | string;
|
||||
title: string;
|
||||
path?: string[];
|
||||
fileExst?: string;
|
||||
inPublic?: boolean;
|
||||
} | null>(null);
|
||||
const [total, setTotal] = React.useState<number>(0);
|
||||
const [hasNextPage, setHasNextPage] = React.useState<boolean>(false);
|
||||
const [isSelectedParentFolder, setIsSelectedParentFolder] =
|
||||
React.useState<boolean>(false);
|
||||
const [searchValue, setSearchValue] = React.useState<string>("");
|
||||
const [isInit, setIsInit] = React.useState(true);
|
||||
|
||||
const { getIcon } = useFilesSettings(getIconProp);
|
||||
|
||||
const { subscribe, unsubscribe } = useSocketHelper({
|
||||
socketHelper,
|
||||
socketSubscribers,
|
||||
disabledItems,
|
||||
filterParam,
|
||||
getIcon,
|
||||
setItems,
|
||||
setBreadCrumbs,
|
||||
setTotal,
|
||||
});
|
||||
|
||||
const {
|
||||
setIsBreadCrumbsLoading,
|
||||
isNextPageLoading,
|
||||
setIsNextPageLoading,
|
||||
isFirstLoad,
|
||||
setIsFirstLoad,
|
||||
showBreadCrumbsLoader,
|
||||
showLoader,
|
||||
} = useLoadersHelper({ items });
|
||||
|
||||
const { isRoot, setIsRoot, getRootData } = useRootHelper({
|
||||
setIsBreadCrumbsLoading,
|
||||
setBreadCrumbs,
|
||||
setTotal,
|
||||
setItems,
|
||||
treeFolders,
|
||||
setHasNextPage,
|
||||
setIsNextPageLoading,
|
||||
isUserOnly,
|
||||
setIsInit,
|
||||
});
|
||||
|
||||
const { getRoomList } = useRoomsHelper({
|
||||
setIsBreadCrumbsLoading,
|
||||
setBreadCrumbs,
|
||||
setIsNextPageLoading,
|
||||
setHasNextPage,
|
||||
setTotal,
|
||||
setItems,
|
||||
isFirstLoad,
|
||||
setIsRoot,
|
||||
searchValue,
|
||||
isRoomsOnly,
|
||||
onSetBaseFolderPath,
|
||||
isInit,
|
||||
setIsInit,
|
||||
});
|
||||
|
||||
const { getFileList } = useFilesHelper({
|
||||
setIsBreadCrumbsLoading,
|
||||
setBreadCrumbs,
|
||||
setIsNextPageLoading,
|
||||
setHasNextPage,
|
||||
setTotal,
|
||||
setItems,
|
||||
selectedItemId,
|
||||
isFirstLoad,
|
||||
setIsRoot,
|
||||
searchValue,
|
||||
disabledItems,
|
||||
setSelectedItemSecurity,
|
||||
isThirdParty,
|
||||
|
||||
setSelectedTreeNode,
|
||||
filterParam,
|
||||
getRootData,
|
||||
onSetBaseFolderPath,
|
||||
isRoomsOnly,
|
||||
rootThirdPartyId,
|
||||
getRoomList,
|
||||
getIcon,
|
||||
|
||||
setIsSelectedParentFolder,
|
||||
roomsFolderId,
|
||||
getFilesArchiveError,
|
||||
isInit,
|
||||
setIsInit,
|
||||
});
|
||||
|
||||
const onSelectAction = React.useCallback(
|
||||
(item: TSelectorItem) => {
|
||||
if (item.isFolder) {
|
||||
setIsFirstLoad(true);
|
||||
setItems([]);
|
||||
setBreadCrumbs((value) => [
|
||||
...value,
|
||||
{
|
||||
label: item.label,
|
||||
id: item.id,
|
||||
isRoom:
|
||||
item.parentId === 0 && item.rootFolderType === FolderType.Rooms,
|
||||
roomType: item.roomType,
|
||||
shared: item.shared,
|
||||
} as TBreadCrumb,
|
||||
]);
|
||||
setSelectedItemId(item.id);
|
||||
setSearchValue("");
|
||||
|
||||
if (item.parentId === 0 && item.rootFolderType === FolderType.Rooms) {
|
||||
setSelectedItemType("rooms");
|
||||
} else {
|
||||
setSelectedItemType("files");
|
||||
}
|
||||
} else if (item.id && item.label) {
|
||||
const inPublic =
|
||||
breadCrumbs.findIndex((f) => f.roomType === RoomsType.PublicRoom) >
|
||||
-1;
|
||||
|
||||
setSelectedFileInfo({
|
||||
id: item.id,
|
||||
title: item.label,
|
||||
fileExst: item.fileExst,
|
||||
inPublic,
|
||||
});
|
||||
}
|
||||
},
|
||||
[breadCrumbs, setIsFirstLoad],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!selectedItemId) return;
|
||||
if (selectedItemId && isRoot) return unsubscribe(+selectedItemId);
|
||||
|
||||
subscribe(+selectedItemId);
|
||||
}, [selectedItemId, isRoot, unsubscribe, subscribe]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsFirstLoad(true);
|
||||
const needRoomList = isRoomsOnly && !currentFolderId;
|
||||
|
||||
if (needRoomList) {
|
||||
setSelectedItemType("rooms");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentFolderId) {
|
||||
setSelectedItemType("rooms");
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedItemId(currentFolderId);
|
||||
|
||||
if (
|
||||
needRoomList ||
|
||||
(!isThirdParty &&
|
||||
parentId === roomsFolderId &&
|
||||
rootFolderType === FolderType.Rooms)
|
||||
) {
|
||||
setSelectedItemType("rooms");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedItemType("files");
|
||||
}, [
|
||||
currentFolderId,
|
||||
isRoomsOnly,
|
||||
isThirdParty,
|
||||
parentId,
|
||||
roomsFolderId,
|
||||
rootFolderType,
|
||||
setIsFirstLoad,
|
||||
]);
|
||||
|
||||
const onClickBreadCrumb = React.useCallback(
|
||||
(item: TBreadCrumb) => {
|
||||
if (!isFirstLoad) {
|
||||
setSearchValue("");
|
||||
setIsFirstLoad(true);
|
||||
if (+item.id === 0) {
|
||||
setSelectedItemSecurity(undefined);
|
||||
setSelectedItemType(undefined);
|
||||
getRootData();
|
||||
} else {
|
||||
setItems([]);
|
||||
|
||||
setBreadCrumbs((bc) => {
|
||||
const idx = bc.findIndex(
|
||||
(value) => value.id.toString() === item.id.toString(),
|
||||
);
|
||||
|
||||
const maxLength = bc.length - 1;
|
||||
let foundParentId = false;
|
||||
let currentFolderIndex = -1;
|
||||
|
||||
const newBreadCrumbs = bc.map((i, index) => {
|
||||
if (!foundParentId) {
|
||||
currentFolderIndex = disabledItems.findIndex(
|
||||
(id) => id === i?.id,
|
||||
);
|
||||
}
|
||||
|
||||
if (index !== maxLength && currentFolderIndex !== -1) {
|
||||
foundParentId = true;
|
||||
if (!isSelectedParentFolder) setIsSelectedParentFolder(true);
|
||||
}
|
||||
|
||||
if (
|
||||
index === maxLength &&
|
||||
!foundParentId &&
|
||||
isSelectedParentFolder
|
||||
)
|
||||
setIsSelectedParentFolder(false);
|
||||
|
||||
return { ...i };
|
||||
});
|
||||
|
||||
newBreadCrumbs.splice(idx + 1, newBreadCrumbs.length - idx - 1);
|
||||
return newBreadCrumbs;
|
||||
});
|
||||
|
||||
setSelectedItemId(item.id);
|
||||
if (item.isRoom) {
|
||||
setSelectedItemType("rooms");
|
||||
} else {
|
||||
setSelectedItemType("files");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
disabledItems,
|
||||
getRootData,
|
||||
isFirstLoad,
|
||||
isSelectedParentFolder,
|
||||
setIsFirstLoad,
|
||||
],
|
||||
);
|
||||
|
||||
const onSearchAction = React.useCallback(
|
||||
(value: string, callback?: Function) => {
|
||||
setIsFirstLoad(true);
|
||||
setItems([]);
|
||||
|
||||
setSearchValue(value);
|
||||
callback?.();
|
||||
},
|
||||
[setIsFirstLoad],
|
||||
);
|
||||
|
||||
const onClearSearchAction = React.useCallback(
|
||||
(callback?: Function) => {
|
||||
setIsFirstLoad(true);
|
||||
setItems([]);
|
||||
|
||||
setSearchValue("");
|
||||
callback?.();
|
||||
},
|
||||
[setIsFirstLoad],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (setIsDataReady) setIsDataReady(!showLoader);
|
||||
}, [setIsDataReady, showLoader]);
|
||||
|
||||
const onSubmitAction = React.useCallback(
|
||||
async (
|
||||
i: unknown,
|
||||
accessRights: unknown,
|
||||
fileName: string,
|
||||
isChecked: boolean,
|
||||
) => {
|
||||
const isPublic =
|
||||
breadCrumbs.findIndex((f) => f.roomType === RoomsType.PublicRoom) > -1;
|
||||
const folderTitle = breadCrumbs[breadCrumbs.length - 1].label;
|
||||
|
||||
onSubmit(
|
||||
selectedItemId,
|
||||
folderTitle,
|
||||
isPublic,
|
||||
breadCrumbs,
|
||||
fileName,
|
||||
isChecked,
|
||||
selectedTreeNode,
|
||||
selectedFileInfo,
|
||||
);
|
||||
},
|
||||
[breadCrumbs, selectedFileInfo, selectedItemId, selectedTreeNode, onSubmit],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (selectedItemType === "rooms") getRoomList(0);
|
||||
if (selectedItemType === "files" && typeof selectedItemId !== "undefined")
|
||||
getFileList(0);
|
||||
}, [getFileList, getRoomList, selectedItemType, selectedItemId]);
|
||||
|
||||
const headerProps: TSelectorHeader = withHeader
|
||||
? { withHeader, headerProps: { headerLabel } }
|
||||
: {};
|
||||
|
||||
const withSearch =
|
||||
!isRoot && items.length ? items.length > 0 : !isRoot && isFirstLoad;
|
||||
|
||||
const searchProps: TSelectorSearch = withSearch
|
||||
? {
|
||||
withSearch,
|
||||
searchLoader: <SearchLoader />,
|
||||
searchPlaceholder: t("Common:Search"),
|
||||
searchValue,
|
||||
isSearchLoading: showBreadCrumbsLoader,
|
||||
onSearch: onSearchAction,
|
||||
onClearSearch: onClearSearchAction,
|
||||
}
|
||||
: {};
|
||||
|
||||
const submitButtonProps: TSelectorSubmitButton = {
|
||||
onSubmit: onSubmitAction,
|
||||
submitButtonLabel,
|
||||
submitButtonId,
|
||||
disableSubmitButton: getIsDisabled(
|
||||
isFirstLoad,
|
||||
isSelectedParentFolder,
|
||||
selectedItemId,
|
||||
selectedItemType,
|
||||
isRoot,
|
||||
selectedItemSecurity,
|
||||
selectedFileInfo,
|
||||
),
|
||||
};
|
||||
|
||||
const cancelButtonProps: TSelectorCancelButton = withCancelButton
|
||||
? {
|
||||
withCancelButton,
|
||||
cancelButtonLabel: t("Common:CancelButton"),
|
||||
cancelButtonId,
|
||||
onCancel,
|
||||
}
|
||||
: {};
|
||||
|
||||
const footerInputProps: TSelectorInput = withFooterInput
|
||||
? {
|
||||
withFooterInput,
|
||||
footerInputHeader,
|
||||
currentFooterInputValue,
|
||||
}
|
||||
: {};
|
||||
|
||||
const footerCheckboxProps: TSelectorCheckbox = withFooterCheckbox
|
||||
? {
|
||||
withFooterCheckbox,
|
||||
footerCheckboxLabel,
|
||||
isChecked: false,
|
||||
setIsChecked: () => {},
|
||||
}
|
||||
: {};
|
||||
|
||||
const breadCrumbsProps: TSelectorBreadCrumbs = {
|
||||
breadCrumbs,
|
||||
breadCrumbsLoader: <BreadCrumbsLoader />,
|
||||
isBreadCrumbsLoading: showBreadCrumbsLoader,
|
||||
withBreadCrumbs: true,
|
||||
onSelectBreadCrumb: onClickBreadCrumb,
|
||||
};
|
||||
|
||||
const SelectorBody = (
|
||||
<Selector
|
||||
{...headerProps}
|
||||
{...searchProps}
|
||||
{...submitButtonProps}
|
||||
{...cancelButtonProps}
|
||||
{...footerInputProps}
|
||||
{...footerCheckboxProps}
|
||||
{...breadCrumbsProps}
|
||||
isMultiSelect={false}
|
||||
items={items}
|
||||
onSelect={onSelectAction}
|
||||
emptyScreenImage={
|
||||
theme?.isBase ? EmptyScreenAltSvgUrl : EmptyScreenAltSvgDarkUrl
|
||||
}
|
||||
emptyScreenHeader={t("SelectorEmptyScreenHeader")}
|
||||
emptyScreenDescription=""
|
||||
searchEmptyScreenImage={
|
||||
theme?.isBase
|
||||
? EmptyScreenFilterAltSvgUrl
|
||||
: EmptyScreenFilterAltDarkSvgUrl
|
||||
}
|
||||
searchEmptyScreenHeader={t("Common:NotFoundTitle")}
|
||||
searchEmptyScreenDescription={t("EmptyFilterDescriptionText")}
|
||||
isLoading={showLoader}
|
||||
rowLoader={
|
||||
<RowLoader
|
||||
isMultiSelect={false}
|
||||
isUser={isRoot}
|
||||
isContainer={showLoader}
|
||||
/>
|
||||
}
|
||||
alwaysShowFooter
|
||||
isNextPageLoading={isNextPageLoading}
|
||||
hasNextPage={hasNextPage}
|
||||
totalItems={total}
|
||||
loadNextPage={
|
||||
isRoot
|
||||
? async () => {}
|
||||
: selectedItemType === "rooms"
|
||||
? getRoomList
|
||||
: getFileList
|
||||
}
|
||||
descriptionText={descriptionText}
|
||||
disableFirstFetch
|
||||
/>
|
||||
);
|
||||
|
||||
const selectorComponent = embedded ? (
|
||||
SelectorBody
|
||||
) : (
|
||||
<>
|
||||
<Backdrop
|
||||
visible={isPanelVisible}
|
||||
isAside
|
||||
withBackground
|
||||
zIndex={309}
|
||||
onClick={onCancel}
|
||||
/>
|
||||
<Aside
|
||||
visible={isPanelVisible}
|
||||
withoutBodyScroll
|
||||
zIndex={310}
|
||||
onClose={onCancel}
|
||||
>
|
||||
{SelectorBody}
|
||||
</Aside>
|
||||
</>
|
||||
);
|
||||
|
||||
return currentDeviceType === DeviceType.mobile && !embedded ? (
|
||||
<Portal visible={isPanelVisible} element={<div>{selectorComponent}</div>} />
|
||||
) : (
|
||||
selectorComponent
|
||||
);
|
||||
};
|
||||
|
||||
export default FilesSelector;
|
67
packages/shared/skeletons/selector/BreadCrumbs.tsx
Normal file
67
packages/shared/skeletons/selector/BreadCrumbs.tsx
Normal file
@ -0,0 +1,67 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { RectangleSkeleton, RectangleSkeletonProps } from "../rectangle";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
padding: 0 16px;
|
||||
|
||||
margin-bottom: 16px;
|
||||
|
||||
gap: 8px;
|
||||
`;
|
||||
|
||||
interface BreadCrumbsProps extends RectangleSkeletonProps {
|
||||
id?: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const BreadCrumbsLoader = ({
|
||||
id,
|
||||
className,
|
||||
style,
|
||||
|
||||
...rest
|
||||
}: BreadCrumbsProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<RectangleSkeleton
|
||||
width="80px"
|
||||
height="22px"
|
||||
style={{ ...style }}
|
||||
{...rest}
|
||||
/>
|
||||
<RectangleSkeleton
|
||||
width="12px"
|
||||
height="12px"
|
||||
style={{ ...style }}
|
||||
{...rest}
|
||||
/>
|
||||
<RectangleSkeleton
|
||||
width="80px"
|
||||
height="22px"
|
||||
style={{ ...style }}
|
||||
{...rest}
|
||||
/>
|
||||
<RectangleSkeleton
|
||||
width="12px"
|
||||
height="12px"
|
||||
style={{ ...style }}
|
||||
{...rest}
|
||||
/>
|
||||
<RectangleSkeleton
|
||||
width="80px"
|
||||
height="22px"
|
||||
style={{ ...style }}
|
||||
{...rest}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default BreadCrumbsLoader;
|
@ -1,4 +1,5 @@
|
||||
import RowLoader from "./Row";
|
||||
import SearchLoader from "./Search";
|
||||
import BreadCrumbsLoader from "./BreadCrumbs";
|
||||
|
||||
export { SearchLoader, RowLoader };
|
||||
export { SearchLoader, RowLoader, BreadCrumbsLoader };
|
||||
|
@ -13,7 +13,7 @@ export type TDirectionY = "bottom" | "top" | "both";
|
||||
|
||||
export type TViewAs = "tile" | "table" | "row" | "settings" | "profile";
|
||||
|
||||
export type TTranslation = (key: string) => string;
|
||||
export type TTranslation = (key: string, prop?: unknown) => string;
|
||||
|
||||
export type NonFunctionPropertyNames<T, ExcludeTypes> = {
|
||||
[K in keyof T]: T[K] extends ExcludeTypes ? never : K;
|
||||
|
@ -104,3 +104,13 @@ export const getModalType = () => {
|
||||
export const isValidDate = (date: Date) => {
|
||||
return moment(date).tz(window.timezone).year() !== 9999;
|
||||
};
|
||||
|
||||
export const presentInArray = (
|
||||
array: string[],
|
||||
search: string,
|
||||
caseInsensitive = false,
|
||||
) => {
|
||||
const pattern = caseInsensitive ? search.toLowerCase() : search;
|
||||
const result = array?.findIndex((item) => item === pattern);
|
||||
return result !== -1;
|
||||
};
|
||||
|
@ -11,7 +11,16 @@ let client: Socket<DefaultEventsMap, DefaultEventsMap> | null = null;
|
||||
let callbacks: { eventName: string; callback: (value: TOnCallback) => void }[] =
|
||||
[];
|
||||
|
||||
const subscribers = new Set();
|
||||
const subscribers = new Set<string>();
|
||||
|
||||
export type TOptSocket = {
|
||||
featureId: string;
|
||||
value: number;
|
||||
data?: string;
|
||||
type?: "folder" | "file";
|
||||
id?: string;
|
||||
cmd?: "create" | "update" | "delete";
|
||||
};
|
||||
|
||||
export type TEmit = {
|
||||
command: string;
|
||||
@ -126,7 +135,7 @@ class SocketIOHelper {
|
||||
}
|
||||
};
|
||||
|
||||
on = (eventName: string, callback: (value: TOnCallback) => void) => {
|
||||
on = (eventName: string, callback: (value: TOptSocket) => void) => {
|
||||
if (!this.isEnabled) {
|
||||
callbacks.push({ eventName, callback });
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user