diff --git a/packages/client/src/Shell.jsx b/packages/client/src/Shell.jsx index bf0165745d..9523d6901a 100644 --- a/packages/client/src/Shell.jsx +++ b/packages/client/src/Shell.jsx @@ -43,6 +43,7 @@ import { DeviceType, IndexedDBStores } from "@docspace/shared/enums"; import indexedDbHelper from "@docspace/shared/utils/indexedDBHelper"; import { useThemeDetector } from "@docspace/shared/hooks/useThemeDetector"; import { sendToastReport } from "@docspace/shared/utils/crashReport"; +import { combineUrl } from "@docspace/shared/utils/combineUrl"; import config from "PACKAGE_FILE"; @@ -77,6 +78,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => { userTheme, //user, userId, + userLoginEventId, currentDeviceType, timezone, showArticleLoader, @@ -134,6 +136,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => { command: "subscribe", data: { roomParts: "backup-restore" }, }); + socketHelper.on("restore-backup", () => { getRestoreProgress() .then((response) => { @@ -159,7 +162,27 @@ const Shell = ({ items = [], page = "home", ...rest }) => { command: "subscribe", data: { roomParts: "QUOTA", individual: true }, }); - }, [socketHelper]); + + socketHelper.emit({ + command: "subscribe", + data: { roomParts: userId }, + }); + + socketHelper.on("s:logout-session", (loginEventId) => { + console.log(`[WS] "logout-session"`, loginEventId, userLoginEventId); + + if (userLoginEventId === loginEventId || loginEventId === 0) { + window.location.replace( + combineUrl(window.ClientConfig?.proxy?.url, "/login"), + ); + } + }); + }, [ + socketHelper, + userLoginEventId, + setPreparationPortalDialogVisible, + userId, + ]); const { t, ready } = useTranslation(["Common"]); //TODO: if enable banner ["Common", "SmartBanner"] @@ -533,6 +556,7 @@ const ShellWrapper = inject( setSnackbarExist, userTheme: isFrame ? frameConfig?.theme : userTheme, userId: userStore?.user?.id, + userLoginEventId: userStore?.user?.loginEventId, currentDeviceType, showArticleLoader: clientLoadingStore.showArticleLoader, setPortalTariff, diff --git a/packages/client/src/components/EmptyContainer/sub-components/EmptyViewContainer/EmptyViewContainer.helpers.tsx b/packages/client/src/components/EmptyContainer/sub-components/EmptyViewContainer/EmptyViewContainer.helpers.tsx index 5ecece938a..a0d86182e1 100644 --- a/packages/client/src/components/EmptyContainer/sub-components/EmptyViewContainer/EmptyViewContainer.helpers.tsx +++ b/packages/client/src/components/EmptyContainer/sub-components/EmptyViewContainer/EmptyViewContainer.helpers.tsx @@ -75,19 +75,16 @@ export const getDescription = ( if (isFolder) { return match([parentRoomType, folderType, access]) .with([P._, FolderType.Done, P._], () => - t("Files:EmptyFormFolderDoneHeaderText"), + t("Files:EmptyFormFolderDoneDescriptionText"), ) - .with([P._, FolderType.InProgress, P._], () => - t("Files:EmptyFormFolderProgressHeaderText"), + t("Files:EmptyFormFolderProgressDescriptionText"), ) - .with( - [ - P._, - P.union(FolderType.SubFolderDone, FolderType.SubFolderInProgress), - P._, - ], - () => t("Files:EmptyFormSubFolderHeaderText"), + .with([P._, FolderType.SubFolderDone, P._], () => + t("Files:EmptyFormSubFolderDoneDescriptionText"), + ) + .with([P._, FolderType.SubFolderInProgress, P._], () => + t("Files:EmptyFormSubFolderProgressDescriptionText"), ) .with( [ @@ -130,16 +127,19 @@ export const getTitle = ( if (isFolder) { return match([parentRoomType, folderType, access]) .with([P._, FolderType.Done, P._], () => - t("Files:EmptyFormFolderDoneDescriptionText"), + t("Files:EmptyFormFolderDoneHeaderText"), ) + .with([P._, FolderType.InProgress, P._], () => - t("Files:EmptyFormFolderProgressDescriptionText"), + t("Files:EmptyFormFolderProgressHeaderText"), ) - .with([P._, FolderType.SubFolderDone, P._], () => - t("Files:EmptyFormSubFolderDoneDescriptionText"), - ) - .with([P._, FolderType.SubFolderInProgress, P._], () => - t("Files:EmptyFormSubFolderProgressDescriptionText"), + .with( + [ + P._, + P.union(FolderType.SubFolderDone, FolderType.SubFolderInProgress), + P._, + ], + () => t("Files:EmptyFormSubFolderHeaderText"), ) .with( [ diff --git a/packages/client/src/helpers/plugins/enums.ts b/packages/client/src/helpers/plugins/enums.ts index a6c59382e7..cd68835609 100644 --- a/packages/client/src/helpers/plugins/enums.ts +++ b/packages/client/src/helpers/plugins/enums.ts @@ -110,7 +110,7 @@ export const enum PluginComponents { export const enum PluginUsersType { owner = "Owner", - docSpaceAdmin = "DocspaceAdmin", + docSpaceAdmin = "DocSpaceAdmin", roomAdmin = "RoomAdmin", collaborator = "Collaborator", user = "User", diff --git a/packages/client/src/pages/Bonus/sub-components/OfficialDocumentation.js b/packages/client/src/pages/Bonus/sub-components/OfficialDocumentation.js index 77ce2661ab..8e77e20437 100644 --- a/packages/client/src/pages/Bonus/sub-components/OfficialDocumentation.js +++ b/packages/client/src/pages/Bonus/sub-components/OfficialDocumentation.js @@ -37,9 +37,9 @@ const OfficialDocumentation = ({ dataBackupUrl }) => { const { t } = useTranslation("PaymentsEnterprise"); const dockerLink = - "https://helpcenter.onlyoffice.com/installation/docspace-enterprise-install-docker.aspx"; + "https://helpcenter.onlyoffice.com/installation/docspace-enterprise-install-script.aspx"; const linuxDocker = - "https://helpcenter.onlyoffice.com/installation/docspace-enterprise-install-linux.aspx"; + "https://helpcenter.onlyoffice.com/installation/docspace-enterprise-install-script.aspx"; const windowsDocker = "https://helpcenter.onlyoffice.com/installation/docspace-enterprise-install-windows.aspx"; diff --git a/packages/client/src/pages/Confirm/sub-components/LanguageCombobox.tsx b/packages/client/src/pages/Confirm/sub-components/LanguageCombobox.tsx index e69c84a573..0379f3104c 100644 --- a/packages/client/src/pages/Confirm/sub-components/LanguageCombobox.tsx +++ b/packages/client/src/pages/Confirm/sub-components/LanguageCombobox.tsx @@ -55,7 +55,7 @@ const LanguageComboboxWrapper = (props: TLanguageCombobox) => { onSelectLanguage={onLanguageSelect} cultures={cultures} selectedCulture={currentCultureName} - withBorder={false} + withBorder={!isMobileView} /> ); }; diff --git a/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableHeader.js b/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableHeader.js index d7f14b4263..9a9e0001c9 100644 --- a/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableHeader.js +++ b/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableHeader.js @@ -141,25 +141,17 @@ class PeopleTableHeader extends React.Component { const { filter, setFilter, setIsLoading, navigate, location } = this.props; const newFilter = filter.clone(); - if (newFilter.sortBy === sortBy && sortBy !== "AZ") { + if ( + newFilter.sortBy === sortBy || + (sortBy === "AZ" && newFilter.sortBy === "firstname") + ) { newFilter.sortOrder = newFilter.sortOrder === "ascending" ? "descending" : "ascending"; } else { newFilter.sortBy = sortBy; if (sortBy === "AZ") { - if ( - newFilter.sortBy !== "lastname" && - newFilter.sortBy !== "firstname" - ) { - newFilter.sortBy = "firstname"; - } else if (newFilter.sortBy === "lastname") { - newFilter.sortBy = "firstname"; - } else { - newFilter.sortBy = "lastname"; - } - newFilter.sortOrder = - newFilter.sortOrder === "ascending" ? "descending" : "ascending"; + newFilter.sortBy = "firstname"; } } diff --git a/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableRow.js b/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableRow.js index f8ba2d2ee6..45c74f06fc 100644 --- a/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableRow.js +++ b/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableRow.js @@ -605,6 +605,7 @@ const PeopleTableRow = (props) => { onClick={onEmailClick} isTextOverflow dir="auto" + enableUserSelect > {email} diff --git a/packages/client/src/pages/Home/Section/Body/TilesView/sub-components/InfiniteGrid.js b/packages/client/src/pages/Home/Section/Body/TilesView/sub-components/InfiniteGrid.js index f3104c9907..625170db8e 100644 --- a/packages/client/src/pages/Home/Section/Body/TilesView/sub-components/InfiniteGrid.js +++ b/packages/client/src/pages/Home/Section/Body/TilesView/sub-components/InfiniteGrid.js @@ -196,6 +196,7 @@ const InfiniteGrid = (props) => { key={key} className={`tiles-loader ${type}`} isFolder={type === "isFolder"} + isRoom={type === "isRoom"} />, ); } diff --git a/packages/client/src/store/FilesActionsStore.js b/packages/client/src/store/FilesActionsStore.js index 4ef3e4d9d3..11fc69fd0c 100644 --- a/packages/client/src/store/FilesActionsStore.js +++ b/packages/client/src/store/FilesActionsStore.js @@ -2504,6 +2504,9 @@ class FilesActionStore { const { clearFiles, setBufferSelection } = this.filesStore; const { clearInsideGroup, insideGroupBackUrl } = this.peopleStore.groupsStore; + const { isLoading } = this.clientLoadingStore; + + if (isLoading) return; setBufferSelection(null); diff --git a/packages/doceditor/src/components/Root.tsx b/packages/doceditor/src/components/Root.tsx index 7928e29e0e..378b9aac04 100644 --- a/packages/doceditor/src/components/Root.tsx +++ b/packages/doceditor/src/components/Root.tsx @@ -103,6 +103,7 @@ const Root = ({ const { filesSettings } = useFilesSettings({}); const { socketHelper } = useSocketHelper({ socketUrl: user ? settings?.socketUrl ?? "" : "", + user, }); const { onSDKRequestSaveAs, diff --git a/packages/doceditor/src/hooks/useSocketHelper.ts b/packages/doceditor/src/hooks/useSocketHelper.ts index 048796ead8..9eeb1a98b7 100644 --- a/packages/doceditor/src/hooks/useSocketHelper.ts +++ b/packages/doceditor/src/hooks/useSocketHelper.ts @@ -29,12 +29,14 @@ import React from "react"; import SocketIOHelper from "@docspace/shared/utils/socket"; +import { combineUrl } from "@docspace/shared/utils/combineUrl"; import { getRestoreProgress } from "@docspace/shared/api/portal"; +import { getUser } from "@docspace/shared/api/people"; import { EDITOR_ID } from "@docspace/shared/constants"; import { UseSocketHelperProps } from "@/types"; -const useSocketHelper = ({ socketUrl }: UseSocketHelperProps) => { +const useSocketHelper = ({ socketUrl, user }: UseSocketHelperProps) => { const [socketHelper, setSocketHelper] = React.useState( null, ); @@ -48,6 +50,11 @@ const useSocketHelper = ({ socketUrl }: UseSocketHelperProps) => { data: { roomParts: "backup-restore" }, }); + socketIOHelper.emit({ + command: "subscribe", + data: { roomParts: user?.id || "" }, + }); + socketIOHelper.on("restore-backup", async () => { try { const response = await getRestoreProgress(); @@ -69,6 +76,24 @@ const useSocketHelper = ({ socketUrl }: UseSocketHelperProps) => { } }); + socketIOHelper.on("s:logout-session", async (loginEventId) => { + console.log(`[WS] "logout-session"`, loginEventId, user?.loginEventId); + + if ( + Number(loginEventId) === user?.loginEventId || + Number(loginEventId) === 0 + ) { + const docEditor = + typeof window !== "undefined" && + window.DocEditor?.instances[EDITOR_ID]; + + docEditor?.requestClose(); + window.location.replace( + combineUrl(window.ClientConfig?.proxy?.url, "/login"), + ); + } + }); + setSocketHelper(socketIOHelper); }, [socketHelper, socketUrl]); @@ -76,4 +101,3 @@ const useSocketHelper = ({ socketUrl }: UseSocketHelperProps) => { }; export default useSocketHelper; - diff --git a/packages/doceditor/src/types/index.ts b/packages/doceditor/src/types/index.ts index 1446d3e59a..739282d2e6 100644 --- a/packages/doceditor/src/types/index.ts +++ b/packages/doceditor/src/types/index.ts @@ -334,6 +334,7 @@ export interface SelectFileDialogProps { export interface UseSocketHelperProps { socketUrl: string; + user?: TUser; } export interface UseEventsProps { diff --git a/packages/login/src/components/LanguageCombobox/index.tsx b/packages/login/src/components/LanguageCombobox/index.tsx index aff0a09439..723bc795f1 100644 --- a/packages/login/src/components/LanguageCombobox/index.tsx +++ b/packages/login/src/components/LanguageCombobox/index.tsx @@ -70,7 +70,7 @@ const LanguageComboboxWrapper = () => { onSelectLanguage={onLanguageSelect} cultures={cultures} selectedCulture={currentCulture} - withBorder={false} + withBorder={!isMobileView} isMobileView={isMobileView} /> ); diff --git a/packages/shared/api/people/types.ts b/packages/shared/api/people/types.ts index d211f6de1a..d74b09c9b2 100644 --- a/packages/shared/api/people/types.ts +++ b/packages/shared/api/people/types.ts @@ -71,6 +71,7 @@ export type TUser = { cultureName?: string; groups?: TUserGroup[]; shared?: boolean; + loginEventId?: number; }; export type TGetUserList = { diff --git a/packages/shared/components/infinite-loader/InfiniteLoader.styled.ts b/packages/shared/components/infinite-loader/InfiniteLoader.styled.ts index 8529db502c..3f14d92b5b 100644 --- a/packages/shared/components/infinite-loader/InfiniteLoader.styled.ts +++ b/packages/shared/components/infinite-loader/InfiniteLoader.styled.ts @@ -28,7 +28,7 @@ import { List } from "react-virtualized"; import styled, { css } from "styled-components"; import { Base } from "../../themes"; -import { mobile, tablet } from "../../utils"; +import { desktop, mobile, tablet } from "../../utils"; import { TViewAs } from "../../types"; const StyledScroll = styled.div` @@ -155,4 +155,28 @@ StyledScroll.defaultProps = { theme: Base, }; -export { StyledScroll, StyledList }; +const paddingCss = css` + @media ${desktop} { + margin-inline-start: 1px; + padding-inline-end: 0; + } + + @media ${tablet} { + margin-inline-start: -1px; + } +`; + +const StyledItem = styled.div` + display: grid; + grid-template-columns: repeat(auto-fill, minmax(216px, 1fr)); + gap: 14px 16px; + width: 100%; + + @media ${tablet} { + gap: 14px; + } + + ${paddingCss}; +`; + +export { StyledScroll, StyledList, StyledItem }; diff --git a/packages/shared/components/infinite-loader/InfiniteLoader.tsx b/packages/shared/components/infinite-loader/InfiniteLoader.tsx index bae74b22fe..85708e28f5 100644 --- a/packages/shared/components/infinite-loader/InfiniteLoader.tsx +++ b/packages/shared/components/infinite-loader/InfiniteLoader.tsx @@ -24,27 +24,66 @@ // 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 React from "react"; +import { useState, useEffect } from "react"; import { isMobile } from "../../utils"; import ListComponent from "./sub-components/List"; import GridComponent from "./sub-components/Grid"; import { InfiniteLoaderProps } from "./InfiniteLoader.types"; +import { MAX_INFINITE_LOADER_SHIFT } from "../../utils/device"; const InfiniteLoaderComponent = (props: InfiniteLoaderProps) => { const { viewAs, isLoading } = props; + const [scrollTop, setScrollTop] = useState(0); + const [showSkeleton, setShowSkeleton] = useState(false); + const scroll = isMobile() ? document.querySelector("#customScrollBar .scroll-wrapper > .scroller") : document.querySelector("#sectionScroll .scroll-wrapper > .scroller"); + const onScroll = (e: Event) => { + const eventTarget = e.target as HTMLElement; + const currentScrollTop = eventTarget.scrollTop; + + setScrollTop(currentScrollTop ?? 0); + + const scrollShift = scrollTop - currentScrollTop; + + if ( + scrollShift > MAX_INFINITE_LOADER_SHIFT || + scrollShift < -MAX_INFINITE_LOADER_SHIFT + ) { + setShowSkeleton(true); + setTimeout(() => { + setShowSkeleton(false); + }, 200); + } + }; + + useEffect(() => { + if (scroll) scroll.addEventListener("scroll", onScroll); + + return () => { + if (scroll) scroll.removeEventListener("scroll", onScroll); + }; + }); + if (isLoading) return null; return viewAs === "tile" ? ( - + ) : ( - + ); }; diff --git a/packages/shared/components/infinite-loader/InfiniteLoader.types.ts b/packages/shared/components/infinite-loader/InfiniteLoader.types.ts index 09f465ec85..18d140d57a 100644 --- a/packages/shared/components/infinite-loader/InfiniteLoader.types.ts +++ b/packages/shared/components/infinite-loader/InfiniteLoader.types.ts @@ -42,6 +42,7 @@ export interface InfiniteLoaderProps { className?: string; infoPanelVisible?: boolean; countTilesInRow?: number; + showSkeleton?: boolean; } export interface ListComponentProps extends InfiniteLoaderProps { diff --git a/packages/shared/components/infinite-loader/sub-components/Grid.tsx b/packages/shared/components/infinite-loader/sub-components/Grid.tsx index 5d50bfa67f..6b872a04e3 100644 --- a/packages/shared/components/infinite-loader/sub-components/Grid.tsx +++ b/packages/shared/components/infinite-loader/sub-components/Grid.tsx @@ -26,8 +26,10 @@ import React, { useCallback, useEffect, useRef } from "react"; import { InfiniteLoader, WindowScroller, List } from "react-virtualized"; -import { StyledList } from "../InfiniteLoader.styled"; +import { StyledItem, StyledList } from "../InfiniteLoader.styled"; import { GridComponentProps } from "../InfiniteLoader.types"; +import { TileSkeleton } from "../../../skeletons/tiles"; +import { RectangleSkeleton } from "../../../skeletons"; const GridComponent = ({ hasMoreFiles, @@ -39,12 +41,13 @@ const GridComponent = ({ children, className, scroll, + showSkeleton, }: GridComponentProps) => { const loaderRef = useRef(null); const listRef = useRef(null); useEffect(() => { - listRef?.current?.recomputeRowHeights(); + // listRef?.current?.recomputeRowHeights(); //TODO: return there will be problems with the height of the tile when clicking on the backspace }); const isItemLoaded = useCallback( @@ -58,11 +61,50 @@ const GridComponent = ({ index, style, key, + isScrolling, }: { index: number; style: React.CSSProperties; key: string; + isScrolling: boolean; }) => { + const elem = children[index] as React.ReactElement; + const itemClassNames = elem.props?.className; + + const isFolder = itemClassNames?.includes("isFolder"); + const isRoom = itemClassNames?.includes("isRoom"); + const isHeader = + itemClassNames?.includes("folder_header") || + itemClassNames?.includes("files_header"); + + if (isScrolling && showSkeleton) { + const list = []; + let i = 0; + + if (isHeader) { + return ( +
+ + + +
+ ); + } + + while (i < countTilesInRow) { + list.push( + , + ); + i += 1; + } + + return ( +
+ {list.map((item) => item)} +
+ ); + } + return (
{children[index]} diff --git a/packages/shared/components/infinite-loader/sub-components/List.tsx b/packages/shared/components/infinite-loader/sub-components/List.tsx index add98587e6..ed70f0b2b6 100644 --- a/packages/shared/components/infinite-loader/sub-components/List.tsx +++ b/packages/shared/components/infinite-loader/sub-components/List.tsx @@ -47,6 +47,7 @@ const ListComponent = ({ className, scroll, infoPanelVisible, + showSkeleton, }: ListComponentProps) => { const loaderRef = useRef(null); const listRef = useRef(null); @@ -89,13 +90,16 @@ const ListComponent = ({ key, index, style, + isScrolling, }: { key: string; index: number; style: React.CSSProperties; + isScrolling: boolean; }) => { const isLoaded = isItemLoaded({ index }); - if (!isLoaded) return getLoader(style, key); + if (!isLoaded || (isScrolling && showSkeleton)) + return getLoader(style, key); return (
@@ -108,10 +112,12 @@ const ListComponent = ({ index, style, key, + isScrolling, }: { index: number; style: React.CSSProperties; key: string; + isScrolling: boolean; }) => { if (!columnInfoPanelStorageName || !columnStorageName) { throw new Error("columnStorageName is required for a table view"); @@ -122,7 +128,8 @@ const ListComponent = ({ : localStorage.getItem(columnStorageName); const isLoaded = isItemLoaded({ index }); - if (!isLoaded) return getLoader(style, key); + if (!isLoaded || (isScrolling && showSkeleton)) + return getLoader(style, key); return (
(null); const [scale, setScale] = useState(1); + const [showOriginSrc, setShowOriginSrc] = useState(false); const [isError, setIsError] = useState(false); const [isLoading, setIsLoading] = useState(false); const [backgroundBlack, setBackgroundBlack] = useState(() => false); @@ -854,18 +855,19 @@ export const ImageViewer = ({ } }; - const onError = useCallback( - (e: SyntheticEvent) => { - if (window.ClientConfig?.imageThumbnails && thumbnailSrc && src) { - // if thumbnailSrc is unavailable, try to load original image - e.currentTarget.src = src; - return; - } + const onError = useCallback(() => { + if ( + window.ClientConfig?.imageThumbnails && + thumbnailSrc && + (src || isTiff) + ) { + // if thumbnailSrc is unavailable, try to load original image + setShowOriginSrc(true); + return; + } - setIsError(true); - }, - [src, thumbnailSrc], - ); + setIsError(true); + }, [src, thumbnailSrc, isTiff]); const model = React.useMemo(() => contextModel(true), [contextModel]); @@ -951,6 +953,16 @@ export const ImageViewer = ({ }; }, []); + useLayoutEffect(() => { + return () => { + if (imgRef.current) { + // abort img loading + // eslint-disable-next-line react-hooks/exhaustive-deps + imgRef.current.src = ""; + } + }; + }, []); + return ( <> {isMobile && !backgroundBlack && mobileDetails} @@ -973,7 +985,9 @@ export const ImageViewer = ({ ` +const StyledHeader = styled.div<{ withoutBorder?: boolean }>` width: calc(100% - 32px); min-height: 53px; height: 53px; diff --git a/packages/shared/components/selector/Selector.types.ts b/packages/shared/components/selector/Selector.types.ts index 298c2b7b3a..29bfd0a026 100644 --- a/packages/shared/components/selector/Selector.types.ts +++ b/packages/shared/components/selector/Selector.types.ts @@ -47,7 +47,7 @@ type THeaderBackButton = | { onBackClick?: undefined; withoutBackButton?: undefined; - withoutBorder: undefined; + withoutBorder?: undefined; }; export type TInfoBarData = { diff --git a/packages/shared/components/share/sub-components/LinkRow.tsx b/packages/shared/components/share/sub-components/LinkRow.tsx index 71d6a40c5b..642b0da607 100644 --- a/packages/shared/components/share/sub-components/LinkRow.tsx +++ b/packages/shared/components/share/sub-components/LinkRow.tsx @@ -175,7 +175,6 @@ const LinkRow = ({ {!isArchiveFolder && ( onCopyLink(link)} title={t("Common:CreateAndCopy")} @@ -221,6 +220,7 @@ const LinkRow = ({ type="onlyIcon" isDisabled={isExpiredLink || isLoaded} manualWidth="fit-content" + withBackdrop={false} /> )}
diff --git a/packages/shared/selectors/Files/hooks/useLoadersHelper.ts b/packages/shared/selectors/Files/hooks/useLoadersHelper.ts index 19f75c3174..94cc0d4a25 100644 --- a/packages/shared/selectors/Files/hooks/useLoadersHelper.ts +++ b/packages/shared/selectors/Files/hooks/useLoadersHelper.ts @@ -44,6 +44,7 @@ const useLoadersHelper = () => { const [isFirstLoad, setIsFirstLoad] = React.useState(true); const startLoader = React.useRef(new Date()); + const loaderTimeout = React.useRef(null); const breadCrumbsLoaderTimeout = React.useRef(null); const breadCrumbsStartLoader = React.useRef(new Date()); @@ -59,9 +60,10 @@ const useLoadersHelper = () => { const calculateLoader = React.useCallback(() => { if (isFirstLoad) { - setShowLoader(true); - - startLoader.current = new Date(); + loaderTimeout.current = setTimeout(() => { + startLoader.current = new Date(); + if (isMount.current) setShowLoader(true); + }, SHOW_LOADER_TIMER); } else if (startLoader.current) { const currentDate = new Date(); @@ -80,6 +82,11 @@ const useLoadersHelper = () => { setShowLoader(false); } }, MIN_LOADER_TIMER - ms); + + loaderTimeout.current = null; + } else if (loaderTimeout.current) { + clearTimeout(loaderTimeout.current); + loaderTimeout.current = null; } }, [isFirstLoad]); diff --git a/packages/shared/selectors/Files/index.tsx b/packages/shared/selectors/Files/index.tsx index 1c601c5d96..ddcd246312 100644 --- a/packages/shared/selectors/Files/index.tsx +++ b/packages/shared/selectors/Files/index.tsx @@ -238,7 +238,7 @@ const FilesSelectorComponent = ({ setSelectedItemType(undefined); getRootData(); } else { - setItems([]); + // setItems([]); setBreadCrumbs((bc) => { const idx = bc.findIndex( @@ -302,7 +302,7 @@ const FilesSelectorComponent = ({ if (item.isFolder) { setIsFirstLoad(true); - setItems([]); + // setItems([]); setBreadCrumbs((value) => [ ...value, { @@ -445,7 +445,14 @@ const FilesSelectorComponent = ({ selectedFileInfo, ); }, - [breadCrumbs, selectedFileInfo, selectedItemId, selectedTreeNode, onSubmit], + [ + breadCrumbs, + rootFolderType, + onSubmit, + selectedItemId, + selectedTreeNode, + selectedFileInfo, + ], ); React.useEffect(() => { @@ -499,7 +506,7 @@ const FilesSelectorComponent = ({ submitButtonLabel, submitButtonId, disableSubmitButton: getIsDisabled( - isFirstLoad, + isFirstLoad && showLoader, isSelectedParentFolder, selectedItemId, selectedItemType, diff --git a/packages/shared/skeletons/tiles/Tile.tsx b/packages/shared/skeletons/tiles/Tile.tsx index 0a8e2fe325..907b95dd4b 100644 --- a/packages/shared/skeletons/tiles/Tile.tsx +++ b/packages/shared/skeletons/tiles/Tile.tsx @@ -27,11 +27,19 @@ import React from "react"; import { RectangleSkeleton } from "@docspace/shared/skeletons"; -import { StyledTile, StyledBottom, StyledMainContent } from "./Tiles.styled"; +import { + StyledTile, + StyledBottom, + StyledMainContent, + StyledRoomTile, + StyledRoomTileTopContent, + StyledRoomTileBottomContent, +} from "./Tiles.styled"; import type { TileSkeletonProps } from "./Tiles.types"; export const TileSkeleton = ({ isFolder, + isRoom, title, borderRadius, backgroundColor, @@ -84,6 +92,79 @@ export const TileSkeleton = ({ /> + ) : isRoom ? ( + + + + + + + + + + + + + ) : ( diff --git a/packages/shared/skeletons/tiles/Tiles.styled.ts b/packages/shared/skeletons/tiles/Tiles.styled.ts index a2e32f975e..aa0e1b645e 100644 --- a/packages/shared/skeletons/tiles/Tiles.styled.ts +++ b/packages/shared/skeletons/tiles/Tiles.styled.ts @@ -114,3 +114,26 @@ export const StyledTilesWrapper = styled.div` grid-template-columns: 1fr; grid-gap: 16px; `; + +export const StyledRoomTile = styled.div` + border: ${(props) => props.theme.filesSection.tilesView.tile.border}; + border-radius: 6px; + height: 120px; +`; + +export const StyledRoomTileTopContent = styled.div` + display: grid; + grid-template-columns: 32px 1fr 24px; + gap: 8px; + align-items: center; + height: 61px; + border-bottom: ${(props) => props.theme.filesSection.tilesView.tile.border}; + padding: 0 8px 0 16px; +`; + +export const StyledRoomTileBottomContent = styled.div` + display: flex; + align-items: center; + padding: 16px; + gap: 4px; +`; diff --git a/packages/shared/skeletons/tiles/Tiles.types.ts b/packages/shared/skeletons/tiles/Tiles.types.ts index 2881313070..982ab305bc 100644 --- a/packages/shared/skeletons/tiles/Tiles.types.ts +++ b/packages/shared/skeletons/tiles/Tiles.types.ts @@ -38,4 +38,5 @@ export interface StyledBottomProps { export interface TileSkeletonProps extends RectangleSkeletonProps { isFolder?: boolean; + isRoom?: boolean; } diff --git a/packages/shared/utils/device.ts b/packages/shared/utils/device.ts index 1900a6a3cf..5ca02c86ce 100644 --- a/packages/shared/utils/device.ts +++ b/packages/shared/utils/device.ts @@ -26,6 +26,7 @@ export const INFO_PANEL_WIDTH = 400; export const TABLE_HEADER_HEIGHT = 40; +export const MAX_INFINITE_LOADER_SHIFT = 800; export function checkIsSSR() { return typeof window === "undefined";