Merge branch 'hotfix/v2.6.1' into bugfix/Bug69601

This commit is contained in:
Viktor Fomin 2024-08-16 12:59:41 +03:00
commit df02095c37
37 changed files with 307 additions and 216 deletions

View File

@ -117,6 +117,7 @@
"OpenLocation": "Open location", "OpenLocation": "Open location",
"PasswordAccess": "Password access", "PasswordAccess": "Password access",
"PasswordLink": "Add a password to protect your link.", "PasswordLink": "Add a password to protect your link.",
"PasswordProtectedFiles": "Please <0>enter a password</0> for the protected files to continue",
"PasswordSuccessfullyCopied": "Password successfully copied", "PasswordSuccessfullyCopied": "Password successfully copied",
"Pin": "Pin", "Pin": "Pin",
"PinToTop": "Pin to top", "PinToTop": "Pin to top",

View File

@ -62,21 +62,14 @@ export default function withFileActions(WrappedFileItem) {
}; };
onDropZoneUpload = (files, uploadToFolder) => { onDropZoneUpload = (files, uploadToFolder) => {
const { t, dragging, setDragging, startUpload, uploadEmptyFolders } = const { t, dragging, setDragging, startUpload, createFoldersTree } =
this.props; this.props;
dragging && setDragging(false); dragging && setDragging(false);
const emptyFolders = files.filter((f) => f.isEmptyDirectory); createFoldersTree(files, uploadToFolder).then((f) => {
if (f.length > 0) startUpload(f, null, t);
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);
}
}; };
onDrop = (items) => { onDrop = (items) => {
@ -371,7 +364,7 @@ export default function withFileActions(WrappedFileItem) {
onSelectItem, onSelectItem,
//setNewBadgeCount, //setNewBadgeCount,
openFileAction, openFileAction,
uploadEmptyFolders, createFoldersTree,
} = filesActionsStore; } = filesActionsStore;
const { setSharingPanelVisible } = dialogsStore; const { setSharingPanelVisible } = dialogsStore;
const { const {
@ -475,7 +468,7 @@ export default function withFileActions(WrappedFileItem) {
dragging, dragging,
setDragging, setDragging,
startUpload, startUpload,
uploadEmptyFolders, createFoldersTree,
draggable, draggable,
setTooltipPosition, setTooltipPosition,
setStartDrag, setStartDrag,

View File

@ -68,7 +68,7 @@ const Item = ({
onBadgeClick, onBadgeClick,
showDragItems, showDragItems,
startUpload, startUpload,
uploadEmptyFolders, createFoldersTree,
setDragging, setDragging,
showBadge, showBadge,
labelBadge, labelBadge,
@ -86,18 +86,12 @@ const Item = ({
const onDropZoneUpload = React.useCallback( const onDropZoneUpload = React.useCallback(
(files, uploadToFolder) => { (files, uploadToFolder) => {
dragging && setDragging(false); dragging && setDragging(false);
const emptyFolders = files.filter((f) => f.isEmptyDirectory);
if (emptyFolders.length > 0) { createFoldersTree(files, uploadToFolder).then((f) => {
uploadEmptyFolders(emptyFolders, uploadToFolder).then(() => { if (f.length > 0) startUpload(f, null, t);
const onlyFiles = files.filter((f) => !f.isEmptyDirectory); });
if (onlyFiles.length > 0) startUpload(onlyFiles, uploadToFolder, t);
});
} else {
startUpload(files, uploadToFolder, t);
}
}, },
[t, dragging, setDragging, startUpload, uploadEmptyFolders], [t, dragging, setDragging, startUpload, createFoldersTree],
); );
const onDrop = React.useCallback( const onDrop = React.useCallback(
@ -193,7 +187,7 @@ const Items = ({
dragging, dragging,
setDragging, setDragging,
startUpload, startUpload,
uploadEmptyFolders, createFoldersTree,
isVisitor, isVisitor,
isCollaborator, isCollaborator,
isAdmin, isAdmin,
@ -326,7 +320,7 @@ const Items = ({
t={t} t={t}
setDragging={setDragging} setDragging={setDragging}
startUpload={startUpload} startUpload={startUpload}
uploadEmptyFolders={uploadEmptyFolders} createFoldersTree={createFoldersTree}
item={item} item={item}
setBufferSelection={setBufferSelection} setBufferSelection={setBufferSelection}
dragging={dragging} dragging={dragging}
@ -388,7 +382,7 @@ const Items = ({
showText, showText,
setDragging, setDragging,
startUpload, startUpload,
uploadEmptyFolders, createFoldersTree,
trashIsEmpty, trashIsEmpty,
isAdmin, isAdmin,
isVisitor, isVisitor,
@ -449,7 +443,7 @@ export default inject(
const { id, access: folderAccess } = selectedFolderStore; const { id, access: folderAccess } = selectedFolderStore;
const { const {
moveDragItems, moveDragItems,
uploadEmptyFolders, createFoldersTree,
deleteAction, deleteAction,
emptyTrashInProgress, emptyTrashInProgress,
} = filesActionsStore; } = filesActionsStore;
@ -478,7 +472,7 @@ export default inject(
setBufferSelection, setBufferSelection,
deleteAction, deleteAction,
startUpload, startUpload,
uploadEmptyFolders, createFoldersTree,
setEmptyTrashDialogVisible, setEmptyTrashDialogVisible,
trashIsEmpty, trashIsEmpty,

View File

@ -70,6 +70,7 @@ import { resendInvitesAgain } from "@docspace/shared/api/people";
import { getCorrectFourValuesStyle } from "@docspace/shared/utils"; import { getCorrectFourValuesStyle } from "@docspace/shared/utils";
import { ArticleButtonLoader } from "@docspace/shared/skeletons/article"; import { ArticleButtonLoader } from "@docspace/shared/skeletons/article";
import { isMobile, isTablet } from "react-device-detect"; import { isMobile, isTablet } from "react-device-detect";
import getFilesFromEvent from "@docspace/shared/components/drag-and-drop/get-files-from-event";
const StyledButton = styled(Button)` const StyledButton = styled(Button)`
font-weight: 700; font-weight: 700;
@ -180,6 +181,7 @@ const ArticleMainButtonContent = (props) => {
parentRoomType, parentRoomType,
isFolder, isFolder,
createFoldersTree,
} = props; } = props;
const navigate = useNavigate(); const navigate = useNavigate();
@ -239,8 +241,12 @@ const ArticleMainButtonContent = (props) => {
); );
const onFileChange = React.useCallback( const onFileChange = React.useCallback(
(e) => { async (e) => {
startUpload(e.target.files, null, t); const files = await getFilesFromEvent(e);
createFoldersTree(files).then((f) => {
if (f.length > 0) startUpload(f, null, t);
});
}, },
[startUpload, t], [startUpload, t],
); );
@ -249,7 +255,7 @@ const ArticleMainButtonContent = (props) => {
if (isPrivacy) { if (isPrivacy) {
encryptionUploadDialog((encryptedFile, encrypted) => { encryptionUploadDialog((encryptedFile, encrypted) => {
encryptedFile.encrypted = encrypted; encryptedFile.encrypted = encrypted;
startUpload([encryptedFile], null, t); startUpload([encryptedFile], null, t); // TODO: createFoldersTree
}); });
} else { } else {
inputFilesElement.current.click(); inputFilesElement.current.click();
@ -901,6 +907,7 @@ export default inject(
versionHistoryStore, versionHistoryStore,
userStore, userStore,
currentTariffStatusStore, currentTariffStatusStore,
filesActionsStore,
}) => { }) => {
const { showArticleLoader } = clientLoadingStore; const { showArticleLoader } = clientLoadingStore;
const { mainButtonMobileVisible } = filesStore; const { mainButtonMobileVisible } = filesStore;
@ -945,6 +952,8 @@ export default inject(
const { frameConfig, isFrame } = settingsStore; const { frameConfig, isFrame } = settingsStore;
const { createFoldersTree } = filesActionsStore;
return { return {
isGracePeriod, isGracePeriod,
setInviteUsersWarningDialogVisible, setInviteUsersWarningDialogVisible,
@ -997,6 +1006,7 @@ export default inject(
isFolder, isFolder,
selectFileFormRoomDialogVisible, selectFileFormRoomDialogVisible,
setSelectFileFormRoomDialogVisible, setSelectFileFormRoomDialogVisible,
createFoldersTree,
}; };
}, },
)( )(

View File

@ -38,6 +38,7 @@ import { IconButton } from "@docspace/shared/components/icon-button";
import RoomsFilter from "@docspace/shared/api/rooms/filter"; import RoomsFilter from "@docspace/shared/api/rooms/filter";
import { RoomSearchArea } from "@docspace/shared/enums"; import { RoomSearchArea } from "@docspace/shared/enums";
import { frameCallEvent } from "@docspace/shared/utils/common";
import { getCategoryUrl } from "SRC_DIR/helpers/utils"; import { getCategoryUrl } from "SRC_DIR/helpers/utils";
import { CategoryType } from "SRC_DIR/helpers/constants"; import { CategoryType } from "SRC_DIR/helpers/constants";
@ -60,6 +61,8 @@ const RoomNoAccessContainer = (props) => {
const navigate = useNavigate(); const navigate = useNavigate();
React.useEffect(() => { React.useEffect(() => {
frameCallEvent({ event: "onNoAccess" });
const timer = setTimeout(onGoToShared, 5000); const timer = setTimeout(onGoToShared, 5000);
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, []); }, []);

View File

@ -88,7 +88,7 @@ const ExternalLinks = ({
copyLink(link.shareLink); copyLink(link.shareLink);
} }
} else { } else {
!externalLinksVisible ? editLink() : disableLink(); !externalLinksVisible ? await editLink() : await disableLink();
} }
onChangeExternalLinksVisible(!externalLinksVisible); onChangeExternalLinksVisible(!externalLinksVisible);
} catch (error) { } catch (error) {
@ -98,9 +98,10 @@ const ExternalLinks = ({
} }
}; };
const disableLink = () => { const disableLink = async () => {
setInvitationLinks(roomId, "Invite", 0, shareLinks[0].id); shareLinks?.length &&
setShareLinks([]); (await setInvitationLinks(roomId, "Invite", 0, shareLinks[0].id));
return setShareLinks([]);
}; };
const editLink = async () => { const editLink = async () => {
@ -120,7 +121,7 @@ const ExternalLinks = ({
copyLink(shareLink); copyLink(shareLink);
setShareLinks([activeLink]); setShareLinks([activeLink]);
setActiveLink(activeLink); return setActiveLink(activeLink);
}; };
const onSelectAccess = async (access) => { const onSelectAccess = async (access) => {

View File

@ -54,7 +54,10 @@ export const GreetingContainer = styled.div`
} }
.tooltip { .tooltip {
p { .invitation-text {
display: flex;
flex-direction: column;
align-items: center;
text-align: center; text-align: center;
} }
@ -65,15 +68,13 @@ export const GreetingContainer = styled.div`
.portal-logo { .portal-logo {
width: 100%; width: 100%;
max-width: 386px;
height: 44px;
margin: 0 auto;
padding-bottom: 16px; padding-bottom: 16px;
height: 26.56px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.injected-svg {
height: 26.56px;
}
} }
`; `;

View File

@ -485,7 +485,7 @@ const CreateUserForm = (props) => {
<PortalLogo className="portal-logo" /> <PortalLogo className="portal-logo" />
{linkData.type === "LinkInvite" && ( {linkData.type === "LinkInvite" && (
<div className="tooltip"> <div className="tooltip">
<Text fontSize="16px"> <Text fontSize="16px" as="div" className="invitation-text">
{roomName ? ( {roomName ? (
<Trans <Trans
t={t} t={t}

View File

@ -97,6 +97,12 @@ const StyledForm = styled(Box)`
.set-app-description { .set-app-description {
width: 100%; width: 100%;
max-width: 500px; max-width: 500px;
.portal-logo {
margin: 0 auto;
max-width: 386px;
height: 44px;
}
} }
.set-app-title { .set-app-title {

View File

@ -52,7 +52,7 @@ const useFiles = ({
dragging, dragging,
setDragging, setDragging,
disableDrag, disableDrag,
uploadEmptyFolders, createFoldersTree,
startUpload, startUpload,
fetchFiles, fetchFiles,
@ -118,16 +118,9 @@ const useFiles = ({
if (disableDrag) return; if (disableDrag) return;
const emptyFolders = files.filter((f) => f.isEmptyDirectory); createFoldersTree(files, uploadToFolder).then((f) => {
if (f.length > 0) startUpload(f, null, t);
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);
}
}; };
React.useEffect(() => { React.useEffect(() => {

View File

@ -264,8 +264,8 @@ const StyledProperties = styled.div`
grid-template-columns: 120px 1fr; grid-template-columns: 120px 1fr;
grid-column-gap: 24px; grid-column-gap: 24px;
-webkit-box-align: center; -webkit-box-align: baseline;
align-items: center; align-items: baseline;
.property-title { .property-title {
font-size: 13px; font-size: 13px;

View File

@ -138,15 +138,8 @@ class InsideGroupTableHeader extends React.Component {
newFilter.sortBy = sortBy; newFilter.sortBy = sortBy;
if (sortBy === "AZ") { if (sortBy === "AZ") {
if ( if (newFilter.sortBy !== "displayname") {
newFilter.sortBy !== "lastname" && newFilter.sortBy = "displayname";
newFilter.sortBy !== "firstname"
) {
newFilter.sortBy = "firstname";
} else if (newFilter.sortBy === "lastname") {
newFilter.sortBy = "firstname";
} else {
newFilter.sortBy = "lastname";
} }
newFilter.sortOrder = newFilter.sortOrder =
newFilter.sortOrder === "ascending" ? "descending" : "ascending"; newFilter.sortOrder === "ascending" ? "descending" : "ascending";
@ -176,10 +169,7 @@ class InsideGroupTableHeader extends React.Component {
} = this.props; } = this.props;
const { sortOrder } = filter; const { sortOrder } = filter;
const sortBy = const sortBy = filter.sortBy === "displayname" ? "AZ" : filter.sortBy;
filter.sortBy === "firstname" || filter.sortBy === "lastname"
? "AZ"
: filter.sortBy;
return ( return (
<TableHeader <TableHeader

View File

@ -143,7 +143,7 @@ class PeopleTableHeader extends React.Component {
if ( if (
newFilter.sortBy === sortBy || newFilter.sortBy === sortBy ||
(sortBy === "AZ" && newFilter.sortBy === "firstname") (sortBy === "AZ" && newFilter.sortBy === "displayname")
) { ) {
newFilter.sortOrder = newFilter.sortOrder =
newFilter.sortOrder === "ascending" ? "descending" : "ascending"; newFilter.sortOrder === "ascending" ? "descending" : "ascending";
@ -151,7 +151,7 @@ class PeopleTableHeader extends React.Component {
newFilter.sortBy = sortBy; newFilter.sortBy = sortBy;
if (sortBy === "AZ") { if (sortBy === "AZ") {
newFilter.sortBy = "firstname"; newFilter.sortBy = "displayname";
} }
} }
@ -178,10 +178,7 @@ class PeopleTableHeader extends React.Component {
} = this.props; } = this.props;
const { sortOrder } = filter; const { sortOrder } = filter;
const sortBy = const sortBy = filter.sortBy === "displayname" ? "AZ" : filter.sortBy;
filter.sortBy === "firstname" || filter.sortBy === "lastname"
? "AZ"
: filter.sortBy;
return ( return (
<TableHeader <TableHeader

View File

@ -1961,16 +1961,9 @@ const SectionFilterContent = ({
const options = []; const options = [];
const firstName = { const firstName = {
id: "sort-by_first-name", id: "sort-by_displayname",
key: "firstname", key: "displayname",
label: t("Common:FirstName"), label: t("Common:Name"),
default: true,
};
const lastName = {
id: "sort-by_last-name",
key: "lastname",
label: t("Common:LastName"),
default: true, default: true,
}; };
@ -2012,7 +2005,7 @@ const SectionFilterContent = ({
hideableColumns.Storage = storage; hideableColumns.Storage = storage;
} }
options.push(firstName, lastName, type, department, email); options.push(firstName, type, department, email);
if (showStorageInfo) options.push(storage); if (showStorageInfo) options.push(storage);
return options; return options;

View File

@ -50,6 +50,7 @@ import {
getCategoryUrl, getCategoryUrl,
} from "SRC_DIR/helpers/utils"; } from "SRC_DIR/helpers/utils";
import TariffBar from "SRC_DIR/components/TariffBar"; import TariffBar from "SRC_DIR/components/TariffBar";
import getFilesFromEvent from "@docspace/shared/components/drag-and-drop/get-files-from-event";
const StyledContainer = styled.div` const StyledContainer = styled.div`
width: 100%; width: 100%;
@ -225,6 +226,7 @@ const SectionHeaderContent = (props) => {
getHeaderOptions, getHeaderOptions,
setBufferSelection, setBufferSelection,
setGroupsBufferSelection, setGroupsBufferSelection,
createFoldersTree,
} = props; } = props;
const location = useLocation(); const location = useLocation();
@ -239,8 +241,12 @@ const SectionHeaderContent = (props) => {
const isSettingsPage = location.pathname.includes("/settings"); const isSettingsPage = location.pathname.includes("/settings");
const onFileChange = React.useCallback( const onFileChange = React.useCallback(
(e) => { async (e) => {
startUpload(e.target.files, null, t); const files = await getFilesFromEvent(e);
createFoldersTree(files).then((f) => {
if (f.length > 0) startUpload(f, null, t);
});
}, },
[startUpload, t], [startUpload, t],
); );
@ -639,6 +645,7 @@ export default inject(
moveToRoomsPage, moveToRoomsPage,
onClickBack, onClickBack,
moveToPublicRoom, moveToPublicRoom,
createFoldersTree,
} = filesActionsStore; } = filesActionsStore;
const { setIsVisible, isVisible } = infoPanelStore; const { setIsVisible, isVisible } = infoPanelStore;
@ -802,6 +809,7 @@ export default inject(
getHeaderOptions, getHeaderOptions,
setBufferSelection, setBufferSelection,
setGroupsBufferSelection, setGroupsBufferSelection,
createFoldersTree,
}; };
}, },
)( )(

View File

@ -82,7 +82,7 @@ const PureHome = (props) => {
startUpload, startUpload,
setDragging, setDragging,
dragging, dragging,
uploadEmptyFolders, createFoldersTree,
disableDrag, disableDrag,
uploaded, uploaded,
converted, converted,
@ -182,7 +182,7 @@ const PureHome = (props) => {
dragging, dragging,
setDragging, setDragging,
disableDrag, disableDrag,
uploadEmptyFolders, createFoldersTree,
startUpload, startUpload,
fetchFiles, fetchFiles,
fetchRooms, fetchRooms,
@ -543,7 +543,7 @@ export default inject(
const { setUploadPanelVisible, startUpload, uploaded, converted } = const { setUploadPanelVisible, startUpload, uploaded, converted } =
uploadDataStore; uploadDataStore;
const { uploadEmptyFolders, onClickBack } = filesActionsStore; const { createFoldersTree, onClickBack } = filesActionsStore;
const selectionLength = isProgressFinished ? selection.length : null; const selectionLength = isProgressFinished ? selection.length : null;
const selectionTitle = isProgressFinished const selectionTitle = isProgressFinished
@ -635,7 +635,7 @@ export default inject(
setUploadPanelVisible, setUploadPanelVisible,
startUpload, startUpload,
uploadEmptyFolders, createFoldersTree,
setToPreviewFile, setToPreviewFile,
setIsPreview, setIsPreview,

View File

@ -130,6 +130,7 @@ const Manager = (props) => {
); );
const [selectedColumns, setSelectedColumns] = useState([ const [selectedColumns, setSelectedColumns] = useState([
{ key: "Name", label: t("Common:Name") }, { key: "Name", label: t("Common:Name") },
{ key: "Size", label: t("Common:Size") },
{ key: "Type", label: t("Common:Type") }, { key: "Type", label: t("Common:Type") },
{ key: "Tags", label: t("Common:Tags") }, { key: "Tags", label: t("Common:Tags") },
]); ]);

View File

@ -125,6 +125,17 @@ class EditGroupStore {
const addedIds = Array.from(this.addedMembersMap.keys()); const addedIds = Array.from(this.addedMembersMap.keys());
const removedIds = Array.from(this.removedMembersMap.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( await updateGroup(
this.group?.id, this.group?.id,

View File

@ -229,17 +229,21 @@ class FilesActionStore {
let level = { result }; let level = { result };
try { try {
folders.forEach((folder) => { folders.forEach((folder) => {
folder.path const folderPath = folder.path.split("/").filter((name) => name !== "");
.split("/")
.filter((name) => name !== "")
.reduce((r, name, i, a) => {
if (!r[name]) {
r[name] = { result: [] };
r.result.push({ name, children: r[name].result });
}
return r[name]; folderPath.reduce((r, name, i, a) => {
}, level); 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) { } catch (e) {
console.error("convertToTree", e); console.error("convertToTree", e);
@ -247,52 +251,67 @@ class FilesActionStore {
return result; return result;
}; };
createFolderTree = async (treeList, parentFolderId) => { createFolderTree = async (treeList, parentFolderId, filesList) => {
if (!treeList || !treeList.length) return; if (!treeList || !treeList.length) return;
for (let i = 0; i < treeList.length; i++) { for (let i = 0; i < treeList.length; i++) {
const treeNode = treeList[i]; const treeNode = treeList[i];
const isFile = treeList[i].isFile;
// console.log( // console.log(
// `createFolderTree parent id = ${parentFolderId} name '${treeNode.name}': `, // `createFolderTree parent id = ${parentFolderId} name '${treeNode.name}': `,
// treeNode.children // treeNode.children
// ); // );
if (isFile) {
treeList[i].file.parentFolderId = parentFolderId;
filesList.push(treeList[i].file);
continue;
}
const folder = await createFolder(parentFolderId, treeNode.name); const folder = await createFolder(parentFolderId, treeNode.name);
const parentId = folder.id; const parentId = folder.id;
if (treeNode.children.length == 0) continue; 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) => { createFoldersTree = async (files, folderId) => {
//console.log("uploadEmptyFolders", emptyFolders, folderId); //console.log("createFoldersTree", files, folderId);
const { secondaryProgressDataStore } = this.uploadDataStore; const { primaryProgressDataStore } = this.uploadDataStore;
const { setSecondaryProgressBarData, clearSecondaryProgressData } =
secondaryProgressDataStore; const { setPrimaryProgressBarData, clearPrimaryProgressData } =
primaryProgressDataStore;
const operationId = uniqueid("operation_"); const operationId = uniqueid("operation_");
const toFolderId = folderId ? folderId : this.selectedFolderStore.id; const toFolderId = folderId ? folderId : this.selectedFolderStore.id;
setSecondaryProgressBarData({ setPrimaryProgressBarData({
icon: "file", icon: "upload",
visible: true, visible: true,
percent: 0, percent: 0,
label: "", label: "",
alert: false, alert: false,
operationId,
}); });
const tree = this.convertToTree(emptyFolders); const tree = this.convertToTree(files);
await this.createFolderTree(tree, toFolderId);
const filesList = [];
await this.createFolderTree(tree, toFolderId, filesList);
this.updateCurrentFolder(null, [folderId], null, operationId); this.updateCurrentFolder(null, [folderId], null, operationId);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT); if (!filesList.length) {
setTimeout(() => clearPrimaryProgressData(), TIMEOUT);
}
return filesList;
}; };
updateFilesAfterDelete = (operationId) => { updateFilesAfterDelete = (operationId) => {

View File

@ -683,9 +683,8 @@ class HotkeyStore {
}; };
uploadClipboardFiles = async (t, event) => { uploadClipboardFiles = async (t, event) => {
const { uploadEmptyFolders } = this.filesActionsStore; const { createFoldersTree } = this.filesActionsStore;
const { startUpload } = this.uploadDataStore; const { startUpload } = this.uploadDataStore;
const currentFolderId = this.selectedFolderStore.id;
if (this.filesStore.hotkeysClipboard.length) { if (this.filesStore.hotkeysClipboard.length) {
return this.moveFilesFromClipboard(t); return this.moveFilesFromClipboard(t);
@ -693,16 +692,9 @@ class HotkeyStore {
const files = await getFilesFromEvent(event); const files = await getFilesFromEvent(event);
const emptyFolders = files.filter((f) => f.isEmptyDirectory); createFoldersTree(files, uploadToFolder).then((f) => {
if (f.length > 0) startUpload(f, null, t);
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);
}
}; };
get countTilesInRow() { get countTilesInRow() {

View File

@ -489,7 +489,7 @@ class LdapFormStore {
completed: true, completed: true,
percents: 100, percents: 100,
certificateConfirmRequest: null, certificateConfirmRequest: null,
error: "", error: t("Common:UnexpectedError"),
}; };
} }
@ -518,7 +518,6 @@ class LdapFormStore {
toastr.success(t("Common:SuccessfullyCompletedOperation")); toastr.success(t("Common:SuccessfullyCompletedOperation"));
} }
} catch (error) { } catch (error) {
console.error(error);
toastr.error(error); toastr.error(error);
this.endProcess(); this.endProcess();
} }

View File

@ -347,7 +347,7 @@ class ProfileActionsStore {
let bookTraining = null; let bookTraining = null;
if (!isMobile && this.isTeamTrainingAlertAvailable) { if (!isMobile && this.authStore.isTeamTrainingAlertAvailable) {
bookTraining = { bookTraining = {
key: "user-menu-book-training", key: "user-menu-book-training",
icon: BookTrainingReactSvgUrl, icon: BookTrainingReactSvgUrl,

View File

@ -25,9 +25,11 @@
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { makeAutoObservable, runInAction } from "mobx"; import { makeAutoObservable, runInAction } from "mobx";
import { Trans } from "react-i18next";
import { TIMEOUT } from "@docspace/client/src/helpers/filesConstants"; import { TIMEOUT } from "@docspace/client/src/helpers/filesConstants";
import uniqueid from "lodash/uniqueId"; import uniqueid from "lodash/uniqueId";
import sumBy from "lodash/sumBy"; import sumBy from "lodash/sumBy";
import uniqBy from "lodash/uniqBy";
import { ConflictResolveType } from "@docspace/shared/enums"; import { ConflictResolveType } from "@docspace/shared/enums";
import { import {
getFileInfo, getFileInfo,
@ -57,6 +59,8 @@ import {
getCategoryTypeByFolderType, getCategoryTypeByFolderType,
getCategoryUrl, getCategoryUrl,
} from "SRC_DIR/helpers/utils"; } from "SRC_DIR/helpers/utils";
import { Link } from "@docspace/shared/components/link";
import { globalColors } from "@docspace/shared/themes";
class UploadDataStore { class UploadDataStore {
settingsStore; settingsStore;
@ -636,7 +640,7 @@ class UploadDataStore {
if (this.uploaded || (this.isParallel && allFilesIsUploaded)) { if (this.uploaded || (this.isParallel && allFilesIsUploaded)) {
this.setConversionPercent(100); this.setConversionPercent(100);
this.finishUploadFiles(); this.finishUploadFiles(t);
} else { } else {
runInAction(() => { runInAction(() => {
this.converted = true; this.converted = true;
@ -773,7 +777,8 @@ class UploadDataStore {
file: file, file: file,
uniqueId: uniqueid("download_row-key_"), uniqueId: uniqueid("download_row-key_"),
fileId: null, fileId: null,
toFolderId, // toFolderId,
toFolderId: file.parentFolderId,
action: "upload", action: "upload",
error: file.size ? null : t("Files:EmptyFile"), error: file.size ? null : t("Files:EmptyFile"),
fileInfo: null, fileInfo: null,
@ -1263,7 +1268,7 @@ class UploadDataStore {
let files = this.files; let files = this.files;
if (files.length === 0 || this.filesSize === 0) { if (files.length === 0 || this.filesSize === 0) {
return this.finishUploadFiles(); return this.finishUploadFiles(t);
} }
const progressData = { const progressData = {
@ -1310,7 +1315,7 @@ class UploadDataStore {
} }
if (!this.filesToConversion.length) { if (!this.filesToConversion.length) {
this.finishUploadFiles(); this.finishUploadFiles(t);
} else { } else {
runInAction(() => { runInAction(() => {
this.uploaded = true; this.uploaded = true;
@ -1372,7 +1377,7 @@ class UploadDataStore {
toFolderId, toFolderId,
fileName, fileName,
fileSize, fileSize,
relativePath, "", // relativePath,
file.encrypted, file.encrypted,
file.lastModifiedDate, file.lastModifiedDate,
createNewIfExist, createNewIfExist,
@ -1512,7 +1517,7 @@ class UploadDataStore {
if (allFilesIsUploaded) { if (allFilesIsUploaded) {
if (!this.filesToConversion.length) { if (!this.filesToConversion.length) {
this.finishUploadFiles(); this.finishUploadFiles(t);
} else { } else {
runInAction(() => { runInAction(() => {
this.uploaded = true; this.uploaded = true;
@ -1542,7 +1547,7 @@ class UploadDataStore {
}); });
}; };
finishUploadFiles = () => { finishUploadFiles = (t) => {
const { fetchFiles, filter } = this.filesStore; const { fetchFiles, filter } = this.filesStore;
const { withPaging } = this.settingsStore; const { withPaging } = this.settingsStore;
@ -1554,7 +1559,7 @@ class UploadDataStore {
this.asyncUploadObj = {}; this.asyncUploadObj = {};
for (let item of this.tempFiles) { for (let item of this.tempFiles) {
const { uploadFiles, folderId, t } = item; const { uploadFiles, folderId } = item;
this.startUpload(uploadFiles, folderId, t); this.startUpload(uploadFiles, folderId, t);
} }
this.tempFiles = []; this.tempFiles = [];
@ -1562,12 +1567,36 @@ class UploadDataStore {
return; return;
} }
const totalErrorsCount = sumBy(this.files, (f) => { const filesWithErrors = this.files.filter((f) => f.error);
f.error && toastr.error(f.error);
return f.error ? 1 : 0; const totalErrorsCount = filesWithErrors.length;
});
if (totalErrorsCount > 0) { if (totalErrorsCount > 0) {
const uniqErrors = uniqBy(filesWithErrors, "error");
uniqErrors.forEach((f) =>
f.error.indexOf("password") > -1
? toastr.warning(
<Trans
i18nKey="Files:PasswordProtectedFiles"
t={t}
components={[
<Link
isHovered
color={globalColors.link}
onClick={() => {
toastr.clear();
this.setUploadPanelVisible(true);
}}
/>,
]}
/>,
null,
60000,
true,
)
: toastr.error(f.error),
);
this.primaryProgressDataStore.setPrimaryProgressBarShowError(true); // for empty file this.primaryProgressDataStore.setPrimaryProgressBarShowError(true); // for empty file
this.primaryProgressDataStore.setPrimaryProgressBarErrors( this.primaryProgressDataStore.setPrimaryProgressBarErrors(
totalErrorsCount, totalErrorsCount,

View File

@ -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<TGroup>;
};
// * Delete // * Delete
export const deleteGroup = (groupId: string) => { export const deleteGroup = (groupId: string) => {

View File

@ -29,7 +29,7 @@ import { getObjectByLocation, toUrlParams } from "../../utils/common";
const DEFAULT_PAGE = 0; const DEFAULT_PAGE = 0;
const DEFAULT_PAGE_COUNT = 25; const DEFAULT_PAGE_COUNT = 25;
const DEFAULT_TOTAL = 0; const DEFAULT_TOTAL = 0;
const DEFAULT_SORT_BY = "firstname"; const DEFAULT_SORT_BY = "displayname";
const DEFAULT_SORT_ORDER = "ascending"; const DEFAULT_SORT_ORDER = "ascending";
const DEFAULT_EMPLOYEE_STATUS = null; const DEFAULT_EMPLOYEE_STATUS = null;
const DEFAULT_ACTIVATION_STATUS = null; const DEFAULT_ACTIVATION_STATUS = null;

View File

@ -29,7 +29,7 @@ import moment from "moment";
export const getCalendarYears = (observedDate: moment.Moment) => { export const getCalendarYears = (observedDate: moment.Moment) => {
const years = []; const years = [];
const selectedYear = observedDate.year(); const selectedYear = observedDate.year();
const firstYear = selectedYear - (selectedYear % 10) - 1; const firstYear = selectedYear - 1;
for (let i = firstYear; i <= firstYear + 15; i += 1) { for (let i = firstYear; i <= firstYear + 15; i += 1) {
years.push(moment(i, "YYYY").format("YYYY")); years.push(moment(i, "YYYY").format("YYYY"));

View File

@ -40,7 +40,7 @@ const StyledErrorContainer = styled.div<{ isEditor: boolean }>`
css` css`
position: absolute; position: absolute;
`} `}
overflow-x: hidden; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;

View File

@ -996,6 +996,7 @@ export const ImageViewer = ({
onDoubleClick={handleDoubleTapOrClick} onDoubleClick={handleDoubleTapOrClick}
onLoad={imageLoaded} onLoad={imageLoaded}
onError={onError} onError={onError}
onContextMenu={(event) => event.preventDefault()}
/> />
</ImageWrapper> </ImageWrapper>
</ImageViewerContainer> </ImageViewerContainer>

View File

@ -36,7 +36,9 @@ export const PlayerBigPlayButton = ({
if (!visible) return; if (!visible) return;
return ( return (
<WrapperPlayerBigPlayButton> <WrapperPlayerBigPlayButton
onContextMenu={(event) => event.preventDefault()}
>
<BigIconPlay onClick={onClick} /> <BigIconPlay onClick={onClick} />
</WrapperPlayerBigPlayButton> </WrapperPlayerBigPlayButton>
); );

View File

@ -406,6 +406,10 @@ export const ViewerPlayer = ({
const percent = Number(event.target.value); const percent = Number(event.target.value);
const newCurrentTime = (percent / 100) * videoRef.current.duration; const newCurrentTime = (percent / 100) * videoRef.current.duration;
const videoCurrentTime = videoRef.current.currentTime;
if (Math.abs(newCurrentTime - videoCurrentTime) <= 0.1) return;
handleProgress(); handleProgress();
setTimeline(percent); setTimeline(percent);
setCurrentTime(newCurrentTime); setCurrentTime(newCurrentTime);
@ -653,6 +657,7 @@ export const ViewerPlayer = ({
onDurationChange={handleDurationChange} onDurationChange={handleDurationChange}
onLoadedMetadata={handleLoadedMetaDataVideo} onLoadedMetadata={handleLoadedMetaDataVideo}
onPlay={() => setIsPlaying(true)} onPlay={() => setIsPlaying(true)}
onContextMenu={(event) => event.preventDefault()}
/> />
<PlayerBigPlayButton <PlayerBigPlayButton
onClick={handleBigPlayButtonClick} onClick={handleBigPlayButtonClick}

View File

@ -79,7 +79,8 @@ const ControlButtons = ({
onClick={onNavigationButtonClick} onClick={onNavigationButtonClick}
/> />
) : null; ) : null;
const children = tariffBar ? React.cloneElement(tariffBar, { title }) : null; const children =
tariffBar && !isFrame ? React.cloneElement(tariffBar, { title }) : null;
const isTabletView = isTablet(); const isTabletView = isTablet();
const contextOptionsFolder = getContextOptionsFolder(); const contextOptionsFolder = getContextOptionsFolder();

View File

@ -145,3 +145,7 @@ export interface SectionProps {
isDesktop?: boolean; isDesktop?: boolean;
getContextModel?: () => ContextMenuModel[]; getContextModel?: () => ContextMenuModel[];
} }
export interface SectionContextMenuProps {
getContextModel: () => ContextMenuModel[];
}

View File

@ -29,14 +29,13 @@ import { useLocation } from "react-router-dom";
// import { inject, observer } from "mobx-react"; // import { inject, observer } from "mobx-react";
import { ContextMenu } from "@docspace/shared/components/context-menu";
import { import {
StyledDropZoneBody, StyledDropZoneBody,
StyledSpacer, StyledSpacer,
StyledSectionBody, StyledSectionBody,
} from "../Section.styled"; } from "../Section.styled";
import { SectionBodyProps } from "../Section.types"; import { SectionBodyProps } from "../Section.types";
import SectionContextMenu from "./SectionContextMenu";
const SectionBody = React.memo( const SectionBody = React.memo(
({ ({
@ -53,49 +52,9 @@ const SectionBody = React.memo(
getContextModel, getContextModel,
}: SectionBodyProps) => { }: SectionBodyProps) => {
const focusRef = React.useRef<HTMLDivElement | null>(null); const focusRef = React.useRef<HTMLDivElement | null>(null);
const cmRef = React.useRef<null | {
show: (e: React.MouseEvent | MouseEvent) => void;
hide: (e: React.MouseEvent | MouseEvent) => void;
toggle: (e: React.MouseEvent | MouseEvent) => boolean;
getVisible: () => boolean;
}>(null);
const location = useLocation(); const location = useLocation();
const [isOpen, setIsOpen] = React.useState(false);
const onContextMenu = React.useCallback(
(e: MouseEvent | React.MouseEvent<Element, 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(() => { const focusSectionBody = React.useCallback(() => {
if (focusRef.current) focusRef.current.focus({ preventScroll: true }); if (focusRef.current) focusRef.current.focus({ preventScroll: true });
}, []); }, []);
@ -108,14 +67,6 @@ const SectionBody = React.memo(
[focusSectionBody], [focusSectionBody],
); );
React.useEffect(() => {
document.addEventListener("contextmenu", onContextMenu);
return () => {
document.removeEventListener("contextmenu", onContextMenu);
};
}, [onContextMenu]);
React.useEffect(() => { React.useEffect(() => {
if (!autoFocus) return; if (!autoFocus) return;
@ -145,16 +96,6 @@ const SectionBody = React.memo(
} }
: {}; : {};
const contextBlock = (
<ContextMenu
ref={cmRef}
onHide={onHide}
getContextModel={getContextModel}
withBackdrop
model={[]}
/>
);
return uploadFiles ? ( return uploadFiles ? (
<StyledDropZoneBody <StyledDropZoneBody
isDropZone isDropZone
@ -179,7 +120,7 @@ const SectionBody = React.memo(
</div> </div>
)} )}
{contextBlock} <SectionContextMenu getContextModel={getContextModel} />
</StyledDropZoneBody> </StyledDropZoneBody>
) : ( ) : (
<StyledSectionBody <StyledSectionBody
@ -200,7 +141,7 @@ const SectionBody = React.memo(
) : ( ) : (
<div className="section-wrapper">{children}</div> <div className="section-wrapper">{children}</div>
)} )}
{contextBlock} <SectionContextMenu getContextModel={getContextModel} />
</StyledSectionBody> </StyledSectionBody>
); );
}, },

View File

@ -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<null | {
show: (e: React.MouseEvent | MouseEvent) => 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<Element, 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 (
<ContextMenu
ref={cmRef}
onHide={onHide}
getContextModel={getContextModel}
withBackdrop
model={[]}
/>
);
},
areEqual,
);
SectionContextMenu.displayName = "SectionContextMenu";
export default SectionContextMenu;

View File

@ -75,6 +75,7 @@ export type TFrameEvents = {
onAuthSuccess: null | ((e: Event) => void); onAuthSuccess: null | ((e: Event) => void);
onSignOut: null | ((e: Event) => void); onSignOut: null | ((e: Event) => void);
onDownload: null | ((e: Event) => void); onDownload: null | ((e: Event) => void);
onNoAccess: null | ((e: Event) => void);
}; };
export type TFrameConfig = { export type TFrameConfig = {
@ -96,6 +97,7 @@ export type TFrameConfig = {
showSelectorCancel: boolean; showSelectorCancel: boolean;
showSelectorHeader: boolean; showSelectorHeader: boolean;
showHeader: boolean; showHeader: boolean;
showHeaderBanner: string;
showTitle: boolean; showTitle: boolean;
showMenu: boolean; showMenu: boolean;
showFilter: boolean; showFilter: boolean;

View File

@ -220,10 +220,12 @@ class AxiosClient {
} }
const loginURL = combineUrl(proxyURL, "/login"); const loginURL = combineUrl(proxyURL, "/login");
if (!this.isSSR) { if (!this.isSSR) {
switch (error.response?.status) { switch (error.response?.status) {
case 401: { case 401: {
if (options.skipUnauthorized) return Promise.resolve(); if (options.skipUnauthorized || window?.ClientConfig?.isFrame)
return Promise.resolve();
if (options.skipLogout) return Promise.reject(error); if (options.skipLogout) return Promise.reject(error);
const opt: AxiosRequestConfig = { const opt: AxiosRequestConfig = {
@ -244,14 +246,13 @@ class AxiosClient {
break; break;
case 403: { case 403: {
const pathname = window.location.pathname; const pathname = window.location.pathname;
const isFrame = window?.ClientConfig?.isFrame;
const isArchived = pathname.indexOf("/rooms/archived") !== -1; const isArchived = pathname.indexOf("/rooms/archived") !== -1;
const isRooms = const isRooms =
pathname.indexOf("/rooms/shared") !== -1 || isArchived; pathname.indexOf("/rooms/shared") !== -1 || isArchived;
if (isRooms && !skipRedirect && !isFrame) { if (isRooms && !skipRedirect && !window?.ClientConfig?.isFrame) {
setTimeout(() => { setTimeout(() => {
window.DocSpace.navigate(isArchived ? "/archived" : "/"); window.DocSpace.navigate(isArchived ? "/archived" : "/");
}, 1000); }, 1000);

View File

@ -53,7 +53,7 @@
showSignOut: true, showSignOut: true,
destroyText: "", destroyText: "",
viewAs: "row", //TODO: ["row", "table", "tile"] viewAs: "row", //TODO: ["row", "table", "tile"]
viewTableColumns: "Name,Type,Tags", viewTableColumns: "Name,Size,Type,Tags",
checkCSP: true, checkCSP: true,
disableActionButton: false, disableActionButton: false,
showSettings: false, showSettings: false,
@ -97,6 +97,8 @@
onAuthSuccess: null, onAuthSuccess: null,
onSignOut: null, onSignOut: null,
onDownload: null, onDownload: null,
onNoAccess: null,
onContentReady: null,
}, },
}; };
@ -813,7 +815,10 @@
targetFrame.style.height = this.config.height; targetFrame.style.height = this.config.height;
targetFrame.parentNode.style.height = "inherit"; targetFrame.parentNode.style.height = "inherit";
if (loader) loader.remove(); if (loader) {
loader.remove();
this.config.events.onContentReady();
}
} }
} }