Web: Client: Data Import: nextcloud select file step was translated into ts

This commit is contained in:
Vladimir Khvan 2024-06-04 14:49:16 +05:00
parent 515fc32865
commit 69b579521f
5 changed files with 438 additions and 13 deletions

View File

@ -52,7 +52,7 @@ export const getStepsData = (
{
title: t("Common:SelectFile"),
description: t("Settings:SelectFileDescriptionNextcloud"),
component: <SelectFileStep t={t} incrementStep={incrementStep} />,
component: <SelectFileStep t={t} />,
},
{
title: t("Settings:SelectUsersWithEmail"),

View File

@ -0,0 +1,404 @@
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// 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 { useState, useRef, useEffect, useCallback } from "react";
import { inject, observer } from "mobx-react";
import { CancelUploadDialog } from "SRC_DIR/components/dialogs";
import { isTablet } from "@docspace/shared/utils/device";
import styled from "styled-components";
import { Text } from "@docspace/shared/components/text";
import { Button, ButtonSize } from "@docspace/shared/components/button";
import { FileInput } from "@docspace/shared/components/file-input";
import { ProgressBar } from "@docspace/shared/components/progress-bar";
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
import { Box } from "@docspace/shared/components/box";
import { Link, LinkType } from "@docspace/shared/components/link";
import { toastr } from "@docspace/shared/components/toast";
import { InputSize } from "@docspace/shared/components/text-input";
import { InjectedSelectFileStepProps, SelectFileStepProps } from "../types";
const Wrapper = styled.div`
max-width: 700px;
margin-top: 16px;
.choose-backup-file {
font-weight: 600;
line-height: 20px;
margin-bottom: 4px;
}
.upload-backup-input {
height: 32px;
margin-bottom: 12px;
.icon-button_svg {
svg {
path {
fill: ${(props) =>
props.theme.client.settings.migration.fileInputIconColor};
}
}
}
}
.upload-back-buttons {
margin-top: 16px;
}
.select-file-progress-text {
margin: 12px 0;
}
.select-file-progress-bar {
margin: 12px 0 16px;
}
`;
const FileUploadContainer = styled.div`
max-width: 350px;
`;
const ErrorBlock = styled.div`
max-width: 700px;
.complete-progress-bar {
margin: 12px 0 16px;
max-width: 350px;
}
.error-text {
font-size: 12px;
margin-bottom: 10px;
color: ${(props) => props.theme.client.settings.migration.errorTextColor};
}
.save-cancel-buttons {
margin-top: 16px;
}
`;
const FAIL_TRIES = 2;
const SelectFileStep = (props: SelectFileStepProps) => {
const {
t,
incrementStep,
setWorkspace,
cancelUploadDialogVisible,
setCancelUploadDialogVisible,
initMigrationName,
singleFileUploading,
getMigrationStatus,
setUsers,
cancelMigration,
fileLoadingStatus,
setLoadingStatus,
files,
setFiles,
} = props as InjectedSelectFileStepProps;
const [isSaveDisabled, setIsSaveDisabled] = useState(true);
const [progress, setProgress] = useState(0);
const [isInfiniteProgress, setIsInfiniteProgress] = useState(true);
const [isError, setIsError] = useState(false);
const [isFileError, setIsFileError] = useState(false);
const isAbort = useRef(false);
const [failTries, setFailTries] = useState(FAIL_TRIES);
const uploadInterval = useRef<number>();
const onUploadToServer = () => {
console.log("handle internet abortion");
};
const poolStatus = useCallback(async () => {
try {
const res = await getMigrationStatus();
if (!res && failTries) {
setFailTries((prevTries) => prevTries - 1);
return;
}
if (!res) {
toastr.error(t("Common:SomethingWentWrong"));
setLoadingStatus("none");
clearInterval(uploadInterval.current);
return;
}
if (res.parseResult.failedArchives.length > 0 || res.error) {
toastr.error(res.error);
setIsFileError(true);
setLoadingStatus("none");
clearInterval(uploadInterval.current);
return;
}
if (res.isCompleted || res.progress === 100) {
clearInterval(uploadInterval.current);
setUsers(res.parseResult);
setLoadingStatus("done");
setIsInfiniteProgress(false);
setIsSaveDisabled(false);
}
setProgress(res.progress);
if (isInfiniteProgress && res.progress > 10) {
setIsInfiniteProgress(false);
}
} catch (error) {
toastr.error(error || t("Common:SomethingWentWrong"));
setIsFileError(true);
setLoadingStatus("none");
setIsError(true);
clearInterval(uploadInterval.current);
}
}, [
failTries,
getMigrationStatus,
isInfiniteProgress,
setLoadingStatus,
setUsers,
t,
]);
const onUploadFile = async (file: File) => {
try {
await singleFileUploading(file, setProgress, isAbort);
if (isAbort.current) return;
await initMigrationName("Nextcloud");
setLoadingStatus("proceed");
} catch (error) {
toastr.error(error || t("Common:SomethingWentWrong"));
setIsFileError(true);
setLoadingStatus("none");
} finally {
isAbort.current = false;
}
};
const onSelectFile = (file: File | File[]) => {
if (file instanceof Array) {
toastr.error(t("Common:SomethingWentWrong"));
return;
}
setProgress(0);
setIsFileError(false);
setIsSaveDisabled(true);
setLoadingStatus("upload");
setFailTries(FAIL_TRIES);
setIsInfiniteProgress(true);
setFiles([file.name]);
onUploadFile(file);
};
const onDownloadArchives = async () => {
try {
const res = await getMigrationStatus();
if (!res) {
throw new Error();
}
const blob = new Blob(res.parseResult.failedArchives, {
type: "text/csv;charset=utf-8",
});
const a = document.createElement("a");
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = "unsupported_files";
a.click();
window.URL.revokeObjectURL(url);
} catch (error) {
toastr.error(error || t("Common:SomethingWentWrong"));
}
};
const onCancel = () => {
setCancelUploadDialogVisible(true);
};
const handleCancelMigration = () => {
isAbort.current = true;
setProgress(0);
setLoadingStatus("none");
clearInterval(uploadInterval.current);
cancelMigration();
};
const hideCancelDialog = () => setCancelUploadDialogVisible(false);
const returnToProviders = () => setWorkspace("");
useEffect(() => {
if (fileLoadingStatus === "proceed") {
uploadInterval.current = window.setInterval(poolStatus, 1000);
} else if (fileLoadingStatus === "done") {
setIsSaveDisabled(false);
}
return () => clearInterval(uploadInterval.current);
}, [fileLoadingStatus, poolStatus]);
return (
<Wrapper>
<FileUploadContainer>
<Text className="choose-backup-file">
{t("Settings:ChooseBackupFile")}
</Text>
<FileInput
scale
onInput={onSelectFile}
className="upload-backup-input"
placeholder={files.join(",") || t("Settings:BackupFile")}
isDisabled={
fileLoadingStatus === "upload" || fileLoadingStatus === "proceed"
}
accept={[".zip"]}
size={InputSize.base}
/>
</FileUploadContainer>
{fileLoadingStatus === "upload" || fileLoadingStatus === "proceed" ? (
<FileUploadContainer>
<ProgressBar
percent={progress}
isInfiniteProgress={isInfiniteProgress}
className="select-file-progress-bar"
label={t("Settings:BackupFileUploading")}
/>
<Button
size={isTablet() ? ButtonSize.medium : ButtonSize.small}
label={t("Common:CancelButton")}
onClick={onCancel}
/>
</FileUploadContainer>
) : (
<ErrorBlock>
{isFileError && (
<Box>
<ProgressBar
percent={100}
className="complete-progress-bar"
label={t("Common:LoadingIsComplete")}
/>
<Text className="error-text">
{t("Settings:UnsupportedFilesDescription")}
</Text>
<Link
type={LinkType.action}
isHovered
fontWeight={600}
onClick={onDownloadArchives}
>
{t("Settings:CheckUnsupportedFiles")}
</Link>
</Box>
)}
{isError ? (
<SaveCancelButtons
className="save-cancel-buttons"
onSaveClick={onUploadToServer}
onCancelClick={returnToProviders}
saveButtonLabel={t("Settings:UploadToServer")}
cancelButtonLabel={t("Common:Back")}
displaySettings
saveButtonDisabled={isSaveDisabled}
showReminder
/>
) : (
<SaveCancelButtons
className="save-cancel-buttons"
onSaveClick={incrementStep}
onCancelClick={returnToProviders}
saveButtonLabel={t("Settings:NextStep")}
cancelButtonLabel={t("Common:Back")}
displaySettings
saveButtonDisabled={isSaveDisabled}
showReminder
/>
)}
</ErrorBlock>
)}
{cancelUploadDialogVisible && (
<CancelUploadDialog
visible={cancelUploadDialogVisible}
onClose={hideCancelDialog}
cancelMigration={handleCancelMigration}
loading={false}
isFifthStep={false}
isSixthStep={false}
/>
)}
</Wrapper>
);
};
export default inject<TStore>(({ dialogsStore, importAccountsStore }) => {
const {
initMigrationName,
singleFileUploading,
getMigrationStatus,
setUsers,
fileLoadingStatus,
setLoadingStatus,
cancelMigration,
setWorkspace,
incrementStep,
files,
setFiles,
} = importAccountsStore;
const { cancelUploadDialogVisible, setCancelUploadDialogVisible } =
dialogsStore;
return {
initMigrationName,
singleFileUploading,
getMigrationStatus,
setUsers,
fileLoadingStatus,
setLoadingStatus,
cancelMigration,
cancelUploadDialogVisible,
setCancelUploadDialogVisible,
setWorkspace,
incrementStep,
files,
setFiles,
};
})(observer(SelectFileStep));

View File

@ -50,6 +50,7 @@ const DataImport = (props: DataImportProps) => {
setWorkspace,
setFiles,
setIsMigrationInit,
setLoadingStatus,
} = props as InjectedDataImportProps;
const { t } = useTranslation(["Settings"]);
@ -76,13 +77,15 @@ const DataImport = (props: DataImportProps) => {
setWorkspace(parseResult.migratorName);
setFiles(parseResult.files);
setIsMigrationInit(true);
setLoadingStatus("done");
}
}, [
getMigrationStatus,
setIsMigrationInit,
setFiles,
setUsers,
setWorkspace,
setFiles,
setIsMigrationInit,
setLoadingStatus,
]);
useEffect(() => {
@ -116,6 +119,7 @@ export default inject<TStore>(
setWorkspace,
setFiles,
setIsMigrationInit,
setLoadingStatus,
} = importAccountsStore;
const { setDocumentTitle } = authStore;
@ -135,6 +139,7 @@ export default inject<TStore>(
setWorkspace,
setFiles,
setIsMigrationInit,
setLoadingStatus,
};
},
)(observer(DataImport));

View File

@ -44,16 +44,23 @@ export interface InjectedProvidersProps extends ProvidersProps {
export interface SelectFileStepProps {
t: TFunciton;
incrementStep: () => void;
}
export interface InjectedSelectFileStepProps extends SelectFileStepProps {
t: TFunciton;
incrementStep: TStore["importAccountsStore"]["incrementStep"];
setWorkspace: TStore["importAccountsStore"]["setWorkspace"];
cancelUploadDialogVisible: TStore["dialogsStore"]["cancelUploadDialogVisible"];
setCancelUploadDialogVisible: TStore["dialogsStore"]["setCancelUploadDialogVisible"];
initMigrationName: TStore["importAccountsStore"]["initMigrationName"];
singleFileUploading: TStore["importAccountsStore"]["singleFileUploading"];
getMigrationStatus: TStore["importAccountsStore"]["getMigrationStatus"];
setUsers: TStore["importAccountsStore"]["setUsers"];
isFileLoading: TStore["importAccountsStore"]["isFileLoading"];
setIsFileLoading: TStore["importAccountsStore"]["setIsFileLoading"];
fileLoadingStatus: TStore["importAccountsStore"]["fileLoadingStatus"];
setLoadingStatus: TStore["importAccountsStore"]["setLoadingStatus"];
cancelMigration: TStore["importAccountsStore"]["cancelMigration"];
files: TStore["importAccountsStore"]["files"];
setFiles: TStore["importAccountsStore"]["setFiles"];
}
export interface DataImportProps {}
@ -70,6 +77,7 @@ export interface InjectedDataImportProps extends DataImportProps {
setWorkspace: TStore["importAccountsStore"]["setWorkspace"];
setFiles: TStore["importAccountsStore"]["setFiles"];
setIsMigrationInit: TStore["importAccountsStore"]["setIsMigrationInit"];
setLoadingStatus: TStore["importAccountsStore"]["setLoadingStatus"];
}
export interface NextcloudProps {}

View File

@ -60,6 +60,8 @@ type TCheckedUsers = {
type CheckedAccountTypes = "withEmail" | "withoutEmail" | "result";
type LoadingState = "none" | "upload" | "proceed" | "done";
class ImportAccountsStore {
services: TWorkspaceService[] = [];
@ -82,7 +84,7 @@ class ImportAccountsStore {
User: "Collaborator",
};
isFileLoading = false;
fileLoadingStatus: LoadingState = "none";
isLoading = false;
@ -194,8 +196,8 @@ class ImportAccountsStore {
});
};
setIsFileLoading = (isLoading: boolean) => {
this.isFileLoading = isLoading;
setLoadingStatus = (status: LoadingState) => {
this.fileLoadingStatus = status;
};
setIsLoading = (isLoading: boolean) => {
@ -339,7 +341,11 @@ class ImportAccountsStore {
let chunk = 0;
while (chunk < chunksNumber && this.isFileLoading) {
while (
chunk < chunksNumber &&
(this.fileLoadingStatus === "upload" ||
this.fileLoadingStatus === "proceed")
) {
if (isAbort.current) return;
// eslint-disable-next-line no-await-in-loop
await uploadFile(
@ -387,7 +393,11 @@ class ImportAccountsStore {
}
chunk = 0;
while (chunk < chunks && this.isFileLoading) {
while (
chunk < chunks &&
(this.fileLoadingStatus === "upload" ||
this.fileLoadingStatus === "proceed")
) {
if (isAbort.current) return;
// eslint-disable-next-line no-await-in-loop
await uploadFile(
@ -400,8 +410,6 @@ class ImportAccountsStore {
}
} catch (e) {
console.error(e);
} finally {
isAbort.current = false;
}
};