diff --git a/packages/client/public/locales/en/Files.json b/packages/client/public/locales/en/Files.json index 1597d2ab33..3d8601db58 100644 --- a/packages/client/public/locales/en/Files.json +++ b/packages/client/public/locales/en/Files.json @@ -117,6 +117,7 @@ "OpenLocation": "Open location", "PasswordAccess": "Password access", "PasswordLink": "Add a password to protect your link.", + "PasswordProtectedFiles": "Please <0>enter a password for the protected files to continue", "PasswordSuccessfullyCopied": "Password successfully copied", "Pin": "Pin", "PinToTop": "Pin to top", diff --git a/packages/client/src/HOCs/withFileActions.js b/packages/client/src/HOCs/withFileActions.js index a6aea017c0..105767bbaa 100644 --- a/packages/client/src/HOCs/withFileActions.js +++ b/packages/client/src/HOCs/withFileActions.js @@ -62,21 +62,14 @@ export default function withFileActions(WrappedFileItem) { }; onDropZoneUpload = (files, uploadToFolder) => { - const { t, dragging, setDragging, startUpload, uploadEmptyFolders } = + const { t, dragging, setDragging, startUpload, createFoldersTree } = this.props; dragging && setDragging(false); - const emptyFolders = files.filter((f) => f.isEmptyDirectory); - - if (emptyFolders.length > 0) { - uploadEmptyFolders(emptyFolders, uploadToFolder).then(() => { - const onlyFiles = files.filter((f) => !f.isEmptyDirectory); - if (onlyFiles.length > 0) startUpload(onlyFiles, uploadToFolder, t); - }); - } else { - startUpload(files, uploadToFolder, t); - } + createFoldersTree(files, uploadToFolder).then((f) => { + if (f.length > 0) startUpload(f, null, t); + }); }; onDrop = (items) => { @@ -371,7 +364,7 @@ export default function withFileActions(WrappedFileItem) { onSelectItem, //setNewBadgeCount, openFileAction, - uploadEmptyFolders, + createFoldersTree, } = filesActionsStore; const { setSharingPanelVisible } = dialogsStore; const { @@ -475,7 +468,7 @@ export default function withFileActions(WrappedFileItem) { dragging, setDragging, startUpload, - uploadEmptyFolders, + createFoldersTree, draggable, setTooltipPosition, setStartDrag, diff --git a/packages/client/src/components/Article/Body/Items.js b/packages/client/src/components/Article/Body/Items.js index f4b212e4fa..e06439b783 100644 --- a/packages/client/src/components/Article/Body/Items.js +++ b/packages/client/src/components/Article/Body/Items.js @@ -68,7 +68,7 @@ const Item = ({ onBadgeClick, showDragItems, startUpload, - uploadEmptyFolders, + createFoldersTree, setDragging, showBadge, labelBadge, @@ -86,18 +86,12 @@ const Item = ({ const onDropZoneUpload = React.useCallback( (files, uploadToFolder) => { dragging && setDragging(false); - const emptyFolders = files.filter((f) => f.isEmptyDirectory); - if (emptyFolders.length > 0) { - uploadEmptyFolders(emptyFolders, uploadToFolder).then(() => { - const onlyFiles = files.filter((f) => !f.isEmptyDirectory); - if (onlyFiles.length > 0) startUpload(onlyFiles, uploadToFolder, t); - }); - } else { - startUpload(files, uploadToFolder, t); - } + createFoldersTree(files, uploadToFolder).then((f) => { + if (f.length > 0) startUpload(f, null, t); + }); }, - [t, dragging, setDragging, startUpload, uploadEmptyFolders], + [t, dragging, setDragging, startUpload, createFoldersTree], ); const onDrop = React.useCallback( @@ -193,7 +187,7 @@ const Items = ({ dragging, setDragging, startUpload, - uploadEmptyFolders, + createFoldersTree, isVisitor, isCollaborator, isAdmin, @@ -326,7 +320,7 @@ const Items = ({ t={t} setDragging={setDragging} startUpload={startUpload} - uploadEmptyFolders={uploadEmptyFolders} + createFoldersTree={createFoldersTree} item={item} setBufferSelection={setBufferSelection} dragging={dragging} @@ -388,7 +382,7 @@ const Items = ({ showText, setDragging, startUpload, - uploadEmptyFolders, + createFoldersTree, trashIsEmpty, isAdmin, isVisitor, @@ -449,7 +443,7 @@ export default inject( const { id, access: folderAccess } = selectedFolderStore; const { moveDragItems, - uploadEmptyFolders, + createFoldersTree, deleteAction, emptyTrashInProgress, } = filesActionsStore; @@ -478,7 +472,7 @@ export default inject( setBufferSelection, deleteAction, startUpload, - uploadEmptyFolders, + createFoldersTree, setEmptyTrashDialogVisible, trashIsEmpty, diff --git a/packages/client/src/components/Article/MainButton/index.js b/packages/client/src/components/Article/MainButton/index.js index 6f1f4e0afb..a92377d711 100644 --- a/packages/client/src/components/Article/MainButton/index.js +++ b/packages/client/src/components/Article/MainButton/index.js @@ -70,6 +70,7 @@ import { resendInvitesAgain } from "@docspace/shared/api/people"; import { getCorrectFourValuesStyle } from "@docspace/shared/utils"; import { ArticleButtonLoader } from "@docspace/shared/skeletons/article"; import { isMobile, isTablet } from "react-device-detect"; +import getFilesFromEvent from "@docspace/shared/components/drag-and-drop/get-files-from-event"; const StyledButton = styled(Button)` font-weight: 700; @@ -180,6 +181,7 @@ const ArticleMainButtonContent = (props) => { parentRoomType, isFolder, + createFoldersTree, } = props; const navigate = useNavigate(); @@ -239,8 +241,12 @@ const ArticleMainButtonContent = (props) => { ); const onFileChange = React.useCallback( - (e) => { - startUpload(e.target.files, null, t); + async (e) => { + const files = await getFilesFromEvent(e); + + createFoldersTree(files).then((f) => { + if (f.length > 0) startUpload(f, null, t); + }); }, [startUpload, t], ); @@ -249,7 +255,7 @@ const ArticleMainButtonContent = (props) => { if (isPrivacy) { encryptionUploadDialog((encryptedFile, encrypted) => { encryptedFile.encrypted = encrypted; - startUpload([encryptedFile], null, t); + startUpload([encryptedFile], null, t); // TODO: createFoldersTree }); } else { inputFilesElement.current.click(); @@ -901,6 +907,7 @@ export default inject( versionHistoryStore, userStore, currentTariffStatusStore, + filesActionsStore, }) => { const { showArticleLoader } = clientLoadingStore; const { mainButtonMobileVisible } = filesStore; @@ -945,6 +952,8 @@ export default inject( const { frameConfig, isFrame } = settingsStore; + const { createFoldersTree } = filesActionsStore; + return { isGracePeriod, setInviteUsersWarningDialogVisible, @@ -997,6 +1006,7 @@ export default inject( isFolder, selectFileFormRoomDialogVisible, setSelectFileFormRoomDialogVisible, + createFoldersTree, }; }, )( diff --git a/packages/client/src/components/EmptyContainer/RoomNoAccessContainer.js b/packages/client/src/components/EmptyContainer/RoomNoAccessContainer.js index e02dc9a77f..61df1e840f 100644 --- a/packages/client/src/components/EmptyContainer/RoomNoAccessContainer.js +++ b/packages/client/src/components/EmptyContainer/RoomNoAccessContainer.js @@ -38,6 +38,7 @@ import { IconButton } from "@docspace/shared/components/icon-button"; import RoomsFilter from "@docspace/shared/api/rooms/filter"; import { RoomSearchArea } from "@docspace/shared/enums"; +import { frameCallEvent } from "@docspace/shared/utils/common"; import { getCategoryUrl } from "SRC_DIR/helpers/utils"; import { CategoryType } from "SRC_DIR/helpers/constants"; @@ -60,6 +61,8 @@ const RoomNoAccessContainer = (props) => { const navigate = useNavigate(); React.useEffect(() => { + frameCallEvent({ event: "onNoAccess" }); + const timer = setTimeout(onGoToShared, 5000); return () => clearTimeout(timer); }, []); diff --git a/packages/client/src/components/panels/InvitePanel/sub-components/ExternalLinks.js b/packages/client/src/components/panels/InvitePanel/sub-components/ExternalLinks.js index a247277c03..93c53609fe 100644 --- a/packages/client/src/components/panels/InvitePanel/sub-components/ExternalLinks.js +++ b/packages/client/src/components/panels/InvitePanel/sub-components/ExternalLinks.js @@ -88,7 +88,7 @@ const ExternalLinks = ({ copyLink(link.shareLink); } } else { - !externalLinksVisible ? editLink() : disableLink(); + !externalLinksVisible ? await editLink() : await disableLink(); } onChangeExternalLinksVisible(!externalLinksVisible); } catch (error) { @@ -98,9 +98,10 @@ const ExternalLinks = ({ } }; - const disableLink = () => { - setInvitationLinks(roomId, "Invite", 0, shareLinks[0].id); - setShareLinks([]); + const disableLink = async () => { + shareLinks?.length && + (await setInvitationLinks(roomId, "Invite", 0, shareLinks[0].id)); + return setShareLinks([]); }; const editLink = async () => { @@ -120,7 +121,7 @@ const ExternalLinks = ({ copyLink(shareLink); setShareLinks([activeLink]); - setActiveLink(activeLink); + return setActiveLink(activeLink); }; const onSelectAccess = async (access) => { diff --git a/packages/client/src/pages/Confirm/sub-components/StyledCreateUser.js b/packages/client/src/pages/Confirm/sub-components/StyledCreateUser.js index a8d6aa98bf..8ff8d3adf4 100644 --- a/packages/client/src/pages/Confirm/sub-components/StyledCreateUser.js +++ b/packages/client/src/pages/Confirm/sub-components/StyledCreateUser.js @@ -54,7 +54,10 @@ export const GreetingContainer = styled.div` } .tooltip { - p { + .invitation-text { + display: flex; + flex-direction: column; + align-items: center; text-align: center; } @@ -65,15 +68,13 @@ export const GreetingContainer = styled.div` .portal-logo { width: 100%; + max-width: 386px; + height: 44px; + margin: 0 auto; padding-bottom: 16px; - height: 26.56px; display: flex; align-items: center; justify-content: center; - - .injected-svg { - height: 26.56px; - } } `; diff --git a/packages/client/src/pages/Confirm/sub-components/createUser.js b/packages/client/src/pages/Confirm/sub-components/createUser.js index 99eb5e95f6..fcd1292fea 100644 --- a/packages/client/src/pages/Confirm/sub-components/createUser.js +++ b/packages/client/src/pages/Confirm/sub-components/createUser.js @@ -485,7 +485,7 @@ const CreateUserForm = (props) => { {linkData.type === "LinkInvite" && (
- + {roomName ? ( f.isEmptyDirectory); - - if (emptyFolders.length > 0) { - uploadEmptyFolders(emptyFolders, uploadToFolder).then(() => { - const onlyFiles = files.filter((f) => !f.isEmptyDirectory); - if (onlyFiles.length > 0) startUpload(onlyFiles, uploadToFolder, t); - }); - } else { - startUpload(files, uploadToFolder, t); - } + createFoldersTree(files, uploadToFolder).then((f) => { + if (f.length > 0) startUpload(f, null, t); + }); }; React.useEffect(() => { diff --git a/packages/client/src/pages/Home/InfoPanel/Body/styles/common.js b/packages/client/src/pages/Home/InfoPanel/Body/styles/common.js index 2332db4d55..fb45cf0d15 100644 --- a/packages/client/src/pages/Home/InfoPanel/Body/styles/common.js +++ b/packages/client/src/pages/Home/InfoPanel/Body/styles/common.js @@ -264,8 +264,8 @@ const StyledProperties = styled.div` grid-template-columns: 120px 1fr; grid-column-gap: 24px; - -webkit-box-align: center; - align-items: center; + -webkit-box-align: baseline; + align-items: baseline; .property-title { font-size: 13px; diff --git a/packages/client/src/pages/Home/Section/AccountsBody/InsideGroup/TableView/TableHeader.js b/packages/client/src/pages/Home/Section/AccountsBody/InsideGroup/TableView/TableHeader.js index 94df06e920..5783767e86 100644 --- a/packages/client/src/pages/Home/Section/AccountsBody/InsideGroup/TableView/TableHeader.js +++ b/packages/client/src/pages/Home/Section/AccountsBody/InsideGroup/TableView/TableHeader.js @@ -138,15 +138,8 @@ class InsideGroupTableHeader extends React.Component { 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"; + if (newFilter.sortBy !== "displayname") { + newFilter.sortBy = "displayname"; } newFilter.sortOrder = newFilter.sortOrder === "ascending" ? "descending" : "ascending"; @@ -176,10 +169,7 @@ class InsideGroupTableHeader extends React.Component { } = this.props; const { sortOrder } = filter; - const sortBy = - filter.sortBy === "firstname" || filter.sortBy === "lastname" - ? "AZ" - : filter.sortBy; + const sortBy = filter.sortBy === "displayname" ? "AZ" : filter.sortBy; return ( { getHeaderOptions, setBufferSelection, setGroupsBufferSelection, + createFoldersTree, } = props; const location = useLocation(); @@ -239,8 +241,12 @@ const SectionHeaderContent = (props) => { const isSettingsPage = location.pathname.includes("/settings"); const onFileChange = React.useCallback( - (e) => { - startUpload(e.target.files, null, t); + async (e) => { + const files = await getFilesFromEvent(e); + + createFoldersTree(files).then((f) => { + if (f.length > 0) startUpload(f, null, t); + }); }, [startUpload, t], ); @@ -639,6 +645,7 @@ export default inject( moveToRoomsPage, onClickBack, moveToPublicRoom, + createFoldersTree, } = filesActionsStore; const { setIsVisible, isVisible } = infoPanelStore; @@ -802,6 +809,7 @@ export default inject( getHeaderOptions, setBufferSelection, setGroupsBufferSelection, + createFoldersTree, }; }, )( diff --git a/packages/client/src/pages/Home/index.js b/packages/client/src/pages/Home/index.js index 197ad6b00a..d9015a5320 100644 --- a/packages/client/src/pages/Home/index.js +++ b/packages/client/src/pages/Home/index.js @@ -82,7 +82,7 @@ const PureHome = (props) => { startUpload, setDragging, dragging, - uploadEmptyFolders, + createFoldersTree, disableDrag, uploaded, converted, @@ -182,7 +182,7 @@ const PureHome = (props) => { dragging, setDragging, disableDrag, - uploadEmptyFolders, + createFoldersTree, startUpload, fetchFiles, fetchRooms, @@ -543,7 +543,7 @@ export default inject( const { setUploadPanelVisible, startUpload, uploaded, converted } = uploadDataStore; - const { uploadEmptyFolders, onClickBack } = filesActionsStore; + const { createFoldersTree, onClickBack } = filesActionsStore; const selectionLength = isProgressFinished ? selection.length : null; const selectionTitle = isProgressFinished @@ -635,7 +635,7 @@ export default inject( setUploadPanelVisible, startUpload, - uploadEmptyFolders, + createFoldersTree, setToPreviewFile, setIsPreview, diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/Manager.js b/packages/client/src/pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/Manager.js index 7c7e509520..a3c1c572bb 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/Manager.js +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/Manager.js @@ -130,6 +130,7 @@ const Manager = (props) => { ); const [selectedColumns, setSelectedColumns] = useState([ { key: "Name", label: t("Common:Name") }, + { key: "Size", label: t("Common:Size") }, { key: "Type", label: t("Common:Type") }, { key: "Tags", label: t("Common:Tags") }, ]); diff --git a/packages/client/src/store/EditGroupStore.ts b/packages/client/src/store/EditGroupStore.ts index 541ace92c8..570113fd75 100644 --- a/packages/client/src/store/EditGroupStore.ts +++ b/packages/client/src/store/EditGroupStore.ts @@ -125,6 +125,17 @@ class EditGroupStore { const addedIds = Array.from(this.addedMembersMap.keys()); const removedIds = Array.from(this.removedMembersMap.keys()); + const oldManager = this.group.manager; + const oldManagerRemovedButRemainsAsMember = + oldManager && + oldManager.id !== this.manager?.id && + !this.removedMembersMap.has(oldManager.id); + + // Requires when new group is without manager and old manager moved to members. updateGroup api method doesn't provide possibility to do it without setting new manager + if (this.manager === null && oldManagerRemovedButRemainsAsMember) { + await api.groups.removeGroupMembers(this.group.id, [oldManager.id]); + addedIds.push(oldManager.id); + } await updateGroup( this.group?.id, diff --git a/packages/client/src/store/FilesActionsStore.js b/packages/client/src/store/FilesActionsStore.js index 1349a9ec62..0cb008057d 100644 --- a/packages/client/src/store/FilesActionsStore.js +++ b/packages/client/src/store/FilesActionsStore.js @@ -229,17 +229,21 @@ class FilesActionStore { let level = { result }; try { folders.forEach((folder) => { - folder.path - .split("/") - .filter((name) => name !== "") - .reduce((r, name, i, a) => { - if (!r[name]) { - r[name] = { result: [] }; - r.result.push({ name, children: r[name].result }); - } + const folderPath = folder.path.split("/").filter((name) => name !== ""); - return r[name]; - }, level); + folderPath.reduce((r, name, i, a) => { + if (!r[name]) { + r[name] = { result: [] }; + r.result.push({ + name, + children: r[name].result, + isFile: folderPath.length - 1 === i && !folder.isEmptyDirectory, + file: folder, + }); + } + + return r[name]; + }, level); }); } catch (e) { console.error("convertToTree", e); @@ -247,52 +251,67 @@ class FilesActionStore { return result; }; - createFolderTree = async (treeList, parentFolderId) => { + createFolderTree = async (treeList, parentFolderId, filesList) => { if (!treeList || !treeList.length) return; for (let i = 0; i < treeList.length; i++) { const treeNode = treeList[i]; + const isFile = treeList[i].isFile; // console.log( // `createFolderTree parent id = ${parentFolderId} name '${treeNode.name}': `, // treeNode.children // ); + if (isFile) { + treeList[i].file.parentFolderId = parentFolderId; + filesList.push(treeList[i].file); + continue; + } + const folder = await createFolder(parentFolderId, treeNode.name); const parentId = folder.id; if (treeNode.children.length == 0) continue; - await this.createFolderTree(treeNode.children, parentId); + await this.createFolderTree(treeNode.children, parentId, filesList); } + + return treeList; }; - uploadEmptyFolders = async (emptyFolders, folderId) => { - //console.log("uploadEmptyFolders", emptyFolders, folderId); + createFoldersTree = async (files, folderId) => { + //console.log("createFoldersTree", files, folderId); - const { secondaryProgressDataStore } = this.uploadDataStore; - const { setSecondaryProgressBarData, clearSecondaryProgressData } = - secondaryProgressDataStore; + const { primaryProgressDataStore } = this.uploadDataStore; + + const { setPrimaryProgressBarData, clearPrimaryProgressData } = + primaryProgressDataStore; const operationId = uniqueid("operation_"); const toFolderId = folderId ? folderId : this.selectedFolderStore.id; - setSecondaryProgressBarData({ - icon: "file", + setPrimaryProgressBarData({ + icon: "upload", visible: true, percent: 0, label: "", alert: false, - operationId, }); - const tree = this.convertToTree(emptyFolders); - await this.createFolderTree(tree, toFolderId); + const tree = this.convertToTree(files); + + const filesList = []; + await this.createFolderTree(tree, toFolderId, filesList); this.updateCurrentFolder(null, [folderId], null, operationId); - setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT); + if (!filesList.length) { + setTimeout(() => clearPrimaryProgressData(), TIMEOUT); + } + + return filesList; }; updateFilesAfterDelete = (operationId) => { diff --git a/packages/client/src/store/HotkeyStore.js b/packages/client/src/store/HotkeyStore.js index 46ed343cdb..559ed6d98d 100644 --- a/packages/client/src/store/HotkeyStore.js +++ b/packages/client/src/store/HotkeyStore.js @@ -683,9 +683,8 @@ class HotkeyStore { }; uploadClipboardFiles = async (t, event) => { - const { uploadEmptyFolders } = this.filesActionsStore; + const { createFoldersTree } = this.filesActionsStore; const { startUpload } = this.uploadDataStore; - const currentFolderId = this.selectedFolderStore.id; if (this.filesStore.hotkeysClipboard.length) { return this.moveFilesFromClipboard(t); @@ -693,16 +692,9 @@ class HotkeyStore { const files = await getFilesFromEvent(event); - const emptyFolders = files.filter((f) => f.isEmptyDirectory); - - if (emptyFolders.length > 0) { - uploadEmptyFolders(emptyFolders, currentFolderId).then(() => { - const onlyFiles = files.filter((f) => !f.isEmptyDirectory); - if (onlyFiles.length > 0) startUpload(onlyFiles, currentFolderId, t); - }); - } else { - startUpload(files, currentFolderId, t); - } + createFoldersTree(files, uploadToFolder).then((f) => { + if (f.length > 0) startUpload(f, null, t); + }); }; get countTilesInRow() { diff --git a/packages/client/src/store/LdapFormStore.js b/packages/client/src/store/LdapFormStore.js index c725630f72..17a374eb53 100644 --- a/packages/client/src/store/LdapFormStore.js +++ b/packages/client/src/store/LdapFormStore.js @@ -489,7 +489,7 @@ class LdapFormStore { completed: true, percents: 100, certificateConfirmRequest: null, - error: "", + error: t("Common:UnexpectedError"), }; } @@ -518,7 +518,6 @@ class LdapFormStore { toastr.success(t("Common:SuccessfullyCompletedOperation")); } } catch (error) { - console.error(error); toastr.error(error); this.endProcess(); } diff --git a/packages/client/src/store/ProfileActionsStore.js b/packages/client/src/store/ProfileActionsStore.js index ef6ed373b4..6c620bed1d 100644 --- a/packages/client/src/store/ProfileActionsStore.js +++ b/packages/client/src/store/ProfileActionsStore.js @@ -347,7 +347,7 @@ class ProfileActionsStore { let bookTraining = null; - if (!isMobile && this.isTeamTrainingAlertAvailable) { + if (!isMobile && this.authStore.isTeamTrainingAlertAvailable) { bookTraining = { key: "user-menu-book-training", icon: BookTrainingReactSvgUrl, diff --git a/packages/client/src/store/UploadDataStore.js b/packages/client/src/store/UploadDataStore.js index 34f3cdb11b..50c4ed7f0e 100644 --- a/packages/client/src/store/UploadDataStore.js +++ b/packages/client/src/store/UploadDataStore.js @@ -25,9 +25,11 @@ // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode import { makeAutoObservable, runInAction } from "mobx"; +import { Trans } from "react-i18next"; import { TIMEOUT } from "@docspace/client/src/helpers/filesConstants"; import uniqueid from "lodash/uniqueId"; import sumBy from "lodash/sumBy"; +import uniqBy from "lodash/uniqBy"; import { ConflictResolveType } from "@docspace/shared/enums"; import { getFileInfo, @@ -57,6 +59,8 @@ import { getCategoryTypeByFolderType, getCategoryUrl, } from "SRC_DIR/helpers/utils"; +import { Link } from "@docspace/shared/components/link"; +import { globalColors } from "@docspace/shared/themes"; class UploadDataStore { settingsStore; @@ -636,7 +640,7 @@ class UploadDataStore { if (this.uploaded || (this.isParallel && allFilesIsUploaded)) { this.setConversionPercent(100); - this.finishUploadFiles(); + this.finishUploadFiles(t); } else { runInAction(() => { this.converted = true; @@ -773,7 +777,8 @@ class UploadDataStore { file: file, uniqueId: uniqueid("download_row-key_"), fileId: null, - toFolderId, + // toFolderId, + toFolderId: file.parentFolderId, action: "upload", error: file.size ? null : t("Files:EmptyFile"), fileInfo: null, @@ -1263,7 +1268,7 @@ class UploadDataStore { let files = this.files; if (files.length === 0 || this.filesSize === 0) { - return this.finishUploadFiles(); + return this.finishUploadFiles(t); } const progressData = { @@ -1310,7 +1315,7 @@ class UploadDataStore { } if (!this.filesToConversion.length) { - this.finishUploadFiles(); + this.finishUploadFiles(t); } else { runInAction(() => { this.uploaded = true; @@ -1372,7 +1377,7 @@ class UploadDataStore { toFolderId, fileName, fileSize, - relativePath, + "", // relativePath, file.encrypted, file.lastModifiedDate, createNewIfExist, @@ -1512,7 +1517,7 @@ class UploadDataStore { if (allFilesIsUploaded) { if (!this.filesToConversion.length) { - this.finishUploadFiles(); + this.finishUploadFiles(t); } else { runInAction(() => { this.uploaded = true; @@ -1542,7 +1547,7 @@ class UploadDataStore { }); }; - finishUploadFiles = () => { + finishUploadFiles = (t) => { const { fetchFiles, filter } = this.filesStore; const { withPaging } = this.settingsStore; @@ -1554,7 +1559,7 @@ class UploadDataStore { this.asyncUploadObj = {}; for (let item of this.tempFiles) { - const { uploadFiles, folderId, t } = item; + const { uploadFiles, folderId } = item; this.startUpload(uploadFiles, folderId, t); } this.tempFiles = []; @@ -1562,12 +1567,36 @@ class UploadDataStore { return; } - const totalErrorsCount = sumBy(this.files, (f) => { - f.error && toastr.error(f.error); - return f.error ? 1 : 0; - }); + const filesWithErrors = this.files.filter((f) => f.error); + + const totalErrorsCount = filesWithErrors.length; if (totalErrorsCount > 0) { + const uniqErrors = uniqBy(filesWithErrors, "error"); + uniqErrors.forEach((f) => + f.error.indexOf("password") > -1 + ? toastr.warning( + { + toastr.clear(); + this.setUploadPanelVisible(true); + }} + />, + ]} + />, + null, + 60000, + true, + ) + : toastr.error(f.error), + ); + this.primaryProgressDataStore.setPrimaryProgressBarShowError(true); // for empty file this.primaryProgressDataStore.setPrimaryProgressBarErrors( totalErrorsCount, diff --git a/packages/shared/api/groups/index.ts b/packages/shared/api/groups/index.ts index 3b54cba3b1..36b1987c68 100644 --- a/packages/shared/api/groups/index.ts +++ b/packages/shared/api/groups/index.ts @@ -146,6 +146,14 @@ export const addGroupMembers = (groupId: string, members: string) => { }); }; +export const removeGroupMembers = (groupId: string, membersIds: string[]) => { + return request({ + method: "delete", + url: `/group/${groupId}/members`, + data: { id: groupId, members: membersIds }, + }) as Promise; +}; + // * Delete export const deleteGroup = (groupId: string) => { diff --git a/packages/shared/api/people/filter.js b/packages/shared/api/people/filter.js index 926e9e7b6c..086224fd9a 100644 --- a/packages/shared/api/people/filter.js +++ b/packages/shared/api/people/filter.js @@ -29,7 +29,7 @@ import { getObjectByLocation, toUrlParams } from "../../utils/common"; const DEFAULT_PAGE = 0; const DEFAULT_PAGE_COUNT = 25; const DEFAULT_TOTAL = 0; -const DEFAULT_SORT_BY = "firstname"; +const DEFAULT_SORT_BY = "displayname"; const DEFAULT_SORT_ORDER = "ascending"; const DEFAULT_EMPLOYEE_STATUS = null; const DEFAULT_ACTIVATION_STATUS = null; diff --git a/packages/shared/components/calendar/utils/getCalendarYears.ts b/packages/shared/components/calendar/utils/getCalendarYears.ts index 8e0630cf57..82a2538044 100644 --- a/packages/shared/components/calendar/utils/getCalendarYears.ts +++ b/packages/shared/components/calendar/utils/getCalendarYears.ts @@ -29,7 +29,7 @@ import moment from "moment"; export const getCalendarYears = (observedDate: moment.Moment) => { const years = []; const selectedYear = observedDate.year(); - const firstYear = selectedYear - (selectedYear % 10) - 1; + const firstYear = selectedYear - 1; for (let i = firstYear; i <= firstYear + 15; i += 1) { years.push(moment(i, "YYYY").format("YYYY")); diff --git a/packages/shared/components/error-container/ErrorContainer.styled.ts b/packages/shared/components/error-container/ErrorContainer.styled.ts index b0b3556f97..8a987d39a2 100644 --- a/packages/shared/components/error-container/ErrorContainer.styled.ts +++ b/packages/shared/components/error-container/ErrorContainer.styled.ts @@ -40,7 +40,7 @@ const StyledErrorContainer = styled.div<{ isEditor: boolean }>` css` position: absolute; `} - overflow-x: hidden; + overflow: hidden; display: flex; flex-direction: column; align-items: center; diff --git a/packages/shared/components/media-viewer/sub-components/ImageViewer/index.tsx b/packages/shared/components/media-viewer/sub-components/ImageViewer/index.tsx index 1bbd458427..9f33ad2172 100644 --- a/packages/shared/components/media-viewer/sub-components/ImageViewer/index.tsx +++ b/packages/shared/components/media-viewer/sub-components/ImageViewer/index.tsx @@ -996,6 +996,7 @@ export const ImageViewer = ({ onDoubleClick={handleDoubleTapOrClick} onLoad={imageLoaded} onError={onError} + onContextMenu={(event) => event.preventDefault()} /> diff --git a/packages/shared/components/media-viewer/sub-components/PlayerBigPlayButton/index.tsx b/packages/shared/components/media-viewer/sub-components/PlayerBigPlayButton/index.tsx index 67c79059f9..8876178cc4 100644 --- a/packages/shared/components/media-viewer/sub-components/PlayerBigPlayButton/index.tsx +++ b/packages/shared/components/media-viewer/sub-components/PlayerBigPlayButton/index.tsx @@ -36,7 +36,9 @@ export const PlayerBigPlayButton = ({ if (!visible) return; return ( - + event.preventDefault()} + > ); diff --git a/packages/shared/components/media-viewer/sub-components/ViewerPlayer/index.tsx b/packages/shared/components/media-viewer/sub-components/ViewerPlayer/index.tsx index e908d44628..2845b7db6d 100644 --- a/packages/shared/components/media-viewer/sub-components/ViewerPlayer/index.tsx +++ b/packages/shared/components/media-viewer/sub-components/ViewerPlayer/index.tsx @@ -406,6 +406,10 @@ export const ViewerPlayer = ({ const percent = Number(event.target.value); const newCurrentTime = (percent / 100) * videoRef.current.duration; + const videoCurrentTime = videoRef.current.currentTime; + + if (Math.abs(newCurrentTime - videoCurrentTime) <= 0.1) return; + handleProgress(); setTimeline(percent); setCurrentTime(newCurrentTime); @@ -653,6 +657,7 @@ export const ViewerPlayer = ({ onDurationChange={handleDurationChange} onLoadedMetadata={handleLoadedMetaDataVideo} onPlay={() => setIsPlaying(true)} + onContextMenu={(event) => event.preventDefault()} /> ) : null; - const children = tariffBar ? React.cloneElement(tariffBar, { title }) : null; + const children = + tariffBar && !isFrame ? React.cloneElement(tariffBar, { title }) : null; const isTabletView = isTablet(); const contextOptionsFolder = getContextOptionsFolder(); diff --git a/packages/shared/components/section/Section.types.ts b/packages/shared/components/section/Section.types.ts index 061b602129..90ad52348f 100644 --- a/packages/shared/components/section/Section.types.ts +++ b/packages/shared/components/section/Section.types.ts @@ -145,3 +145,7 @@ export interface SectionProps { isDesktop?: boolean; getContextModel?: () => ContextMenuModel[]; } + +export interface SectionContextMenuProps { + getContextModel: () => ContextMenuModel[]; +} diff --git a/packages/shared/components/section/sub-components/SectionBody.tsx b/packages/shared/components/section/sub-components/SectionBody.tsx index 914a2e92c6..eba713e65f 100644 --- a/packages/shared/components/section/sub-components/SectionBody.tsx +++ b/packages/shared/components/section/sub-components/SectionBody.tsx @@ -29,14 +29,13 @@ import { useLocation } from "react-router-dom"; // import { inject, observer } from "mobx-react"; -import { ContextMenu } from "@docspace/shared/components/context-menu"; - import { StyledDropZoneBody, StyledSpacer, StyledSectionBody, } from "../Section.styled"; import { SectionBodyProps } from "../Section.types"; +import SectionContextMenu from "./SectionContextMenu"; const SectionBody = React.memo( ({ @@ -53,49 +52,9 @@ const SectionBody = React.memo( getContextModel, }: SectionBodyProps) => { const focusRef = React.useRef(null); - const cmRef = React.useRef void; - hide: (e: React.MouseEvent | MouseEvent) => void; - toggle: (e: React.MouseEvent | MouseEvent) => boolean; - getVisible: () => boolean; - }>(null); + const location = useLocation(); - const [isOpen, setIsOpen] = React.useState(false); - - const onContextMenu = React.useCallback( - (e: MouseEvent | React.MouseEvent) => { - const bodyElem = document.getElementsByClassName( - "section-body", - )[0] as HTMLDivElement; - - const target = e.target as Node; - - if ( - !getContextModel || - !getContextModel() || - !bodyElem || - !bodyElem.contains(target) - ) - return; - - e.stopPropagation(); - e.preventDefault(); - - // if (cmRef.current) cmRef.current.toggle(e); - if (cmRef.current) { - if (!isOpen) cmRef?.current?.show(e); - else cmRef?.current?.hide(e); - setIsOpen(!isOpen); - } - }, - [getContextModel, isOpen], - ); - - const onHide = () => { - setIsOpen(false); - }; - const focusSectionBody = React.useCallback(() => { if (focusRef.current) focusRef.current.focus({ preventScroll: true }); }, []); @@ -108,14 +67,6 @@ const SectionBody = React.memo( [focusSectionBody], ); - React.useEffect(() => { - document.addEventListener("contextmenu", onContextMenu); - - return () => { - document.removeEventListener("contextmenu", onContextMenu); - }; - }, [onContextMenu]); - React.useEffect(() => { if (!autoFocus) return; @@ -145,16 +96,6 @@ const SectionBody = React.memo( } : {}; - const contextBlock = ( - - ); - return uploadFiles ? ( )} - {contextBlock} + ) : ( {children}
)} - {contextBlock} + ); }, diff --git a/packages/shared/components/section/sub-components/SectionContextMenu.tsx b/packages/shared/components/section/sub-components/SectionContextMenu.tsx new file mode 100644 index 0000000000..7dafb4b03a --- /dev/null +++ b/packages/shared/components/section/sub-components/SectionContextMenu.tsx @@ -0,0 +1,80 @@ +import React from "react"; +import { ContextMenu } from "@docspace/shared/components/context-menu"; +import isEqual from "lodash/isEqual"; +import { SectionContextMenuProps } from "../Section.types"; + +const areEqual = ( + prevProps: SectionContextMenuProps, + nextProps: SectionContextMenuProps, +) => { + if (!isEqual(prevProps, nextProps)) return true; + return false; +}; + +const SectionContextMenu = React.memo( + ({ getContextModel }: SectionContextMenuProps) => { + const [isOpen, setIsOpen] = React.useState(false); + + const cmRef = React.useRef void; + hide: (e: React.MouseEvent | MouseEvent) => void; + toggle: (e: React.MouseEvent | MouseEvent) => boolean; + getVisible: () => boolean; + }>(null); + + const onHide = () => { + setIsOpen(false); + }; + + const onContextMenu = React.useCallback( + (e: MouseEvent | React.MouseEvent) => { + const bodyElem = document.getElementsByClassName( + "section-body", + )[0] as HTMLDivElement; + + const target = e.target as Node; + + if ( + !getContextModel || + !getContextModel() || + !bodyElem || + !bodyElem.contains(target) + ) + return; + + e.stopPropagation(); + e.preventDefault(); + + // if (cmRef.current) cmRef.current.toggle(e); + if (cmRef.current) { + if (!isOpen) cmRef?.current?.show(e); + else cmRef?.current?.hide(e); + setIsOpen(!isOpen); + } + }, + [getContextModel, isOpen], + ); + + React.useEffect(() => { + document.addEventListener("contextmenu", onContextMenu); + + return () => { + document.removeEventListener("contextmenu", onContextMenu); + }; + }, [onContextMenu]); + + return ( + + ); + }, + areEqual, +); +SectionContextMenu.displayName = "SectionContextMenu"; + +export default SectionContextMenu; diff --git a/packages/shared/types/Frame.ts b/packages/shared/types/Frame.ts index 1dd42178ad..469ff145df 100644 --- a/packages/shared/types/Frame.ts +++ b/packages/shared/types/Frame.ts @@ -75,6 +75,7 @@ export type TFrameEvents = { onAuthSuccess: null | ((e: Event) => void); onSignOut: null | ((e: Event) => void); onDownload: null | ((e: Event) => void); + onNoAccess: null | ((e: Event) => void); }; export type TFrameConfig = { @@ -96,6 +97,7 @@ export type TFrameConfig = { showSelectorCancel: boolean; showSelectorHeader: boolean; showHeader: boolean; + showHeaderBanner: string; showTitle: boolean; showMenu: boolean; showFilter: boolean; diff --git a/packages/shared/utils/axiosClient.ts b/packages/shared/utils/axiosClient.ts index c035f9e298..cec4503331 100644 --- a/packages/shared/utils/axiosClient.ts +++ b/packages/shared/utils/axiosClient.ts @@ -220,10 +220,12 @@ class AxiosClient { } const loginURL = combineUrl(proxyURL, "/login"); + if (!this.isSSR) { switch (error.response?.status) { case 401: { - if (options.skipUnauthorized) return Promise.resolve(); + if (options.skipUnauthorized || window?.ClientConfig?.isFrame) + return Promise.resolve(); if (options.skipLogout) return Promise.reject(error); const opt: AxiosRequestConfig = { @@ -244,14 +246,13 @@ class AxiosClient { break; case 403: { const pathname = window.location.pathname; - const isFrame = window?.ClientConfig?.isFrame; const isArchived = pathname.indexOf("/rooms/archived") !== -1; const isRooms = pathname.indexOf("/rooms/shared") !== -1 || isArchived; - if (isRooms && !skipRedirect && !isFrame) { + if (isRooms && !skipRedirect && !window?.ClientConfig?.isFrame) { setTimeout(() => { window.DocSpace.navigate(isArchived ? "/archived" : "/"); }, 1000); diff --git a/public/scripts/sdk/1.0.1/api.js b/public/scripts/sdk/1.0.1/api.js index ae265191e0..a5247f8a0f 100644 --- a/public/scripts/sdk/1.0.1/api.js +++ b/public/scripts/sdk/1.0.1/api.js @@ -53,7 +53,7 @@ showSignOut: true, destroyText: "", viewAs: "row", //TODO: ["row", "table", "tile"] - viewTableColumns: "Name,Type,Tags", + viewTableColumns: "Name,Size,Type,Tags", checkCSP: true, disableActionButton: false, showSettings: false, @@ -97,6 +97,8 @@ onAuthSuccess: null, onSignOut: null, onDownload: null, + onNoAccess: null, + onContentReady: null, }, }; @@ -813,7 +815,10 @@ targetFrame.style.height = this.config.height; targetFrame.parentNode.style.height = "inherit"; - if (loader) loader.remove(); + if (loader) { + loader.remove(); + this.config.events.onContentReady(); + } } }