Merge pull request #566 from ONLYOFFICE/bugfix/restore-data-import
bugfix/restore-data-import
This commit is contained in:
commit
e4fb34c058
@ -51,11 +51,7 @@ const GoogleWorkspace = (props: WorkspaceProps) => {
|
||||
"Common, SMTPSettings, Settings",
|
||||
]);
|
||||
|
||||
const StepsData = getStepsData(
|
||||
t,
|
||||
filteredUsers.length === 0,
|
||||
t("Common:OrganizationName"),
|
||||
);
|
||||
const StepsData = getStepsData(t, filteredUsers.length === 0);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (migratingWorkspace === "GoogleWorkspace" && !isMigrationInit) {
|
||||
@ -68,6 +64,7 @@ const GoogleWorkspace = (props: WorkspaceProps) => {
|
||||
}
|
||||
setIsMigrationInit(true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!ready) return <SelectFileLoader />;
|
||||
@ -81,7 +78,6 @@ const GoogleWorkspace = (props: WorkspaceProps) => {
|
||||
title={StepsData[step - 1].title}
|
||||
description={StepsData[step - 1].description}
|
||||
component={StepsData[step - 1].component}
|
||||
organizationName={t("Common:OrganizationName")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -64,6 +64,7 @@ const NextcloudWorkspace = (props: WorkspaceProps) => {
|
||||
}
|
||||
setIsMigrationInit(true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!ready) return <SelectFileLoader />;
|
||||
|
@ -64,6 +64,7 @@ const OnlyofficeWorkspace = (props: WorkspaceProps) => {
|
||||
}
|
||||
setIsMigrationInit(true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!ready) return <SelectFileLoader />;
|
||||
|
@ -24,7 +24,7 @@
|
||||
// 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 styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
import { tablet, mobile } from "@docspace/shared/utils/device";
|
||||
|
||||
import { TableContainer } from "@docspace/shared/components/table";
|
||||
|
@ -29,15 +29,25 @@ import { inject, observer } from "mobx-react";
|
||||
import { tablet } from "@docspace/shared/utils/device";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link, LinkType } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { RowContainer } from "@docspace/shared/components/row-container";
|
||||
import { Row } from "@docspace/shared/components/row";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
import { TEnhancedMigrationUser } from "@docspace/shared/api/settings/types";
|
||||
import UsersRow from "./UsersRow";
|
||||
import { AddEmailRowProps, RowViewProps } from "../../../../types";
|
||||
|
||||
const StyledRowContainer = styled(RowContainer)`
|
||||
margin: 0 0 20px;
|
||||
|
||||
.clear-icon {
|
||||
margin-inline-end: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledRow = styled(Row)`
|
||||
@ -71,6 +81,7 @@ const RowView = (props: RowViewProps) => {
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = props as AddEmailRowProps;
|
||||
|
||||
const [openedEmailKey, setOpenedEmailKey] = useState("");
|
||||
@ -85,6 +96,8 @@ const RowView = (props: RowViewProps) => {
|
||||
const handleToggle = (user: TEnhancedMigrationUser) =>
|
||||
toggleAccount(user, checkedAccountType);
|
||||
|
||||
const onClearFilter = () => setSearchValue("");
|
||||
|
||||
const isIndeterminate =
|
||||
checkedUsers.withoutEmail.length > 0 &&
|
||||
checkedUsers.withoutEmail.length !== usersWithFilledEmails.length;
|
||||
@ -95,26 +108,56 @@ const RowView = (props: RowViewProps) => {
|
||||
|
||||
return (
|
||||
<StyledRowContainer useReactWindow={false}>
|
||||
<StyledRow
|
||||
checked={isChecked}
|
||||
onSelect={toggleAll}
|
||||
indeterminate={isIndeterminate}
|
||||
isDisabled={usersWithFilledEmails.length === 0}
|
||||
>
|
||||
<Text className="row-header-title">{t("Common:Name")}</Text>
|
||||
</StyledRow>
|
||||
{accountsData.map((data) => (
|
||||
<UsersRow
|
||||
t={t}
|
||||
key={data.key}
|
||||
data={data}
|
||||
sectionWidth={sectionWidth}
|
||||
toggleAccount={() => handleToggle(data)}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
isEmailOpen={openedEmailKey === data.key}
|
||||
setOpenedEmailKey={setOpenedEmailKey}
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<StyledRow
|
||||
checked={isChecked}
|
||||
onSelect={toggleAll}
|
||||
indeterminate={isIndeterminate}
|
||||
isDisabled={usersWithFilledEmails.length === 0}
|
||||
>
|
||||
<Text className="row-header-title">{t("Common:Name")}</Text>
|
||||
</StyledRow>
|
||||
{accountsData.map((data) => (
|
||||
<UsersRow
|
||||
t={t}
|
||||
key={data.key}
|
||||
data={data}
|
||||
sectionWidth={sectionWidth}
|
||||
toggleAccount={() => handleToggle(data)}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
isEmailOpen={openedEmailKey === data.key}
|
||||
setOpenedEmailKey={setOpenedEmailKey}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size={12}
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type={LinkType.action}
|
||||
isHovered
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
)}
|
||||
</StyledRowContainer>
|
||||
);
|
||||
};
|
||||
@ -126,6 +169,7 @@ export default inject<TStore>(({ importAccountsStore }) => {
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
@ -134,5 +178,6 @@ export default inject<TStore>(({ importAccountsStore }) => {
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
};
|
||||
})(observer(RowView));
|
||||
|
@ -27,7 +27,13 @@
|
||||
import { useState, useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link, LinkType } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { TableBody } from "@docspace/shared/components/table";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
import UsersTableHeader from "./UsersTableHeader";
|
||||
import UsersTableRow from "./UsersTableRow";
|
||||
import { StyledTableContainer } from "../../../../StyledDataImport";
|
||||
@ -45,13 +51,13 @@ const TableView = (props: TableViewProps) => {
|
||||
t,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
|
||||
userId,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
users,
|
||||
setSearchValue,
|
||||
} = props as AddEmailTableProps;
|
||||
const [openedEmailKey, setOpenedEmailKey] = useState<string>("");
|
||||
const tableRef = useRef(null);
|
||||
@ -67,6 +73,10 @@ const TableView = (props: TableViewProps) => {
|
||||
checkedAccountType,
|
||||
);
|
||||
|
||||
const onClearFilter = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
|
||||
const columnInfoPanelStorageName = `${INFO_PANEL_COLUMNS_SIZE}=${userId}`;
|
||||
|
||||
@ -76,44 +86,74 @@ const TableView = (props: TableViewProps) => {
|
||||
|
||||
return (
|
||||
<StyledTableContainer forwardedRef={tableRef} useReactWindow>
|
||||
<UsersTableHeader
|
||||
t={t}
|
||||
sectionWidth={sectionWidth}
|
||||
tableRef={tableRef}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={
|
||||
usersWithFilledEmails.length > 0 &&
|
||||
checkedUsers.withoutEmail.length === usersWithFilledEmails.length
|
||||
}
|
||||
toggleAll={toggleAll}
|
||||
/>
|
||||
<TableBody
|
||||
itemHeight={49}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
filesLength={accountsData.length}
|
||||
hasMoreFiles={false}
|
||||
itemCount={accountsData.length}
|
||||
fetchMoreFiles={() => {}}
|
||||
>
|
||||
{accountsData.map((data) => (
|
||||
<UsersTableRow
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<UsersTableHeader
|
||||
t={t}
|
||||
key={data.key}
|
||||
id={data.key}
|
||||
email={data.email || ""}
|
||||
displayName={data.displayName}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={() => toggleAccount(data, checkedAccountType)}
|
||||
isEmailOpen={openedEmailKey === data.key}
|
||||
setOpenedEmailKey={setOpenedEmailKey}
|
||||
sectionWidth={sectionWidth}
|
||||
tableRef={tableRef}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={
|
||||
usersWithFilledEmails.length > 0 &&
|
||||
checkedUsers.withoutEmail.length === usersWithFilledEmails.length
|
||||
}
|
||||
toggleAll={toggleAll}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableBody
|
||||
itemHeight={49}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
filesLength={accountsData.length}
|
||||
hasMoreFiles={false}
|
||||
itemCount={accountsData.length}
|
||||
fetchMoreFiles={() => {}}
|
||||
>
|
||||
{accountsData.map((data) => (
|
||||
<UsersTableRow
|
||||
t={t}
|
||||
key={data.key}
|
||||
id={data.key}
|
||||
email={data.email || ""}
|
||||
displayName={data.displayName}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={() => toggleAccount(data, checkedAccountType)}
|
||||
isEmailOpen={openedEmailKey === data.key}
|
||||
setOpenedEmailKey={setOpenedEmailKey}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size={12}
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type={LinkType.action}
|
||||
isHovered
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledTableContainer>
|
||||
);
|
||||
};
|
||||
@ -126,6 +166,7 @@ export default inject<TStore>(({ userStore, importAccountsStore }) => {
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
@ -135,5 +176,6 @@ export default inject<TStore>(({ userStore, importAccountsStore }) => {
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
};
|
||||
})(observer(TableView));
|
||||
|
@ -51,7 +51,6 @@ const REFRESH_TIMEOUT = 100;
|
||||
const AddEmailsStep = (props: AddEmailsStepProps) => {
|
||||
const {
|
||||
t,
|
||||
|
||||
incrementStep,
|
||||
decrementStep,
|
||||
users,
|
||||
@ -67,10 +66,8 @@ const AddEmailsStep = (props: AddEmailsStepProps) => {
|
||||
setWorkspace,
|
||||
setMigratingWorkspace,
|
||||
setMigrationPhase,
|
||||
|
||||
cancelUploadDialogVisible,
|
||||
setCancelUploadDialogVisible,
|
||||
|
||||
quotaCharacteristics,
|
||||
} = props as InjectedAddEmailsStepProps;
|
||||
|
||||
@ -97,8 +94,8 @@ const AddEmailsStep = (props: AddEmailsStepProps) => {
|
||||
const filteredAccounts = dataPortion.filter(
|
||||
(data) =>
|
||||
data.firstName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.displayName.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.email.toLowerCase().startsWith(searchValue.toLowerCase()),
|
||||
data.displayName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.email?.toLowerCase().startsWith(searchValue.toLowerCase()),
|
||||
);
|
||||
|
||||
const handleStepIncrement = () => {
|
||||
@ -190,12 +187,15 @@ const AddEmailsStep = (props: AddEmailsStepProps) => {
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Text fontWeight={600} lineHeight="20px" className="mb-17">
|
||||
{t("Settings:WithoutEmailHint")}
|
||||
</Text>
|
||||
<>
|
||||
<Text fontWeight={600} lineHeight="20px" className="mb-17">
|
||||
{t("Settings:WithoutEmailHint")}
|
||||
</Text>
|
||||
{Buttons}
|
||||
</>
|
||||
)}
|
||||
|
||||
{Buttons}
|
||||
{filteredAccounts.length > 0 && Buttons}
|
||||
|
||||
{cancelUploadDialogVisible && (
|
||||
<CancelUploadDialog
|
||||
|
@ -42,7 +42,6 @@ const ImportProcessingStep = (props: ImportProcessingStepProps) => {
|
||||
const {
|
||||
t,
|
||||
migratorName,
|
||||
|
||||
incrementStep,
|
||||
setIsLoading,
|
||||
proceedFileMigration,
|
||||
@ -50,8 +49,7 @@ const ImportProcessingStep = (props: ImportProcessingStepProps) => {
|
||||
} = props as InjectedImportProcessingStepProps;
|
||||
|
||||
const [percent, setPercent] = useState(0);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
const [isVisibleProgress, setIsVisibleProgress] = useState(false);
|
||||
const [failTries, setFailTries] = useState(FAIL_TRIES);
|
||||
|
||||
const uploadInterval = useRef<number>();
|
||||
@ -59,7 +57,7 @@ const ImportProcessingStep = (props: ImportProcessingStepProps) => {
|
||||
const handleFileMigration = async () => {
|
||||
setIsLoading(true);
|
||||
setPercent(0);
|
||||
setIsVisible(true);
|
||||
setIsVisibleProgress(true);
|
||||
try {
|
||||
await proceedFileMigration(migratorName);
|
||||
|
||||
@ -78,17 +76,12 @@ const ImportProcessingStep = (props: ImportProcessingStepProps) => {
|
||||
}
|
||||
|
||||
setPercent(res.progress);
|
||||
|
||||
if (res.progress > 10) {
|
||||
setIsVisible(false);
|
||||
} else {
|
||||
setIsVisible(true);
|
||||
}
|
||||
setIsVisibleProgress(res.progress <= 10);
|
||||
|
||||
if (res.isCompleted || res.progress === 100) {
|
||||
clearInterval(uploadInterval.current);
|
||||
setIsLoading(false);
|
||||
setIsVisible(false);
|
||||
setIsVisibleProgress(false);
|
||||
setPercent(100);
|
||||
setTimeout(() => {
|
||||
incrementStep();
|
||||
@ -111,7 +104,7 @@ const ImportProcessingStep = (props: ImportProcessingStepProps) => {
|
||||
<Wrapper>
|
||||
<ProgressBar
|
||||
percent={percent}
|
||||
isInfiniteProgress={isVisible}
|
||||
isInfiniteProgress={isVisibleProgress}
|
||||
className="data-import-progress-bar"
|
||||
/>
|
||||
</Wrapper>
|
||||
|
@ -180,6 +180,7 @@ const ImportStep = (props: ImportStepProps) => {
|
||||
workspace: t("Common:ProductName"),
|
||||
sectionIcon: RoomsIcon,
|
||||
}}
|
||||
isDisabled={false}
|
||||
/>
|
||||
<ImportSection
|
||||
isChecked={importOptions.importCommonFiles}
|
||||
|
@ -86,20 +86,22 @@ const Wrapper = styled.div`
|
||||
const FileUploadContainer = styled.div`
|
||||
max-width: 350px;
|
||||
|
||||
.cancel-btn {
|
||||
@media ${mobile} {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.cancelUploadButton {
|
||||
@media ${mobile} {
|
||||
margin-bottom: 0;
|
||||
|
||||
width: auto;
|
||||
|
||||
position: fixed;
|
||||
inset-inline: 0px;
|
||||
bottom: 0px;
|
||||
|
||||
padding: 16px;
|
||||
|
||||
background: white;
|
||||
|
||||
background: ${(props) =>
|
||||
props.theme.client.settings.migration.workspaceBackground};
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
@ -133,19 +135,15 @@ const FAIL_TRIES = 2;
|
||||
|
||||
const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
const {
|
||||
t,
|
||||
isMultipleUpload,
|
||||
migratorName,
|
||||
acceptedExtensions,
|
||||
|
||||
t,
|
||||
incrementStep,
|
||||
setWorkspace,
|
||||
|
||||
cancelUploadDialogVisible,
|
||||
setCancelUploadDialogVisible,
|
||||
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
initMigrations,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
cancelMigration,
|
||||
@ -153,9 +151,9 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
setLoadingStatus,
|
||||
files,
|
||||
setFiles,
|
||||
multipleFileUploading,
|
||||
migratingWorkspace,
|
||||
setMigratingWorkspace,
|
||||
uploadFiles,
|
||||
} = props as InjectedSelectFileStepProps;
|
||||
|
||||
const [isSaveDisabled, setIsSaveDisabled] = useState(
|
||||
@ -174,9 +172,18 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
const [chunkSize, setChunkSize] = useState(0);
|
||||
|
||||
const [failTries, setFailTries] = useState(FAIL_TRIES);
|
||||
|
||||
const uploadInterval = useRef<number>();
|
||||
|
||||
const handleError = useCallback(
|
||||
(message?: string) => {
|
||||
toastr.error(message || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setLoadingStatus("none");
|
||||
clearInterval(uploadInterval.current);
|
||||
},
|
||||
[t, setLoadingStatus],
|
||||
);
|
||||
|
||||
const poolStatus = useCallback(async () => {
|
||||
try {
|
||||
const res = await getMigrationStatus();
|
||||
@ -187,29 +194,24 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
toastr.error(t("Common:SomethingWentWrong"));
|
||||
setLoadingStatus("none");
|
||||
clearInterval(uploadInterval.current);
|
||||
handleError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.parseResult.failedArchives.length > 0 || res.error) {
|
||||
toastr.error(res.error);
|
||||
setIsFileError(true);
|
||||
setLoadingStatus("none");
|
||||
clearInterval(uploadInterval.current);
|
||||
handleError(res.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.isCompleted || res.progress === 100) {
|
||||
clearInterval(uploadInterval.current);
|
||||
if (
|
||||
|
||||
const totalUsers =
|
||||
res.parseResult.users.length +
|
||||
res.parseResult.existUsers.length +
|
||||
res.parseResult.withoutEmailUsers.length >
|
||||
0
|
||||
) {
|
||||
setUsers(res.parseResult);
|
||||
res.parseResult.existUsers.length +
|
||||
res.parseResult.withoutEmailUsers.length;
|
||||
|
||||
if (totalUsers > 0) {
|
||||
setIsBackupEmpty(false);
|
||||
setLoadingStatus("done");
|
||||
setUsers(res.parseResult);
|
||||
@ -228,15 +230,8 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
setIsInfiniteProgress(false);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
if (error.message === "Network Error") {
|
||||
setIsNetworkError(true);
|
||||
}
|
||||
toastr.error(error || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setLoadingStatus("none");
|
||||
clearInterval(uploadInterval.current);
|
||||
}
|
||||
handleError(error as string);
|
||||
setIsNetworkError(true);
|
||||
}
|
||||
}, [
|
||||
failTries,
|
||||
@ -244,49 +239,31 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
isInfiniteProgress,
|
||||
setLoadingStatus,
|
||||
setUsers,
|
||||
t,
|
||||
handleError,
|
||||
]);
|
||||
|
||||
const onUploadFile = async (file: File | File[]) => {
|
||||
try {
|
||||
if (file instanceof Array) {
|
||||
setFiles(file.map((item) => item.name));
|
||||
await multipleFileUploading(
|
||||
file,
|
||||
setProgress,
|
||||
isAbort,
|
||||
setChunk,
|
||||
startChunk,
|
||||
setChunkSize,
|
||||
chunkSize,
|
||||
);
|
||||
} else {
|
||||
setFiles([file.name]);
|
||||
await singleFileUploading(
|
||||
file,
|
||||
setProgress,
|
||||
isAbort,
|
||||
setChunk,
|
||||
startChunk,
|
||||
setChunkSize,
|
||||
chunkSize,
|
||||
);
|
||||
}
|
||||
const filesData = Array.isArray(file) ? file : [file];
|
||||
setFiles(filesData.map((item) => item.name));
|
||||
|
||||
await uploadFiles(
|
||||
filesData,
|
||||
setProgress,
|
||||
isAbort,
|
||||
setChunk,
|
||||
startChunk,
|
||||
setChunkSize,
|
||||
chunkSize,
|
||||
);
|
||||
|
||||
if (isAbort.current) return;
|
||||
|
||||
await initMigrationName(migratorName);
|
||||
await initMigrations(migratorName);
|
||||
setLoadingStatus("proceed");
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
if (error.message === "Network Error") {
|
||||
setIsNetworkError(true);
|
||||
}
|
||||
|
||||
toastr.error(error || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setLoadingStatus("none");
|
||||
}
|
||||
handleError(error as string);
|
||||
setIsNetworkError(true);
|
||||
} finally {
|
||||
isAbort.current = false;
|
||||
}
|
||||
@ -300,6 +277,7 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
|
||||
setProgress(0);
|
||||
setIsFileError(false);
|
||||
setIsBackupEmpty(false);
|
||||
setIsSaveDisabled(true);
|
||||
setLoadingStatus("upload");
|
||||
setFailTries(FAIL_TRIES);
|
||||
@ -405,6 +383,7 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
}
|
||||
accept={acceptedExtensions}
|
||||
size={InputSize.base}
|
||||
isMultiple={migratorName === "GoogleWorkspace"}
|
||||
/>
|
||||
</FileUploadContainer>
|
||||
{fileLoadingStatus === "upload" || fileLoadingStatus === "proceed" ? (
|
||||
@ -418,6 +397,7 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
<div className="cancelUploadButton">
|
||||
<Button
|
||||
size={isTablet() ? ButtonSize.medium : ButtonSize.small}
|
||||
className="cancel-btn"
|
||||
label={t("Common:CancelButton")}
|
||||
onClick={onCancel}
|
||||
scale={isMobile()}
|
||||
@ -503,8 +483,7 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
|
||||
export default inject<TStore>(({ dialogsStore, importAccountsStore }) => {
|
||||
const {
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
initMigrations,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
fileLoadingStatus,
|
||||
@ -514,16 +493,15 @@ export default inject<TStore>(({ dialogsStore, importAccountsStore }) => {
|
||||
incrementStep,
|
||||
files,
|
||||
setFiles,
|
||||
multipleFileUploading,
|
||||
migratingWorkspace,
|
||||
setMigratingWorkspace,
|
||||
uploadFiles,
|
||||
} = importAccountsStore;
|
||||
const { cancelUploadDialogVisible, setCancelUploadDialogVisible } =
|
||||
dialogsStore;
|
||||
|
||||
return {
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
initMigrations,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
fileLoadingStatus,
|
||||
@ -535,8 +513,8 @@ export default inject<TStore>(({ dialogsStore, importAccountsStore }) => {
|
||||
incrementStep,
|
||||
files,
|
||||
setFiles,
|
||||
multipleFileUploading,
|
||||
migratingWorkspace,
|
||||
setMigratingWorkspace,
|
||||
uploadFiles,
|
||||
};
|
||||
})(observer(SelectFileStep));
|
||||
|
@ -24,7 +24,7 @@
|
||||
// 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 styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { RowContent } from "@docspace/shared/components/row-content";
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { tablet } from "@docspace/shared/utils/device";
|
||||
import styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
@ -91,7 +91,6 @@ const RowView = (props: RowViewProps) => {
|
||||
t,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
|
||||
checkedUsers,
|
||||
withEmailUsers,
|
||||
toggleAccount,
|
||||
|
@ -196,9 +196,12 @@ const SelectUsersStep = (props: SelectUsersStepProps) => {
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Text fontWeight={600} lineHeight="20px" className="mb-17">
|
||||
{t("Settings:AddEmailsWarning")}
|
||||
</Text>
|
||||
<>
|
||||
<Text fontWeight={600} lineHeight="20px" className="mb-17">
|
||||
{t("Settings:AddEmailsWarning")}
|
||||
</Text>
|
||||
{Buttons}
|
||||
</>
|
||||
)}
|
||||
|
||||
{filteredAccounts.length > 0 && Buttons}
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { tablet } from "@docspace/shared/utils/device";
|
||||
import styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
@ -55,7 +55,7 @@ const StyledRowContainer = styled(RowContainer)`
|
||||
margin-inline-start: -16px;
|
||||
width: 100%;
|
||||
|
||||
margin-top: -31.5px;
|
||||
margin-top: -32.5px;
|
||||
top: 53px;
|
||||
margin-bottom: -29.5px;
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
import { useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { Base } from "@docspace/shared/themes";
|
||||
import styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
@ -97,7 +97,6 @@ const TableView = (props: TypeSelectTableViewProps) => {
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
typeOptions,
|
||||
|
||||
userId,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
|
@ -27,9 +27,9 @@
|
||||
import { useEffect, useCallback } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import useViewEffect from "SRC_DIR/Hooks/useViewEffect";
|
||||
import { setDocumentTitle } from "SRC_DIR/helpers/utils";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { setDocumentTitle } from "SRC_DIR/helpers/utils";
|
||||
import useViewEffect from "SRC_DIR/Hooks/useViewEffect";
|
||||
|
||||
import { DataImportProps, InjectedDataImportProps } from "./types";
|
||||
|
||||
@ -43,7 +43,6 @@ const DataImport = (props: DataImportProps) => {
|
||||
viewAs,
|
||||
setViewAs,
|
||||
currentDeviceType,
|
||||
|
||||
getMigrationStatus,
|
||||
isMigrationInit,
|
||||
setUsers,
|
||||
@ -74,15 +73,14 @@ const DataImport = (props: DataImportProps) => {
|
||||
|
||||
const { parseResult, error, isCompleted } = response;
|
||||
|
||||
if (
|
||||
error ||
|
||||
parseResult.failedArchives.length > 0 ||
|
||||
const isErrorOrFailedParse = error || parseResult.failedArchives.length > 0;
|
||||
const isNoUsersParsed =
|
||||
parseResult.users.length +
|
||||
parseResult.existUsers.length +
|
||||
parseResult.withoutEmailUsers.length ===
|
||||
0
|
||||
)
|
||||
return;
|
||||
0;
|
||||
|
||||
if (isErrorOrFailedParse || isNoUsersParsed) return;
|
||||
|
||||
if (parseResult.operation === "parse") {
|
||||
setWorkspace(parseResult.migratorName);
|
||||
@ -140,7 +138,7 @@ const DataImport = (props: DataImportProps) => {
|
||||
};
|
||||
|
||||
export default inject<TStore>(
|
||||
({ authStore, settingsStore, setup, importAccountsStore }) => {
|
||||
({ settingsStore, setup, importAccountsStore }) => {
|
||||
const {
|
||||
getMigrationStatus,
|
||||
isMigrationInit,
|
||||
@ -160,7 +158,6 @@ export default inject<TStore>(
|
||||
viewAs,
|
||||
setViewAs,
|
||||
currentDeviceType,
|
||||
|
||||
getMigrationStatus,
|
||||
isMigrationInit,
|
||||
setUsers,
|
||||
|
@ -59,8 +59,7 @@ export interface InjectedSelectFileStepProps extends SelectFileStepProps {
|
||||
setWorkspace: TStore["importAccountsStore"]["setWorkspace"];
|
||||
cancelUploadDialogVisible: TStore["dialogsStore"]["cancelUploadDialogVisible"];
|
||||
setCancelUploadDialogVisible: TStore["dialogsStore"]["setCancelUploadDialogVisible"];
|
||||
initMigrationName: TStore["importAccountsStore"]["initMigrationName"];
|
||||
singleFileUploading: TStore["importAccountsStore"]["singleFileUploading"];
|
||||
initMigrations: TStore["importAccountsStore"]["initMigrations"];
|
||||
getMigrationStatus: TStore["importAccountsStore"]["getMigrationStatus"];
|
||||
setUsers: TStore["importAccountsStore"]["setUsers"];
|
||||
fileLoadingStatus: TStore["importAccountsStore"]["fileLoadingStatus"];
|
||||
@ -68,15 +67,14 @@ export interface InjectedSelectFileStepProps extends SelectFileStepProps {
|
||||
cancelMigration: TStore["importAccountsStore"]["cancelMigration"];
|
||||
files: TStore["importAccountsStore"]["files"];
|
||||
setFiles: TStore["importAccountsStore"]["setFiles"];
|
||||
multipleFileUploading: TStore["importAccountsStore"]["multipleFileUploading"];
|
||||
migratingWorkspace: TStore["importAccountsStore"]["migratingWorkspace"];
|
||||
setMigratingWorkspace: TStore["importAccountsStore"]["setMigratingWorkspace"];
|
||||
uploadFiles: TStore["importAccountsStore"]["uploadFiles"];
|
||||
}
|
||||
|
||||
export interface DataImportProps {}
|
||||
|
||||
export interface InjectedDataImportProps extends DataImportProps {
|
||||
setDocumentTitle: TStore["authStore"]["setDocumentTitle"];
|
||||
getMigrationStatus: TStore["importAccountsStore"]["getMigrationStatus"];
|
||||
viewAs: TStore["setup"]["viewAs"];
|
||||
setViewAs: TStore["setup"]["setViewAs"];
|
||||
@ -102,8 +100,6 @@ export interface InjectedWorkspaceProps extends WorkspaceProps {
|
||||
migrationPhase: TStore["importAccountsStore"]["migrationPhase"];
|
||||
isMigrationInit: TStore["importAccountsStore"]["isMigrationInit"];
|
||||
setIsMigrationInit: TStore["importAccountsStore"]["setIsMigrationInit"];
|
||||
|
||||
setDocumentTitle: TStore["authStore"]["setDocumentTitle"];
|
||||
}
|
||||
|
||||
export interface LayoutProps {
|
||||
@ -172,6 +168,7 @@ export interface InjectedTableViewProps extends TableViewProps {
|
||||
|
||||
export interface AddEmailTableProps extends InjectedTableViewProps {
|
||||
users: TStore["importAccountsStore"]["users"];
|
||||
setSearchValue: TStore["importAccountsStore"]["setSearchValue"];
|
||||
}
|
||||
|
||||
export interface SelectUserTableProps extends InjectedTableViewProps {
|
||||
@ -223,6 +220,7 @@ export interface AddEmailRowProps extends RowViewProps {
|
||||
toggleAccount: TStore["importAccountsStore"]["toggleAccount"];
|
||||
toggleAllAccounts: TStore["importAccountsStore"]["toggleAllAccounts"];
|
||||
isAccountChecked: TStore["importAccountsStore"]["isAccountChecked"];
|
||||
setSearchValue: TStore["importAccountsStore"]["setSearchValue"];
|
||||
}
|
||||
|
||||
export interface UsersRowProps {
|
||||
|
@ -27,6 +27,7 @@
|
||||
import axios from "axios";
|
||||
import { uploadFile } from "@docspace/shared/api/files";
|
||||
import { combineUrl } from "@docspace/shared/utils/combineUrl";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { makeAutoObservable, runInAction } from "mobx";
|
||||
import {
|
||||
migrationList,
|
||||
@ -328,8 +329,8 @@ class ImportAccountsStore {
|
||||
};
|
||||
};
|
||||
|
||||
multipleFileUploading = async (
|
||||
files: File[],
|
||||
uploadFiles = async (
|
||||
filesData: File | File[],
|
||||
setProgress: (progress: number) => void,
|
||||
isAbort: React.MutableRefObject<boolean>,
|
||||
setChunk: React.Dispatch<React.SetStateAction<number>>,
|
||||
@ -338,29 +339,34 @@ class ImportAccountsStore {
|
||||
chunkSize: number,
|
||||
) => {
|
||||
let chunk = 0;
|
||||
const location = combineUrl(
|
||||
window.location.origin,
|
||||
"migrationFileUpload.ashx",
|
||||
);
|
||||
|
||||
try {
|
||||
const location = combineUrl(
|
||||
window.location.origin,
|
||||
"migrationFileUpload.ashx",
|
||||
);
|
||||
const requestsDataArray: { formData: FormData; fileName: string }[] = [];
|
||||
let chunkUploadSize = chunkSize;
|
||||
|
||||
let chunkUploadSize = 0;
|
||||
if (!chunkSize) {
|
||||
const res = await axios.post<{
|
||||
Success: boolean;
|
||||
ChunkSize: number;
|
||||
Message: string;
|
||||
}>(`${location}?Init=${startChunk === 0}`);
|
||||
|
||||
if (chunkSize) {
|
||||
chunkUploadSize = chunkSize;
|
||||
} else {
|
||||
const res: { data: { ChunkSize: number } } = await axios.post(
|
||||
`${location}?Init=${startChunk === 0}`,
|
||||
);
|
||||
if (!res.data.Success) {
|
||||
toastr.error(res.data.Message);
|
||||
throw new Error(res.data.Message);
|
||||
}
|
||||
|
||||
chunkUploadSize = res.data.ChunkSize;
|
||||
setChunkSize(chunkUploadSize);
|
||||
}
|
||||
|
||||
if (!chunkUploadSize) return;
|
||||
if (!chunkUploadSize || isAbort.current) return;
|
||||
|
||||
if (isAbort.current) return;
|
||||
const requestsDataArray: { formData: FormData; fileName: string }[] = [];
|
||||
const files = Array.isArray(filesData) ? filesData : [filesData];
|
||||
|
||||
const chunksNumber = files
|
||||
.map((file) => Math.ceil(file.size / chunkUploadSize))
|
||||
@ -396,74 +402,9 @@ class ImportAccountsStore {
|
||||
setProgress(Math.ceil(progress));
|
||||
chunk += 1;
|
||||
}
|
||||
} catch (e) {
|
||||
setChunk(chunk);
|
||||
}
|
||||
};
|
||||
|
||||
singleFileUploading = async (
|
||||
file: File,
|
||||
setProgress: (progress: number) => void,
|
||||
isAbort: React.MutableRefObject<boolean>,
|
||||
setChunk: React.Dispatch<React.SetStateAction<number>>,
|
||||
startChunk: number,
|
||||
setChunkSize: React.Dispatch<React.SetStateAction<number>>,
|
||||
chunkSize: number,
|
||||
) => {
|
||||
let chunk = 0;
|
||||
try {
|
||||
const location = combineUrl(
|
||||
window.location.origin,
|
||||
"migrationFileUpload.ashx",
|
||||
);
|
||||
|
||||
let chunkUploadSize = 0;
|
||||
|
||||
if (chunkSize) {
|
||||
chunkUploadSize = chunkSize;
|
||||
} else {
|
||||
const res: { data: { ChunkSize: number } } = await axios.post(
|
||||
`${location}?Init=${startChunk === 0}`,
|
||||
);
|
||||
|
||||
chunkUploadSize = res.data.ChunkSize;
|
||||
setChunkSize(chunkUploadSize);
|
||||
}
|
||||
|
||||
if (!chunkUploadSize) return;
|
||||
|
||||
const requestsDataArray = [];
|
||||
|
||||
const chunks = Math.ceil(file.size / chunkUploadSize);
|
||||
|
||||
if (isAbort.current) return;
|
||||
|
||||
while (chunk < chunks) {
|
||||
const offset = chunk * chunkUploadSize;
|
||||
const formData = new FormData();
|
||||
formData.append("file", file.slice(offset, offset + chunkUploadSize));
|
||||
requestsDataArray.push(formData);
|
||||
chunk += 1;
|
||||
}
|
||||
|
||||
chunk = startChunk || 0;
|
||||
while (
|
||||
chunk < chunks &&
|
||||
(this.fileLoadingStatus === "upload" ||
|
||||
this.fileLoadingStatus === "proceed")
|
||||
) {
|
||||
if (isAbort.current) return;
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await uploadFile(
|
||||
`${location}?Name=${file.name}`,
|
||||
requestsDataArray[chunk],
|
||||
);
|
||||
const progress = (chunk / chunks) * 100;
|
||||
setProgress(Math.ceil(progress));
|
||||
chunk += 1;
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (error) {
|
||||
setChunk(chunk);
|
||||
throw new Error(error as string);
|
||||
}
|
||||
};
|
||||
|
||||
@ -481,7 +422,7 @@ class ImportAccountsStore {
|
||||
};
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
initMigrationName = (name: TWorkspaceService) => {
|
||||
initMigrations = (name: TWorkspaceService) => {
|
||||
return initMigration(name);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user