188 lines
5.5 KiB
TypeScript
188 lines
5.5 KiB
TypeScript
|
import type { ComponentProcessProps } from "components/system/Apps/RenderComponent";
|
||
|
import StyledButton from "components/system/Dialogs/Transfer/StyledButton";
|
||
|
import StyledTransfer from "components/system/Dialogs/Transfer/StyledTransfer";
|
||
|
import type {
|
||
|
FileReaders,
|
||
|
ObjectReaders,
|
||
|
} from "components/system/Dialogs/Transfer/useTransferDialog";
|
||
|
import { useProcesses } from "contexts/process";
|
||
|
import { basename, dirname } from "path";
|
||
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||
|
import { ONE_TIME_PASSIVE_EVENT } from "utils/constants";
|
||
|
import { haltEvent } from "utils/functions";
|
||
|
|
||
|
const MAX_TITLE_LENGTH = 37;
|
||
|
|
||
|
const isFileReaders = (
|
||
|
readers?: FileReaders | ObjectReaders
|
||
|
): readers is FileReaders => Array.isArray(readers?.[0]);
|
||
|
|
||
|
const Transfer: FC<ComponentProcessProps> = ({ id }) => {
|
||
|
const {
|
||
|
argument,
|
||
|
closeWithTransition,
|
||
|
processes: { [id]: process } = {},
|
||
|
title,
|
||
|
} = useProcesses();
|
||
|
const { closing, fileReaders, url } = process || {};
|
||
|
const [currentTransfer, setCurrentTransfer] = useState<[string, File]>();
|
||
|
const [cd = "", { name = "" } = {}] = currentTransfer || [];
|
||
|
const [progress, setProgress] = useState<number>(0);
|
||
|
const actionName = useMemo(
|
||
|
() => (url && !fileReaders ? "Extraindo" : "Copiando"),
|
||
|
[fileReaders, url]
|
||
|
);
|
||
|
const processing = useRef(false);
|
||
|
const completeTransfer = useCallback(() => {
|
||
|
processing.current = false;
|
||
|
closeWithTransition(id);
|
||
|
}, [closeWithTransition, id]);
|
||
|
const processObjectReader = useCallback(
|
||
|
([reader, ...remainingReaders]: ObjectReaders) => {
|
||
|
const isComplete = remainingReaders.length === 0;
|
||
|
|
||
|
reader.read().then(() => {
|
||
|
setProgress((currentProgress) => currentProgress + 1);
|
||
|
|
||
|
if (isComplete) {
|
||
|
reader.done?.();
|
||
|
completeTransfer();
|
||
|
} else {
|
||
|
const [{ directory, name: nextName }] = remainingReaders;
|
||
|
|
||
|
setCurrentTransfer([directory, { name: nextName } as File]);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
if (!isComplete) processObjectReader(remainingReaders);
|
||
|
},
|
||
|
[completeTransfer]
|
||
|
);
|
||
|
const processFileReader = useCallback(
|
||
|
([[file, directory, reader], ...remainingReaders]: FileReaders) => {
|
||
|
let fileProgress = 0;
|
||
|
|
||
|
setCurrentTransfer([directory, file]);
|
||
|
|
||
|
reader.addEventListener(
|
||
|
"progress",
|
||
|
({ loaded = 0 }) => {
|
||
|
const progressLoaded = loaded - fileProgress;
|
||
|
|
||
|
setProgress((currentProgress) => currentProgress + progressLoaded);
|
||
|
fileProgress = loaded;
|
||
|
},
|
||
|
{ passive: true }
|
||
|
);
|
||
|
reader.addEventListener(
|
||
|
"loadend",
|
||
|
() => {
|
||
|
if (remainingReaders.length > 0) {
|
||
|
processFileReader(remainingReaders);
|
||
|
} else {
|
||
|
completeTransfer();
|
||
|
}
|
||
|
},
|
||
|
ONE_TIME_PASSIVE_EVENT
|
||
|
);
|
||
|
reader.readAsArrayBuffer(file);
|
||
|
},
|
||
|
[completeTransfer]
|
||
|
);
|
||
|
const totalTransferSize = useMemo(
|
||
|
() =>
|
||
|
isFileReaders(fileReaders)
|
||
|
? fileReaders.reduce((acc, [{ size = 0 }]) => acc + size, 0)
|
||
|
: fileReaders?.length || Number.POSITIVE_INFINITY,
|
||
|
[fileReaders]
|
||
|
);
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (!processing.current) {
|
||
|
if (fileReaders) {
|
||
|
if (fileReaders?.length > 0) {
|
||
|
processing.current = true;
|
||
|
|
||
|
if (isFileReaders(fileReaders)) {
|
||
|
processFileReader(fileReaders);
|
||
|
} else {
|
||
|
const [{ directory, name: firstName }] = fileReaders;
|
||
|
|
||
|
setCurrentTransfer([directory, { name: firstName } as File]);
|
||
|
processObjectReader(fileReaders);
|
||
|
}
|
||
|
} else {
|
||
|
closeWithTransition(id);
|
||
|
}
|
||
|
} else if (url) {
|
||
|
setCurrentTransfer([dirname(url), { name: basename(url) } as File]);
|
||
|
}
|
||
|
}
|
||
|
}, [
|
||
|
closeWithTransition,
|
||
|
fileReaders,
|
||
|
id,
|
||
|
processFileReader,
|
||
|
processObjectReader,
|
||
|
url,
|
||
|
]);
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (processing.current) {
|
||
|
const progressPercent = Math.floor((progress / totalTransferSize) * 100);
|
||
|
|
||
|
argument(id, "progress", progressPercent);
|
||
|
title(id, `${progressPercent}% completo`);
|
||
|
}
|
||
|
}, [argument, id, progress, title, totalTransferSize]);
|
||
|
|
||
|
useEffect(() => title(id, `${actionName}...`), [actionName, id, title]);
|
||
|
|
||
|
useEffect(
|
||
|
() => () => {
|
||
|
if (closing && processing.current) {
|
||
|
if (isFileReaders(fileReaders)) {
|
||
|
// eslint-disable-next-line unicorn/no-unreadable-array-destructuring
|
||
|
fileReaders.forEach(([, , reader]) => reader.abort());
|
||
|
} else {
|
||
|
fileReaders?.forEach((reader) => reader.abort());
|
||
|
fileReaders?.[0]?.done?.();
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
[closing, fileReaders]
|
||
|
);
|
||
|
|
||
|
return (
|
||
|
<StyledTransfer onContextMenu={haltEvent}>
|
||
|
<h1>
|
||
|
{name
|
||
|
? `${actionName} '${
|
||
|
name.length >= MAX_TITLE_LENGTH
|
||
|
? `${name.slice(0, MAX_TITLE_LENGTH)}...`
|
||
|
: name
|
||
|
}'`
|
||
|
: ""}
|
||
|
</h1>
|
||
|
<div>
|
||
|
<h2>{cd ? `Para '${cd}'` : ""}</h2>
|
||
|
<progress
|
||
|
max={totalTransferSize}
|
||
|
value={
|
||
|
totalTransferSize === Number.POSITIVE_INFINITY
|
||
|
? undefined
|
||
|
: progress
|
||
|
}
|
||
|
/>
|
||
|
</div>
|
||
|
<nav>
|
||
|
<StyledButton onClick={() => closeWithTransition(id)}>
|
||
|
Cancel
|
||
|
</StyledButton>
|
||
|
</nav>
|
||
|
</StyledTransfer>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
export default Transfer;
|