From 41a6083a4bfbe20d800ff5482513df896984126e Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Fri, 19 Jul 2024 11:17:02 +0300 Subject: [PATCH] Client:Store: refactoring files store --- .../client/src/store/FilesActiveItemsStore.ts | 0 packages/client/src/store/FilesListStore.ts | 955 +++++++++++++++++- packages/client/src/store/FilesSocketStore.ts | 156 +-- packages/client/src/store/FilesStore.js | 923 +---------------- .../client/src/store/SelectedFolderStore.ts | 16 +- packages/client/src/store/ThirdPartyStore.js | 2 +- packages/client/src/store/index.js | 4 +- packages/shared/api/files/filter.ts | 2 +- packages/shared/enums/index.ts | 6 +- packages/shared/types/index.ts | 5 +- packages/shared/utils/socket.ts | 2 +- 11 files changed, 1054 insertions(+), 1017 deletions(-) create mode 100644 packages/client/src/store/FilesActiveItemsStore.ts diff --git a/packages/client/src/store/FilesActiveItemsStore.ts b/packages/client/src/store/FilesActiveItemsStore.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/client/src/store/FilesListStore.ts b/packages/client/src/store/FilesListStore.ts index 91ceb8ac33..e7d6738adf 100644 --- a/packages/client/src/store/FilesListStore.ts +++ b/packages/client/src/store/FilesListStore.ts @@ -1,14 +1,957 @@ +import { makeAutoObservable, runInAction } from "mobx"; +import axios from "axios"; + +import api from "@docspace/shared/api"; + import { TFile, TFolder } from "@docspace/shared/api/files/types"; -import { makeAutoObservable } from "mobx"; +import FilesFilter, { + TSortBy, + TSortOrder, +} from "@docspace/shared/api/files/filter"; -class FilesSelectionStore { - files: TFile[] = []; +import { TRoom } from "@docspace/shared/api/rooms/types"; +import RoomsFilter from "@docspace/shared/api/rooms/filter"; - folders: TFolder[] = []; +import { toastr } from "@docspace/shared/components/toast"; - constructor() { +import { ROOMS_PROVIDER_TYPE_NAME } from "@docspace/shared/constants"; + +import { isDesktop } from "@docspace/shared/utils"; +import { getDaysRemaining, isPublicRoom } from "@docspace/shared/utils/common"; + +import { SettingsStore } from "@docspace/shared/store/SettingsStore"; +import { UserStore } from "@docspace/shared/store/UserStore"; + +import { + FileStatus, + FilterKeys, + FilterType, + FolderType, + RoomSearchArea, + RoomsProviderType, + RoomsType, + ShareAccessRights, +} from "@docspace/shared/enums"; + +import { + getCategoryTypeByFolderType, + getCategoryUrl, +} from "SRC_DIR/helpers/utils"; +import { CategoryType } from "SRC_DIR/helpers/constants"; + +import FilesStore from "./FilesStore"; +import SelectedFolderStore from "./SelectedFolderStore"; +import TreeFoldersStore from "./TreeFoldersStore"; +import ClientLoadingStore from "./ClientLoadingStore"; +import PublicRoomStore from "./PublicRoomStore"; +import InfoPanelStore from "./InfoPanelStore"; +import PluginStore from "./PluginStore"; +import FilesSettingsStore from "./FilesSettingsStore"; +import ThirdPartyStore from "./ThirdPartyStore"; + +let requestCounter = 0; + +const NotFoundHttpCode = 404; +const ForbiddenHttpCode = 403; +const PaymentRequiredHttpCode = 402; +const UnauthorizedHttpCode = 401; + +class FilesListStore { + files: Map = new Map(); + + folders: Map = new Map(); + + isEmptyPage: boolean = false; + + roomsController = new AbortController(); + + filesController = new AbortController(); + + constructor( + private settingsStore: SettingsStore, + private selectedFolderStore: SelectedFolderStore, + private treeFoldersStore: TreeFoldersStore, + private clientLoadingStore: ClientLoadingStore, + private userStore: UserStore, + private publicRoomStore: PublicRoomStore, + private infoPanelStore: InfoPanelStore, + private pluginStore: PluginStore, + private filesSettingsStore: FilesSettingsStore, + private thirdPartyStore: ThirdPartyStore, + + private filesStore: FilesStore, + ) { makeAutoObservable(this); } + + setIsEmptyPage = (isEmptyPage: boolean) => { + this.isEmptyPage = isEmptyPage; + }; + + setFile = (file: TFile) => { + if (!this.files.has(file.id)) return; + + this.files.set(file.id, file); + this.filesStore.createThumbnail(file); + }; + + setFiles = (files: TFile[]) => { + const { socketHelper } = this.settingsStore; + + if (this.files.size > 0) { + const roomParts = Array.from(this.files.keys()).map((k) => `FILE-${k}`); + + socketHelper.emit({ + command: "unsubscribe", + data: { + roomParts, + individual: true, + }, + }); + } + + const newFiles: Map = new Map(); + const newRoomParts: string[] = []; + + files.forEach((value) => { + const key = value.id; + newFiles.set(key, value); + newRoomParts.push(`FILE-${key}`); + }); + + this.files = newFiles; + + if (newRoomParts.length) { + socketHelper.emit({ + command: "subscribe", + data: { + roomParts: newRoomParts, + individual: true, + }, + }); + } + + this.filesStore.createThumbnails(files); + }; + + updateFileStatus = (id: string | number | undefined, status: FileStatus) => { + if (!id) return; + + const item = this.files.get(id); + + if (item) this.files.set(id, { ...item, fileStatus: status }); + }; + + getFileInfo = async (id: string | number) => { + const fileInfo = await api.files.getFileInfo(id); + + this.setFile(fileInfo); + + return fileInfo; + }; + + setFolder = (folder: TRoom | TFolder) => { + if (!this.folders.has(folder.id)) return; + + this.folders.set(folder.id, folder); + }; + + setFolders = (folders: TRoom[] | TFolder[]) => { + const { socketHelper } = this.settingsStore; + + if (folders.length === 0) return; + + if (this.folders.size > 0) { + const roomParts = Array.from(this.folders.keys()).map((k) => `DIR-${k}`); + + socketHelper.emit({ + command: "unsubscribe", + data: { + roomParts, + individual: true, + }, + }); + } + + const newFolders: Map = new Map(); + const newRoomParts: string[] = []; + + folders.forEach((value) => { + const key = value.id; + newFolders.set(key, value); + newRoomParts.push(`DIR-${key}`); + }); + + this.folders = newFolders; + + socketHelper.emit({ + command: "subscribe", + data: { + roomParts: newRoomParts, + individual: true, + }, + }); + }; + + addFolder = (folder: TRoom | TFolder) => { + const { socketHelper } = this.settingsStore; + + const newFolders = new Map([ + [folder.id, folder], + ...this.folders.entries(), + ]); + + socketHelper.emit({ + command: "subscribe", + data: { + roomParts: `DIR-${folder.id}`, + individual: true, + }, + }); + + this.folders = newFolders; + }; + + removeFolder = (key: string | number) => { + this.folders.delete(key); + }; + + updateRoomMute = (id: string | number, mute: boolean) => { + const room = this.folders.get(id); + + if (!room) return; + + this.folders.set(id, { ...room, mute }); + }; + + getFolderInfo = async (id: string | number) => { + const folderInfo = await api.files.getFolderInfo(id); + + this.setFolder(folderInfo); + + return folderInfo; + }; + + clearFiles = () => { + this.files = new Map(); + this.folders = new Map(); + + this.selectedFolderStore.setSelectedFolder(null); + }; + + abortAllFetch = () => { + this.filesController.abort(); + this.roomsController.abort(); + this.filesController = new AbortController(); + this.roomsController = new AbortController(); + }; + + fetchFiles = async ( + folderId: string | number, + filter: FilesFilter, + clearFilter: boolean = true, + withSubfolders: boolean = false, + clearSelection: boolean = true, + ) => { + const { setSelectedNode } = this.treeFoldersStore; + + if (this.clientLoadingStore.isLoading) { + this.abortAllFetch(); + } + + const filterData = filter ? filter.clone() : FilesFilter.getDefault(); + filterData.folder = folderId; + + if (folderId === "@my" && this.userStore.user?.isVisitor) { + const url = getCategoryUrl(CategoryType.Shared); + + window.DocSpace.navigate( + `${url}?${RoomsFilter.getDefault().toUrlParams()}`, + ); + + return; + } + + this.filesStore.setIsErrorRoomNotAvailable(false); + + const filterStorageItem = + this.userStore.user?.id && + localStorage.getItem(`UserFilter=${this.userStore.user.id}`); + + if (filterStorageItem && !filter) { + const splitFilter = filterStorageItem.split(","); + + filterData.sortBy = splitFilter[0] as TSortBy; + filterData.pageCount = +splitFilter[1]; + filterData.sortOrder = splitFilter[2] as TSortOrder; + } + + if (!this.settingsStore.withPaging) { + filterData.page = 0; + filterData.pageCount = 100; + } + + const defaultFilter = FilesFilter.getDefault(); + + const { filterType, searchInContent } = filterData; + + if (typeof filterData.withSubfolders !== "boolean") + filterData.withSubfolders = defaultFilter.withSubfolders; + + if (typeof searchInContent !== "boolean") + filterData.searchInContent = defaultFilter.searchInContent; + + if (!Object.keys(FilterType).find((key) => FilterType[key] === filterType)) + filterData.filterType = defaultFilter.filterType; + + setSelectedNode([`${folderId}`]); + + try { + const folder = await api.files.getFolder( + folderId, + filterData, + this.filesController.signal, + ); + + let newTotal = folder.total; + + // fixed row loader if total and items length is different + const itemsLength = folder.folders.length + folder.files.length; + if (itemsLength < filterData.pageCount) { + newTotal = + filterData.page > 0 + ? itemsLength + this.files.size + this.folders.size + : itemsLength; + } + + filterData.total = newTotal; + + if ( + (folder.current.roomType === RoomsType.PublicRoom || + folder.current.roomType === RoomsType.FormRoom || + folder.current.roomType === RoomsType.CustomRoom) && + !this.publicRoomStore.isPublicRoom + ) { + await this.publicRoomStore.getExternalLinks(folder.current.id); + } + + if (newTotal > 0) { + const lastPage = filterData.getLastPage(); + + if (filterData.page > lastPage) { + filterData.page = lastPage; + + return this.fetchFiles( + folderId, + filterData, + clearFilter, + withSubfolders, + ); + } + } + + runInAction(() => { + if (!this.publicRoomStore.isPublicRoom) { + this.filesStore.categoryType = getCategoryTypeByFolderType( + folder.current.rootFolderType, + folder.current.parentId, + ); + } + }); + + if (this.filesStore.isPreview) { + // save filter for after closing preview change url + this.filesStore.setTempFilter(filterData); + } else { + this.filesStore.setFilesFilter(filterData); // TODO: FILTER + } + + const isPrivacyFolder = + folder.current.rootFolderType === FolderType.Privacy; + + let inRoom = false; + + const navigationPath = await Promise.all( + folder.pathParts.map(async (f, idx) => { + const { Rooms, Archive } = FolderType; + + const isCurrentFolder = folder.current.id === f.id; + + const folderInfo = isCurrentFolder + ? folder.current + : { ...f, id: f.id }; + + const { title, roomType } = folderInfo; + + inRoom = inRoom || (!!roomType && !isCurrentFolder); + + const isRootRoom = + idx === 0 && + (folder.current.rootFolderType === Rooms || + folder.current.rootFolderType === Archive); + + let shared; + let canCopyPublicLink; + + if (idx === 1) { + let room = folder.current; + + if (!isCurrentFolder) { + room = await api.files.getFolderInfo(folderId); + shared = room.shared; + canCopyPublicLink = + room.access === ShareAccessRights.RoomManager || + room.access === ShareAccessRights.None; + + if ("canCopyPublicLink" in room) + room.canCopyPublicLink = canCopyPublicLink; + this.infoPanelStore.setInfoPanelRoom(room); + } + + const { mute } = room; + + runInAction(() => { + this.filesStore.isMuteCurrentRoomNotifications = mute; + }); + } + + return { + id: folderId, + title, + isRoom: !!roomType, + roomType, + isRootRoom, + shared, + canCopyPublicLink, + }; + }), + ).then((res) => { + return res + .filter((item, index) => { + return index !== res.length - 1; + }) + .reverse(); + }); + this.selectedFolderStore.setSelectedFolder({ + folders: folder.folders, + ...folder.current, + inRoom, + isRoom: !!folder.current.roomType, + pathParts: folder.pathParts, + navigationPath, + ...{ new: folder.new }, + // type, + }); + + runInAction(() => { + const isEmptyList = [...folder.folders, ...folder.files].length === 0; + + if (filter && isEmptyList) { + const { + authorType, + roomId, + search, + withSubfolders: curWithSubFolders, + filterType: curFilterType, + searchInContent: curSearchInContent, + } = filter; + + const isFiltered = + authorType || + roomId || + search || + curWithSubFolders || + curFilterType || + curSearchInContent; + + if (isFiltered) { + this.setIsEmptyPage(false); + } else { + this.setIsEmptyPage(isEmptyList); + } + } else { + this.setIsEmptyPage(isEmptyList); + } + this.setFolders(isPrivacyFolder && !isDesktop() ? [] : folder.folders); + this.setFiles(isPrivacyFolder && !isDesktop() ? [] : folder.files); + }); + + if (clearFilter) { + if (clearSelection) { + // Find not processed + const tempSelection = this.filesStore.selection.filter( + (f) => + !this.filesStore.activeFiles.find((elem) => elem.id === f.id), + ); + const tempBuffer = + this.filesStore.bufferSelection && + this.filesStore.activeFiles.find( + (elem) => elem.id === this.filesStore.bufferSelection?.id, + ) == null + ? this.filesStore.bufferSelection + : null; + + // console.log({ tempSelection, tempBuffer }); + + // Clear all selections + this.filesStore.setSelected("close"); + + // TODO: see bug 63479 + if (this.selectedFolderStore?.id === folderId) { + // Restore not processed + if (tempSelection.length) + this.filesStore.setSelection(tempSelection); + if (tempBuffer) this.filesStore.setBufferSelection(tempBuffer); + } + } + } + + this.clientLoadingStore.setIsSectionHeaderLoading(false); + + const selectedFolder = { + selectedFolder: { ...this.selectedFolderStore }, + }; + + if (this.filesStore.createdItem) { + const newItem = this.filesList.find( + (item) => item.id === this.filesStore.createdItem?.id, + ); + + if (newItem) { + this.filesStore.setBufferSelection(newItem); + this.filesStore.setScrollToItem({ + id: newItem.id, + type: this.filesStore.createdItem?.type, + }); + } + + this.filesStore.setCreatedItem(null); + } + + if (isPublicRoom()) { + return folder; + } + + return selectedFolder; + } catch (err) { + if (err?.response?.status === 402) + this.filesStore.currentTariffStatusStore.setPortalTariff(); + + const isThirdPartyError = Number.isNaN(+folderId); + + if (requestCounter > 0 && !isThirdPartyError) return; + + requestCounter = +1; + + const isUserError = [ + NotFoundHttpCode, + ForbiddenHttpCode, + PaymentRequiredHttpCode, + UnauthorizedHttpCode, + ].includes(err?.response?.status); + + if (isUserError && !isThirdPartyError) { + this.filesStore.setIsErrorRoomNotAvailable(true); + } else if (axios.isCancel(err)) { + console.log("Request canceled", err.message); + } else { + toastr.error(err); + if (isThirdPartyError) { + const userId = this.userStore?.user?.id; + const searchArea = window.DocSpace.location.pathname.includes( + "shared", + ) + ? RoomSearchArea.Active + : RoomSearchArea.Archive; + + window.DocSpace.navigate( + `${window.DocSpace.location.pathname}?${RoomsFilter.getDefault(userId, searchArea).toUrlParams(userId, true)}`, + ); + } + return; + } + } finally { + if (window?.DocSpace?.location?.state?.highlightFileId) { + this.filesStore.setHighlightFile({ + highlightFileId: window.DocSpace.location.state.highlightFileId, + isFileHasExst: window.DocSpace.location.state.isFileHasExst, + }); + } + } + }; + + fetchRooms = async ( + folderId, + filter, + clearFilter = true, + withSubfolders = false, + clearSelection = true, + withFilterLocalStorage = false, + ) => { + const { setSelectedNode } = this.treeFoldersStore; + + if (this.clientLoadingStore.isLoading) { + this.abortAllFetch(); + } + + const filterData = filter + ? filter.clone() + : RoomsFilter.getDefault(this.userStore.user?.id); + + if (!this.settingsStore.withPaging) { + const isCustomCountPage = + filter && filter.pageCount !== 100 && filter.pageCount !== 25; + + if (!isCustomCountPage) { + filterData.page = 0; + filterData.pageCount = 100; + } + } + + if (folderId) setSelectedNode([`${folderId}`]); + + const defaultFilter = RoomsFilter.getDefault(); + + const { provider, quotaFilter, type } = filterData; + + if (!ROOMS_PROVIDER_TYPE_NAME[provider]) + filterData.provider = defaultFilter.provider; + + if ( + quotaFilter && + quotaFilter !== FilterKeys.customQuota && + quotaFilter !== FilterKeys.defaultQuota + ) + filterData.quotaFilter = defaultFilter.quotaFilter; + + if (type && !RoomsType[type]) filterData.type = defaultFilter.type; + + try { + const rooms = await api.rooms.getRooms( + filterData, + this.roomsController.signal, + ); + + if (!folderId) setSelectedNode([`${rooms.current.id}`]); + + filterData.total = rooms.total; + + if (rooms.total > 0) { + const lastPage = filterData.getLastPage(); + + if (filterData.page > lastPage) { + filterData.page = lastPage; + + return this.fetchRooms( + folderId, + filterData, + undefined, + undefined, + undefined, + true, + ); + } + + runInAction(() => { + this.filesStore.categoryType = getCategoryTypeByFolderType( + rooms.current.rootFolderType, + rooms.current.parentId, + ); + }); + + this.filesStore.setRoomsFilter(filterData); + + runInAction(() => { + const isEmptyList = rooms.folders.length === 0; + if (filter && isEmptyList) { + const { + subjectId, + filterValue, + type: curType, + withSubfolders: withRoomsSubfolders, + searchInContent: searchInContentRooms, + tags, + withoutTags, + quotaFilter: curQuotaFilter, + provider: curProvider, + } = filter; + + const isFiltered = + subjectId || + filterValue || + curType || + curProvider || + withRoomsSubfolders || + searchInContentRooms || + tags || + withoutTags || + curQuotaFilter; + + if (isFiltered) { + this.setIsEmptyPage(false); + } else { + this.setIsEmptyPage(isEmptyList); + } + } else { + this.setIsEmptyPage(isEmptyList); + } + + this.setFolders(rooms.folders); + this.setFiles([]); + + if (clearFilter) { + if (clearSelection) { + this.filesStore.setSelected("close"); + } + } + + this.infoPanelStore.setInfoPanelRoom(null); + this.selectedFolderStore.setSelectedFolder({ + folders: rooms.folders, + ...rooms.current, + pathParts: rooms.pathParts, + navigationPath: [], + ...{ new: rooms.new }, + }); + + this.clientLoadingStore.setIsSectionHeaderLoading(false); + + const selectedFolder = { + selectedFolder: { ...this.selectedFolderStore }, + }; + + if (this.filesStore.createdItem) { + const newItem = this.filesStore.filesList.find( + (item) => item.id === this.filesStore.createdItem?.id, + ); + + if (newItem) { + this.filesStore.setBufferSelection(newItem); + this.filesStore.setScrollToItem({ + id: newItem.id, + type: this.filesStore.createdItem?.type, + }); + } + + this.filesStore.setCreatedItem(null); + } + this.filesStore.setIsErrorRoomNotAvailable(false); + + return selectedFolder; + }); + } + } catch (err) { + if (err?.response?.status === 402) + this.filesStore.currentTariffStatusStore.setPortalTariff(); + + if (axios.isCancel(err)) { + console.log("Request canceled", err.message); + } else { + toastr.error(err); + } + } + }; + + getFilesListItems = (items: (TFile | TFolder | TRoom)[]) => { + const { fileItemsList } = this.pluginStore; + const { enablePlugins } = this.settingsStore; + const { getIcon } = this.filesSettingsStore; + + return items.map((item) => { + const { id, rootFolderId, access } = item; + + let thirdPartyIcon = ""; + let providerType = ""; + + if ("providerKey" in item) { + thirdPartyIcon = this.thirdPartyStore.getThirdPartyIcon( + item.providerKey, + "small", + ); + } + + if ("providerKey" in item) { + providerType = + RoomsProviderType[ + Object.keys(RoomsProviderType).find( + (key) => key === item.providerKey, + ) + ]; + } + + let canOpenPlayer = false; + let needConvert = false; + + if ("viewAccessibility" in item) { + canOpenPlayer = + item.viewAccessibility?.ImageView || + item.viewAccessibility?.MediaView; + + needConvert = item.viewAccessibility?.MustConvert; + } + + const previewUrl = canOpenPlayer + ? this.filesStore.getItemUrl(id, false, needConvert, canOpenPlayer) + : null; + + const contextOptions = this.filesStore.getFilesContextOptions(item); + const isThirdPartyFolder = + "providerKey" in item && item.providerKey && id === rootFolderId; + + const iconSize = this.filesStore.viewAs === "table" ? 24 : 32; + + let isFolder = false; + + if ("parentId" in item) { + this.folders.forEach((value) => { + if (value.id === item.id && value.parentId === item.parentId) + isFolder = true; + }); + } + + const { isRecycleBinFolder } = this.treeFoldersStore; + + const folderUrl = + isFolder && this.filesStore.getItemUrl(id, isFolder, false, false); + + const isEditing = + "fileStatus" in item && item.fileStatus === FileStatus.IsEditing; + + const docUrl = + !canOpenPlayer && + !isFolder && + this.filesStore.getItemUrl(id, false, needConvert); + + const href = isRecycleBinFolder + ? null + : previewUrl || (!isFolder ? docUrl : folderUrl); + + const isRoom = "roomType" in item && !!item.roomType; + + const logo = "logo" in item ? item.logo : null; + + const fileExst = "fileExst" in item ? item.fileExst : undefined; + const providerKey = "providerKey" in item ? item.providerKey : null; + const contentLength = + "contentLength" in item ? item.contentLength : undefined; + const roomType = "roomType" in item ? item.roomType : undefined; + const isArchive = "isArchive" in item ? item.isArchive : undefined; + const type = "type" in item ? item.type : undefined; + + const icon = + isRoom && logo?.medium + ? logo?.medium + : getIcon( + iconSize, + fileExst, + providerKey, + contentLength, + roomType, + isArchive, + type, + ); + + const defaultRoomIcon = isRoom + ? getIcon( + iconSize, + fileExst, + providerKey, + contentLength, + roomType, + isArchive, + type, + ) + : undefined; + + const pluginOptions = { + fileTypeName: "", + isPlugin: false, + fileTileIcon: "", + }; + + if (enablePlugins && fileItemsList) { + fileItemsList.forEach(({ value }) => { + if (value.extension === fileExst) { + if (value.fileTypeName) + pluginOptions.fileTypeName = value.fileTypeName; + pluginOptions.isPlugin = true; + if (value.fileIconTile) + pluginOptions.fileTileIcon = value.fileIconTile; + } + }); + } + + const isForm = fileExst === ".oform"; + + const canCopyPublicLink = + access === ShareAccessRights.RoomManager || + access === ShareAccessRights.None; + + return { + ...item, + + access, + daysRemaining: + "autoDelete" in item && + item.autoDelete && + getDaysRemaining(item.autoDelete), + + contentLength, + contextOptions, + + fileExst, + + icon, + defaultRoomIcon, + id, + isFolder, + logo, + + rootFolderId, + + providerKey, + canOpenPlayer, + + previewUrl, + folderUrl, + href, + isThirdPartyFolder, + isEditing, + roomType, + isRoom, + isArchive, + + thirdPartyIcon, + providerType, + + ...pluginOptions, + + type, + + isForm, + canCopyPublicLink, + }; + }); + }; + + get filesList() { + const newFolders = Array.from(this.folders.values()); + + newFolders.sort((a, b) => { + const firstValue = a.roomType ? 1 : 0; + const secondValue = b.roomType ? 1 : 0; + + return secondValue - firstValue; + }); + + const items = [...newFolders, ...Array.from(this.files.values())]; + + if (items.length > 0 && this.isEmptyPage) { + this.setIsEmptyPage(false); + } + + return this.getFilesListItems(items); + } } -export default FilesSelectionStore; +export default FilesListStore; diff --git a/packages/client/src/store/FilesSocketStore.ts b/packages/client/src/store/FilesSocketStore.ts index 0f2f709734..ce369c425e 100644 --- a/packages/client/src/store/FilesSocketStore.ts +++ b/packages/client/src/store/FilesSocketStore.ts @@ -17,6 +17,7 @@ import ClientLoadingStore from "./ClientLoadingStore"; import SelectedFolderStore from "./SelectedFolderStore"; import TreeFoldersStore from "./TreeFoldersStore"; import InfoPanelStore from "./InfoPanelStore"; +import FilesListStore from "./FilesListStore"; class FilesSocketStore { constructor( @@ -27,6 +28,8 @@ class FilesSocketStore { private infoPanelStore: Readonly, private userStore: Readonly, + private filesListStore: Readonly, + private filesStore: Readonly, ) { makeAutoObservable(this); @@ -157,6 +160,8 @@ class FilesSocketStore { if (typeof opt === "string") return; const { fileId, count } = opt; + if (!fileId) return; + const { socketSubscribers } = socketHelper; const pathParts = `FILE-${fileId}`; @@ -164,46 +169,44 @@ class FilesSocketStore { console.log(`[WS] markasnew-file ${fileId}:${count}`); - const foundIndex = - fileId && this.filesStore.files.findIndex((x) => x.id === fileId); - this.treeFoldersStore.fetchTreeFolders(); - if (foundIndex === -1 || !foundIndex) return; + const fileStatus = this.filesListStore.files.get(fileId)?.fileStatus; - this.filesStore.updateFileStatus( - foundIndex, - typeof count !== "undefined" && Number(count) > 0 - ? this.filesStore.files[foundIndex].fileStatus | FileStatus.IsNew - : this.filesStore.files[foundIndex].fileStatus & ~FileStatus.IsNew, - ); + const status = + typeof count !== "undefined" && Number(count) > 0 && !fileStatus + ? FileStatus.IsNew + : fileStatus === FileStatus.IsNew + ? FileStatus.None + : fileStatus || FileStatus.None; + + if (status !== fileStatus) + this.filesListStore.updateFileStatus(fileId, status); }); // WAIT FOR RESPONSES OF EDITING FILE socketHelper.on("s:start-edit-file", (id) => { + if (typeof id !== "string") return; + const { socketSubscribers } = socketHelper; + const pathParts = `FILE-${id}`; if (!socketSubscribers.has(pathParts)) return; - const foundIndex = this.filesStore.files.findIndex((x) => x.id === id); - if (foundIndex === -1) return; + console.log(`[WS] s:start-edit-file`, id); - console.log( - `[WS] s:start-edit-file`, - id, - this.filesStore.files[foundIndex].title, - ); + const fileStatus = this.filesListStore.files.get(id)?.fileStatus; this.filesStore.updateSelectionStatus( id, - this.filesStore.files[foundIndex].fileStatus | FileStatus.IsEditing, + fileStatus || FileStatus.IsEditing, true, ); - this.filesStore.updateFileStatus( - foundIndex, - this.filesStore.files[foundIndex].fileStatus | FileStatus.IsEditing, + this.filesListStore.updateFileStatus( + id, + fileStatus || FileStatus.IsEditing, ); }); @@ -220,6 +223,8 @@ class FilesSocketStore { }); socketHelper.on("s:stop-edit-file", (id) => { + if (typeof id !== "string") return; + const { socketSubscribers } = socketHelper; const pathParts = `FILE-${id}`; @@ -228,25 +233,18 @@ class FilesSocketStore { if (!socketSubscribers.has(pathParts)) return; - const foundIndex = this.filesStore.files.findIndex((x) => x.id === id); - if (foundIndex == -1) return; + console.log(`[WS] s:stop-edit-file`, id); - console.log( - `[WS] s:stop-edit-file`, - id, - this.filesStore.files[foundIndex].title, - ); + const currFile = this.filesListStore.files.get(id); + const fileStatus = currFile?.fileStatus; + const status = + fileStatus === FileStatus.IsEditing + ? FileStatus.None + : fileStatus || FileStatus.None; - this.filesStore.updateSelectionStatus( - id, - this.filesStore.files[foundIndex].fileStatus & ~FileStatus.IsEditing, - false, - ); + this.filesStore.updateSelectionStatus(id, status, false); - this.filesStore.updateFileStatus( - foundIndex, - this.filesStore.files[foundIndex].fileStatus & ~FileStatus.IsEditing, - ); + this.filesListStore.updateFileStatus(id, status); this.filesStore.getFileInfo(id).then((file) => { if ( @@ -258,60 +256,57 @@ class FilesSocketStore { } }); - this.filesStore.createThumbnail(this.filesStore.files[foundIndex]); + this.filesStore.createThumbnail(currFile); }); - this.filesStore.createNewFilesQueue.on( - "resolve", - this.filesStore.onResolveNewFile, - ); + this.filesStore.createNewFilesQueue.on("resolve", this.onResolveNewFile); } wsModifyFolderCreate = async (opt: TOptSocket | string) => { if (typeof opt === "string") return; if (opt?.type === "file" && opt?.id && opt.data) { - const foundIndex = this.filesStore.files.findIndex( - (x) => x.id === opt?.id, - ); + const curFile = this.filesListStore.files.get(opt.id); const file = JSON.parse(opt?.data); if (this.selectedFolderStore.id !== file.folderId) { - const movedToIndex = this.filesStore.getFolderIndex(file.folderId); - if (movedToIndex > -1) - this.filesStore.folders[movedToIndex].filesCount++; + const folder = this.filesListStore.folders.get(file.folderId); + if (folder) + this.filesListStore.folders.set(folder.id, { + ...folder, + filesCount: folder.filesCount + 1, + }); + return; } // To update a file version - if (foundIndex > -1 && !this.settingsStore.withPaging) { + if (curFile && !this.settingsStore.withPaging) { if ( - this.filesStore.files[foundIndex].version !== file.version || - this.filesStore.files[foundIndex].versionGroup !== file.versionGroup + curFile.version !== file.version || + curFile.versionGroup !== file.versionGroup ) { - this.filesStore.files[foundIndex].version = file.version; - this.filesStore.files[foundIndex].versionGroup = file.versionGroup; + curFile.version = file.version; + curFile.versionGroup = file.versionGroup; } this.filesStore.checkSelection(file); } - if (foundIndex > -1) return; + if (curFile) return; setTimeout(() => { - const foundIndex = this.filesStore.files.findIndex( - (x) => x.id === file.id, - ); - if (foundIndex > -1) { + const foundFile = this.filesListStore.files.get(file.id); + + if (foundFile) { // console.log("Skip in timeout"); return null; } this.filesStore.createNewFilesQueue.enqueue(() => { - const foundIndex = this.filesStore.files.findIndex( - (x) => x.id === file.id, - ); - if (foundIndex > -1) { + const foundedFile = this.filesListStore.files.get(file.id); + + if (foundedFile) { // console.log("Skip in queue"); return null; } @@ -319,19 +314,23 @@ class FilesSocketStore { return api.files.getFileInfo(file.id); }); }, 300); - } else if (opt?.type === "folder" && opt?.id) { - const foundIndex = this.filesStore.folders.findIndex( - (x) => x.id === opt?.id, - ); + } else if (opt?.type === "folder" && opt?.id && opt?.data) { + const curFolder = this.filesListStore.folders.get(opt.id); - if (foundIndex > -1) return; + if (curFolder) return; const folder = JSON.parse(opt?.data); - if (this.selectedFolderStore.id != folder.parentId) { - const movedToIndex = this.filesStore.getFolderIndex(folder.parentId); - if (movedToIndex > -1) - this.filesStore.folders[movedToIndex].foldersCount++; + if ( + this.selectedFolderStore.id?.toString() !== folder.parentId.toString() + ) { + const parentFolder = this.filesListStore.folders.get(folder?.parentId); + + if (parentFolder) + this.filesListStore.folders.set(parentFolder.id, { + ...parentFolder, + filesCount: folder.filesCount + 1, + }); } if ( @@ -340,20 +339,23 @@ class FilesSocketStore { folder.createdBy.id === this.userStore?.user?.id && this.filesStore.roomCreated) ) { - return (this.filesStore.roomCreated = false); + return this.filesStore.setRoomCreated(false); } const folderInfo = await api.files.getFolderInfo(folder.id); console.log("[WS] create new folder", folderInfo.id, folderInfo.title); - const newFolders = [folderInfo, ...this.filesStore.folders]; + const newFolders = new Map([ + [folder.id, folderInfo], + ...this.filesListStore.folders.entries(), + ]); if ( - newFolders.length > this.filesStore.filter.pageCount && + this.filesListStore.folders.size > this.filesStore.filter.pageCount && this.settingsStore.withPaging ) { - newFolders.pop(); // Remove last + this.filesListStore.removeFolder(Array.from(newFolders.keys()).pop()); // Remove last } const newFilter = this.filesStore.filter; @@ -361,7 +363,7 @@ class FilesSocketStore { runInAction(() => { this.filesStore.setFilter(newFilter); - this.filesStore.setFolders(newFolders); + this.filesListStore.addFolder(folderInfo); }); } }; @@ -383,7 +385,7 @@ class FilesSocketStore { api.files .getFolderInfo(folder.id) - .then(this.filesStore.setFolder) + .then(this.filesListStore.setFolder) .catch(() => { // console.log("Folder deleted") }); diff --git a/packages/client/src/store/FilesStore.js b/packages/client/src/store/FilesStore.js index 7c2dc39993..d65346243a 100644 --- a/packages/client/src/store/FilesStore.js +++ b/packages/client/src/store/FilesStore.js @@ -24,7 +24,6 @@ // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode -import axios from "axios"; import { makeAutoObservable, runInAction } from "mobx"; import api from "@docspace/shared/api"; @@ -73,13 +72,6 @@ import Queue from "queue-promise"; const { FilesFilter, RoomsFilter } = api; const storageViewAs = localStorage.getItem("viewAs"); -let requestCounter = 0; - -const NotFoundHttpCode = 404; -const ForbiddenHttpCode = 403; -const PaymentRequiredHttpCode = 402; -const UnauthorizedHttpCode = 401; - const THUMBNAILS_CACHE = 500; let timerId; @@ -158,7 +150,6 @@ class FilesStore { filesIsLoading = false; isEmptyPage = true; - isLoadedFetchFiles = false; tempActionFilesIds = []; tempActionFoldersIds = []; @@ -336,13 +327,6 @@ class FilesStore { this.activeFiles = items; }; - clearFiles = () => { - this.setFolders([]); - this.setFiles([]); - - this.selectedFolderStore.setSelectedFolder(null); - }; - mappingActiveItems = (items, destFolderId) => { const arrayFormation = items.map((item) => typeof item === "object" @@ -392,10 +376,6 @@ class FilesStore { this.startDrag = startDrag; }; - setIsEmptyPage = (isEmptyPage) => { - this.isEmptyPage = isEmptyPage; - }; - setIsLoadedEmptyPage = (isLoadedEmptyPage) => { this.isLoadedEmptyPage = isLoadedEmptyPage; }; @@ -510,98 +490,6 @@ class FilesStore { this.setBufferSelection(null); }; - setFiles = (files) => { - const { socketHelper } = this.settingsStore; - - if (files.length === 0 && this.files.length === 0) return; - - if (this.files?.length > 0) { - socketHelper.emit({ - command: "unsubscribe", - data: { - roomParts: this.files.map((f) => `FILE-${f.id}`), - individual: true, - }, - }); - } - - this.files = files; - - if (this.files?.length > 0) { - socketHelper.emit({ - command: "subscribe", - data: { - roomParts: this.files.map((f) => `FILE-${f.id}`), - individual: true, - }, - }); - - // this.files?.forEach((file) => - // console.log("[WS] subscribe to file's changes", file.id, file.title) - // ); - } - - this.createThumbnails(); - }; - - setFolders = (folders) => { - const { socketHelper } = this.settingsStore; - if (folders.length === 0 && this.folders.length === 0) return; - - if (this.folders?.length > 0) { - const ids = this.folders - .map((f) => { - if (this.selectedFolderStore.id === f.id) return ""; - return `DIR-${f.id}`; - }) - .filter((id) => id); - - if (ids.length) - socketHelper.emit({ - command: "unsubscribe", - data: { - roomParts: ids, - individual: true, - }, - }); - } - - this.folders = folders; - - if (this.folders?.length > 0) { - socketHelper.emit({ - command: "subscribe", - data: { - roomParts: this.folders.map((f) => `DIR-${f.id}`), - individual: true, - }, - }); - } - }; - - getFileIndex = (id) => { - const index = this.files.findIndex((x) => x.id === id); - return index; - }; - - updateFileStatus = (index, status) => { - if (index < 0) return; - - this.files[index].fileStatus = status; - }; - updateRoomMute = (index, status) => { - if (index < 0) return; - - this.folders[index].mute = status; - }; - setFile = (file) => { - const index = this.files.findIndex((x) => x.id === file.id); - if (index !== -1) { - this.files[index] = file; - this.createThumbnail(file); - } - }; - updateSelection = (id) => { const indexFileList = this.filesList.findIndex( (filelist) => filelist.id === id, @@ -620,23 +508,6 @@ class FilesStore { } }; - getFolderIndex = (id) => { - const index = this.folders.findIndex((x) => x.id === id); - return index; - }; - - updateFolder = (index, folder) => { - if (index !== -1) this.folders[index] = folder; - - this.updateSelection(folder.id); - }; - - setFolder = (folder) => { - const index = this.getFolderIndex(folder.id); - - this.updateFolder(index, folder); - }; - getFilesChecked = (file, selected) => { if (!file.parentId) { if (this.activeFiles.find((elem) => elem.id === file.id)) return false; @@ -812,10 +683,6 @@ class FilesStore { this.bufferSelection = bufferSelection; }; - setIsLoadedFetchFiles = (isLoadedFetchFiles) => { - this.isLoadedFetchFiles = isLoadedFetchFiles; - }; - //TODO: FILTER setFilesFilter = (filter) => { if (!this.publicRoomStore.isPublicRoom) { @@ -939,517 +806,6 @@ class FilesStore { return res; }; - abortAllFetch = () => { - this.filesController.abort(); - this.roomsController.abort(); - this.filesController = new AbortController(); - this.roomsController = new AbortController(); - }; - - fetchFiles = ( - folderId, - filter, - clearFilter = true, - withSubfolders = false, - clearSelection = true, - ) => { - const { setSelectedNode } = this.treeFoldersStore; - - if (this.clientLoadingStore.isLoading) { - this.abortAllFetch(); - } - - const filterData = filter ? filter.clone() : FilesFilter.getDefault(); - filterData.folder = folderId; - - if (folderId === "@my" && this.userStore.user.isVisitor) { - const url = getCategoryUrl(CategoryType.Shared); - return window.DocSpace.navigate( - `${url}?${RoomsFilter.getDefault().toUrlParams()}`, - ); - } - - this.setIsErrorRoomNotAvailable(false); - this.setIsLoadedFetchFiles(false); - - const filterStorageItem = - this.userStore.user?.id && - localStorage.getItem(`UserFilter=${this.userStore.user.id}`); - - if (filterStorageItem && !filter) { - const splitFilter = filterStorageItem.split(","); - - filterData.sortBy = splitFilter[0]; - filterData.pageCount = +splitFilter[1]; - filterData.sortOrder = splitFilter[2]; - } - - if (!this.settingsStore.withPaging) { - filterData.page = 0; - filterData.pageCount = 100; - } - - const defaultFilter = FilesFilter.getDefault(); - - const { filterType, searchInContent } = filterData; - - if (typeof filterData.withSubfolders !== "boolean") - filterData.withSubfolders = defaultFilter.withSubfolders; - - if (typeof searchInContent !== "boolean") - filterData.searchInContent = defaultFilter.searchInContent; - - if (!Object.keys(FilterType).find((key) => FilterType[key] === filterType)) - filterData.filterType = defaultFilter.filterType; - - setSelectedNode([folderId + ""]); - - return api.files - .getFolder(folderId, filterData, this.filesController.signal) - .then(async (data) => { - let newTotal = data.total; - - // fixed row loader if total and items length is different - const itemsLength = data.folders.length + data.files.length; - if (itemsLength < filterData.pageCount) { - newTotal = - filterData.page > 0 - ? itemsLength + this.files.length + this.folders.length - : itemsLength; - } - - filterData.total = newTotal; - - if ( - (data.current.roomType === RoomsType.PublicRoom || - data.current.roomType === RoomsType.FormRoom || - data.current.roomType === RoomsType.CustomRoom) && - !this.publicRoomStore.isPublicRoom - ) { - await this.publicRoomStore.getExternalLinks(data.current.id); - } - - if (newTotal > 0) { - const lastPage = filterData.getLastPage(); - - if (filterData.page > lastPage) { - filterData.page = lastPage; - - return this.fetchFiles( - folderId, - filterData, - clearFilter, - withSubfolders, - ); - } - } - - runInAction(() => { - if (!this.publicRoomStore.isPublicRoom) { - this.categoryType = getCategoryTypeByFolderType( - data.current.rootFolderType, - data.current.parentId, - ); - } - }); - - if (this.isPreview) { - //save filter for after closing preview change url - this.setTempFilter(filterData); - } else { - this.setFilesFilter(filterData); //TODO: FILTER - } - - const isPrivacyFolder = - data.current.rootFolderType === FolderType.Privacy; - - const navigationPath = await Promise.all( - data.pathParts.map(async (folder, idx) => { - const { Rooms, Archive } = FolderType; - - let folderId = folder.id; - - // if ( - // data.current.providerKey && - // data.current.rootFolderType === Rooms && - // this.treeFoldersStore.myRoomsId - // ) { - // folderId = this.treeFoldersStore.myRoomsId; - // } - - const isCurrentFolder = data.current.id == folderId; - - const folderInfo = isCurrentFolder - ? data.current - : { ...folder, id: folderId }; - - const { title, roomType } = folderInfo; - - const isRootRoom = - idx === 0 && - (data.current.rootFolderType === Rooms || - data.current.rootFolderType === Archive); - - let shared, canCopyPublicLink; - if (idx === 1) { - let room = data.current; - - if (!isCurrentFolder) { - room = await api.files.getFolderInfo(folderId); - shared = room.shared; - canCopyPublicLink = - room.access === ShareAccessRights.RoomManager || - room.access === ShareAccessRights.None; - - room.canCopyPublicLink = canCopyPublicLink; - this.infoPanelStore.setInfoPanelRoom(room); - } - - const { mute } = room; - - runInAction(() => { - this.isMuteCurrentRoomNotifications = mute; - }); - } - - return { - id: folderId, - title, - isRoom: !!roomType, - roomType, - isRootRoom, - shared, - canCopyPublicLink, - }; - }), - ).then((res) => { - return res - .filter((item, index) => { - return index !== res.length - 1; - }) - .reverse(); - }); - this.selectedFolderStore.setSelectedFolder({ - folders: data.folders, - ...data.current, - inRoom: !!data.current.inRoom, - isRoom: !!data.current.roomType, - pathParts: data.pathParts, - navigationPath, - ...{ new: data.new }, - // type, - }); - - runInAction(() => { - const isEmptyList = [...data.folders, ...data.files].length === 0; - - if (filter && isEmptyList) { - const { - authorType, - roomId, - search, - withSubfolders, - filterType, - searchInContent, - } = filter; - const isFiltered = - authorType || - roomId || - search || - withSubfolders || - filterType || - searchInContent; - - if (isFiltered) { - this.setIsEmptyPage(false); - } else { - this.setIsEmptyPage(isEmptyList); - } - } else { - this.setIsEmptyPage(isEmptyList); - } - this.setFolders(isPrivacyFolder && !isDesktop() ? [] : data.folders); - this.setFiles(isPrivacyFolder && !isDesktop() ? [] : data.files); - }); - - if (clearFilter) { - if (clearSelection) { - // Find not processed - const tempSelection = this.selection.filter( - (f) => !this.activeFiles.find((elem) => elem.id === f.id), - ); - const tempBuffer = - this.bufferSelection && - this.activeFiles.find( - (elem) => elem.id === this.bufferSelection.id, - ) == null - ? this.bufferSelection - : null; - - // console.log({ tempSelection, tempBuffer }); - - // Clear all selections - this.setSelected("close"); - - // TODO: see bug 63479 - if (this.selectedFolderStore?.id === folderId) { - // Restore not processed - tempSelection.length && this.setSelection(tempSelection); - tempBuffer && this.setBufferSelection(tempBuffer); - } - } - } - - this.clientLoadingStore.setIsSectionHeaderLoading(false); - - const selectedFolder = { - selectedFolder: { ...this.selectedFolderStore }, - }; - - if (this.createdItem) { - const newItem = this.filesList.find( - (item) => item.id === this.createdItem.id, - ); - - if (newItem) { - this.setBufferSelection(newItem); - this.setScrollToItem({ - id: newItem.id, - type: this.createdItem.type, - }); - } - - this.setCreatedItem(null); - } - - if (isPublicRoom()) { - return Promise.resolve(data); - } else { - return Promise.resolve(selectedFolder); - } - }) - .catch((err) => { - if (err?.response?.status === 402) - this.currentTariffStatusStore.setPortalTariff(); - - const isThirdPartyError = isNaN(+folderId); - - if (requestCounter > 0 && !isThirdPartyError) return; - - requestCounter++; - const isUserError = [ - NotFoundHttpCode, - ForbiddenHttpCode, - PaymentRequiredHttpCode, - UnauthorizedHttpCode, - ].includes(err?.response?.status); - - if (isUserError && !isThirdPartyError) { - this.setIsErrorRoomNotAvailable(true); - } else { - if (axios.isCancel(err)) { - console.log("Request canceled", err.message); - } else { - toastr.error(err); - if (isThirdPartyError) { - const userId = this.userStore?.user?.id; - const searchArea = window.DocSpace.location.pathname.includes( - "shared", - ) - ? RoomSearchArea.Active - : RoomSearchArea.Archive; - - return window.DocSpace.navigate( - `${window.DocSpace.location.pathname}?${RoomsFilter.getDefault(userId, searchArea).toUrlParams(userId, true)}`, - ); - } - } - } - }) - .finally(() => { - this.setIsLoadedFetchFiles(true); - - if (window?.DocSpace?.location?.state?.highlightFileId) { - this.setHighlightFile({ - highlightFileId: window.DocSpace.location.state.highlightFileId, - isFileHasExst: window.DocSpace.location.state.isFileHasExst, - }); - } - }); - }; - - fetchRooms = ( - folderId, - filter, - clearFilter = true, - withSubfolders = false, - clearSelection = true, - withFilterLocalStorage = false, - ) => { - const { setSelectedNode, roomsFolderId } = this.treeFoldersStore; - - if (this.clientLoadingStore.isLoading) { - this.abortAllFetch(); - } - - const filterData = !!filter - ? filter.clone() - : RoomsFilter.getDefault(this.userStore.user?.id); - - if (!this.settingsStore.withPaging) { - const isCustomCountPage = - filter && filter.pageCount !== 100 && filter.pageCount !== 25; - - if (!isCustomCountPage) { - filterData.page = 0; - filterData.pageCount = 100; - } - } - - if (folderId) setSelectedNode([folderId + ""]); - - const defaultFilter = RoomsFilter.getDefault(); - - const { provider, quotaFilter, type } = filterData; - - if (!ROOMS_PROVIDER_TYPE_NAME[provider]) - filterData.provider = defaultFilter.provider; - - if ( - quotaFilter && - quotaFilter !== FilterKeys.customQuota && - quotaFilter !== FilterKeys.defaultQuota - ) - filterData.quotaFilter = defaultFilter.quotaFilter; - - if (type && !RoomsType[type]) filterData.type = defaultFilter.type; - - const request = () => - api.rooms - .getRooms(filterData, this.roomsController.signal) - .then(async (data) => { - if (!folderId) setSelectedNode([data.current.id + ""]); - - filterData.total = data.total; - - if (data.total > 0) { - const lastPage = filterData.getLastPage(); - - if (filterData.page > lastPage) { - filterData.page = lastPage; - - return this.fetchRooms( - folderId, - filterData, - undefined, - undefined, - undefined, - true, - ); - } - } - - runInAction(() => { - this.categoryType = getCategoryTypeByFolderType( - data.current.rootFolderType, - data.current.parentId, - ); - }); - - this.setRoomsFilter(filterData); - - runInAction(() => { - const isEmptyList = data.folders.length === 0; - if (filter && isEmptyList) { - const { - subjectId, - filterValue, - type, - withSubfolders: withRoomsSubfolders, - searchInContent: searchInContentRooms, - tags, - withoutTags, - quotaFilter, - provider, - } = filter; - - const isFiltered = - subjectId || - filterValue || - type || - provider || - withRoomsSubfolders || - searchInContentRooms || - tags || - withoutTags || - quotaFilter; - - if (!!isFiltered) { - this.setIsEmptyPage(false); - } else { - this.setIsEmptyPage(isEmptyList); - } - } else { - this.setIsEmptyPage(isEmptyList); - } - - this.setFolders(data.folders); - this.setFiles([]); - }); - - if (clearFilter) { - if (clearSelection) { - this.setSelected("close"); - } - } - - this.infoPanelStore.setInfoPanelRoom(null); - this.selectedFolderStore.setSelectedFolder({ - folders: data.folders, - ...data.current, - pathParts: data.pathParts, - navigationPath: [], - ...{ new: data.new }, - }); - - this.clientLoadingStore.setIsSectionHeaderLoading(false); - - const selectedFolder = { - selectedFolder: { ...this.selectedFolderStore }, - }; - - if (this.createdItem) { - const newItem = this.filesList.find( - (item) => item.id === this.createdItem.id, - ); - - if (newItem) { - this.setBufferSelection(newItem); - this.setScrollToItem({ - id: newItem.id, - type: this.createdItem.type, - }); - } - - this.setCreatedItem(null); - } - this.setIsErrorRoomNotAvailable(false); - return Promise.resolve(selectedFolder); - }) - .catch((err) => { - if (err?.response?.status === 402) - this.currentTariffStatusStore.setPortalTariff(); - - if (axios.isCancel(err)) { - console.log("Request canceled", err.message); - } else { - toastr.error(err); - } - }); - - return request(); - }; - setCustomRoomQuota = async (quotaSize, itemsIDs, inRoom = false, filter) => { const rooms = await api.rooms.setCustomRoomQuota(itemsIDs, +quotaSize); @@ -2202,6 +1558,10 @@ class FilesStore { return api.files.createFolder(parentFolderId, title); } + setRoomCreated = (value) => { + this.roomCreated = value; + }; + createRoom = (roomParams) => { this.roomCreated = true; return api.rooms.createRoom(roomParams); @@ -2727,267 +2087,6 @@ class FilesStore { } }; - getFilesListItems = (items) => { - const { fileItemsList } = this.pluginStore; - const { enablePlugins } = this.settingsStore; - const { getIcon } = this.filesSettingsStore; - - return items.map((item) => { - const { - availableExternalRights, - access, - autoDelete, - originTitle, - comment, - contentLength, - created, - createdBy, - encrypted, - fileExst, - filesCount, - fileStatus, - fileType, - folderId, - foldersCount, - id, - logo, - locked, - originId, - originFolderId, - originRoomId, - originRoomTitle, - parentId, - pureContentLength, - rootFolderType, - rootFolderId, - shared, - title, - type, - hasDraft, - updated, - updatedBy, - version, - versionGroup, - viewUrl, - webUrl, - providerKey, - thumbnailUrl, - thumbnailStatus, - canShare, - canEdit, - roomType, - isArchive, - tags, - pinned, - security, - viewAccessibility, - mute, - inRoom, - requestToken, - lastOpened, - quotaLimit, - usedSpace, - isCustomQuota, - providerId, - startFilling, - draftLocation, - } = item; - - const thirdPartyIcon = this.thirdPartyStore.getThirdPartyIcon( - item.providerKey, - "small", - ); - - const providerType = - RoomsProviderType[ - Object.keys(RoomsProviderType).find((key) => key === item.providerKey) - ]; - - const canOpenPlayer = - item.viewAccessibility?.ImageView || item.viewAccessibility?.MediaView; - - const previewUrl = canOpenPlayer - ? this.getItemUrl(id, false, needConvert, canOpenPlayer) - : null; - - const contextOptions = this.getFilesContextOptions(item); - const isThirdPartyFolder = providerKey && id === rootFolderId; - - const iconSize = this.viewAs === "table" ? 24 : 32; - - let isFolder = false; - this.folders.map((x) => { - if (x.id === item.id && x.parentId === item.parentId) isFolder = true; - }); - - const { isRecycleBinFolder } = this.treeFoldersStore; - - const folderUrl = isFolder && this.getItemUrl(id, isFolder, false, false); - - const needConvert = item.viewAccessibility?.MustConvert; - const isEditing = - (item.fileStatus & FileStatus.IsEditing) === FileStatus.IsEditing; - - const docUrl = - !canOpenPlayer && !isFolder && this.getItemUrl(id, false, needConvert); - - const href = isRecycleBinFolder - ? null - : previewUrl - ? previewUrl - : !isFolder - ? docUrl - : folderUrl; - - const isRoom = !!roomType; - - const icon = - isRoom && logo?.medium - ? logo?.medium - : getIcon( - iconSize, - fileExst, - providerKey, - contentLength, - roomType, - isArchive, - type, - ); - - const defaultRoomIcon = isRoom - ? getIcon( - iconSize, - fileExst, - providerKey, - contentLength, - roomType, - isArchive, - type, - ) - : undefined; - - const pluginOptions = {}; - - if (enablePlugins && fileItemsList) { - fileItemsList.forEach(({ key, value }) => { - if (value.extension === fileExst) { - if (value.fileTypeName) - pluginOptions.fileTypeName = value.fileTypeName; - pluginOptions.isPlugin = true; - if (value.fileIconTile) - pluginOptions.fileTileIcon = value.fileIconTile; - } - }); - } - - const isForm = fileExst === ".oform"; - - const canCopyPublicLink = - access === ShareAccessRights.RoomManager || - access === ShareAccessRights.None; - - return { - availableExternalRights, - access, - daysRemaining: autoDelete && getDaysRemaining(autoDelete), - originTitle, - //checked, - comment, - contentLength, - contextOptions, - created, - createdBy, - encrypted, - fileExst, - filesCount, - fileStatus, - fileType, - folderId, - foldersCount, - icon, - defaultRoomIcon, - id, - isFolder, - logo, - locked, - new: item.new, - mute, - parentId, - pureContentLength, - rootFolderType, - rootFolderId, - //selectedItem, - shared, - title, - updated, - updatedBy, - version, - versionGroup, - viewUrl, - webUrl, - providerKey, - canOpenPlayer, - //canShare, - canShare, - canEdit, - thumbnailUrl, - thumbnailStatus, - originId, - originFolderId, - originRoomId, - originRoomTitle, - previewUrl, - folderUrl, - href, - isThirdPartyFolder, - isEditing, - roomType, - isRoom, - isArchive, - tags, - pinned, - thirdPartyIcon, - providerType, - security, - viewAccessibility, - ...pluginOptions, - inRoom, - type, - hasDraft, - isForm, - canCopyPublicLink, - requestToken, - lastOpened, - quotaLimit, - usedSpace, - isCustomQuota, - providerId, - startFilling, - draftLocation, - }; - }); - }; - get filesList() { - //return [...this.folders, ...this.files]; - - const newFolders = [...this.folders]; - - newFolders.sort((a, b) => { - const firstValue = a.roomType ? 1 : 0; - const secondValue = b.roomType ? 1 : 0; - - return secondValue - firstValue; - }); - - const items = [...newFolders, ...this.files]; - - if (items.length > 0 && this.isEmptyPage) { - this.setIsEmptyPage(false); - } - - return this.getFilesListItems(items); - } - get cbMenuItems() { const { isDocument, isPresentation, isSpreadsheet, isArchive } = this.filesSettingsStore; @@ -3435,18 +2534,6 @@ class FilesStore { unpinRoom = (id) => api.rooms.unpinRoom(id); - getFileInfo = async (id) => { - const fileInfo = await api.files.getFileInfo(id); - this.setFile(fileInfo); - return fileInfo; - }; - - getFolderInfo = async (id) => { - const folderInfo = await api.files.getFolderInfo(id); - this.setFolder(folderInfo); - return folderInfo; - }; - openDocEditor = (id, preview = false, shareKey = null, editForm = false) => { const { openOnNewPage } = this.filesSettingsStore; const foundIndex = this.files.findIndex((x) => x.id === id); @@ -3481,7 +2568,7 @@ class FilesStore { window.open(url, openOnNewPage ? "_blank" : "_self"); }; - createThumbnails = async (files = null) => { + createThumbnails = async (files) => { if ((this.viewAs !== "tile" || !this.files) && !files) return; const currentFiles = files || this.files; diff --git a/packages/client/src/store/SelectedFolderStore.ts b/packages/client/src/store/SelectedFolderStore.ts index 7ea052ed77..a11abdd1b6 100644 --- a/packages/client/src/store/SelectedFolderStore.ts +++ b/packages/client/src/store/SelectedFolderStore.ts @@ -45,13 +45,13 @@ import { TLogo, TRoomSecurity } from "@docspace/shared/api/rooms/types"; import { setDocumentTitle } from "../helpers/utils"; export type TNavigationPath = { - id: number; + id: number | string; title: string; isRoom: boolean; - roomType: RoomsType; - isRootRoom: boolean; - shared: boolean; - canCopyPublicLink: boolean; + roomType?: RoomsType; + isRootRoom?: boolean; + shared?: boolean; + canCopyPublicLink?: boolean; }; type ExcludeTypes = SettingsStore | Function; @@ -115,6 +115,8 @@ class SelectedFolderStore { isRoom = false; + inRoom = false; + isArchive = false; logo: TLogo | null = null; @@ -127,9 +129,7 @@ class SelectedFolderStore { security: TFolderSecurity | TRoomSecurity | null = null; - type = null; - - inRoom = false; + type: FolderType | null = null; isFolder = true; diff --git a/packages/client/src/store/ThirdPartyStore.js b/packages/client/src/store/ThirdPartyStore.js index 6d0676fd18..bc649c60d1 100644 --- a/packages/client/src/store/ThirdPartyStore.js +++ b/packages/client/src/store/ThirdPartyStore.js @@ -297,4 +297,4 @@ class ThirdPartyStore { } } -export default new ThirdPartyStore(); +export default ThirdPartyStore; diff --git a/packages/client/src/store/index.js b/packages/client/src/store/index.js index 0c572ed5e7..aa5788c059 100644 --- a/packages/client/src/store/index.js +++ b/packages/client/src/store/index.js @@ -50,7 +50,7 @@ import LdapFormStore from "./LdapFormStore"; import FilesStore from "./FilesStore"; import SelectedFolderStore from "./SelectedFolderStore"; import TreeFoldersStore from "./TreeFoldersStore"; -import thirdPartyStore from "./ThirdPartyStore"; +import ThirdPartyStore from "./ThirdPartyStore"; import FilesSettingsStore from "./FilesSettingsStore"; import FilesActionsStore from "./FilesActionsStore"; import MediaViewerDataStore from "./MediaViewerDataStore"; @@ -84,6 +84,8 @@ import CampaignsStore from "./CampaignsStore"; import OAuthStore from "./OAuthStore"; import FilesSocketStore from "./FilesSocketStore"; +const thirdPartyStore = new ThirdPartyStore(); + const oauthStore = new OAuthStore(userStore); const selectedFolderStore = new SelectedFolderStore(settingsStore); diff --git a/packages/shared/api/files/filter.ts b/packages/shared/api/files/filter.ts index a02b32e540..377a8c2855 100644 --- a/packages/shared/api/files/filter.ts +++ b/packages/shared/api/files/filter.ts @@ -99,7 +99,7 @@ class FilesFilter { selectedItem: { key?: string | number }; - folder: string; + folder: string | number; searchInContent: boolean | null; diff --git a/packages/shared/enums/index.ts b/packages/shared/enums/index.ts index 2090aaaceb..2963975138 100644 --- a/packages/shared/enums/index.ts +++ b/packages/shared/enums/index.ts @@ -129,7 +129,7 @@ export const enum FilterSubject { * Enum for filter type. * @readonly */ -export const enum FilterType { +export enum FilterType { None = 0, FilesOnly = 1, FoldersOnly = 2, @@ -174,7 +174,7 @@ export const enum FileType { * Enum for room provider type. * @readonly */ -export const enum RoomsProviderType { +export enum RoomsProviderType { Box = 1, DropboxV2 = 2, GoogleDrive = 3, @@ -227,7 +227,7 @@ export const enum PageType { * Enum for root folders type. * @readonly */ -export const enum FolderType { +export enum FolderType { DEFAULT = 0, COMMON = 1, BUNCH = 2, diff --git a/packages/shared/types/index.ts b/packages/shared/types/index.ts index ea56f951f2..c4af38c862 100644 --- a/packages/shared/types/index.ts +++ b/packages/shared/types/index.ts @@ -24,6 +24,8 @@ // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode +import { Location, NavigateFunction } from "react-router"; + import { TGetColorTheme, TSettings, @@ -99,7 +101,8 @@ declare global { timezone: string; snackbar?: {}; DocSpace: { - navigate: (path: string, state?: { [key: string]: unknown }) => void; + navigate: NavigateFunction; + location: Location; }; ClientConfig?: { pdfViewerUrl: string; diff --git a/packages/shared/utils/socket.ts b/packages/shared/utils/socket.ts index 249226ec35..f6bcac6954 100644 --- a/packages/shared/utils/socket.ts +++ b/packages/shared/utils/socket.ts @@ -62,7 +62,7 @@ export type TOptSocket = { export type TEmit = { command: string; - data: { roomParts: string | []; individual?: boolean }; + data: { roomParts: string | string[]; individual?: boolean }; room?: null | boolean; };