2024-10-16 01:48:11 +00:00
|
|
|
|
"use client";
|
2024-10-24 00:02:29 +00:00
|
|
|
|
import { useCallback, useMemo, useState, useTransition } from "react";
|
2024-10-16 01:48:11 +00:00
|
|
|
|
import { useDropzone } from "react-dropzone";
|
|
|
|
|
|
import orderBy from "lodash.orderby";
|
2024-10-24 00:02:29 +00:00
|
|
|
|
import { FaGithub } from "react-icons/fa";
|
|
|
|
|
|
import { Loading } from "./Loading";
|
|
|
|
|
|
import { FileItem } from "./FileItem";
|
|
|
|
|
|
import { DownloadForm } from "./DownloadForm";
|
|
|
|
|
|
import { handleInputFile } from "./utils";
|
2024-10-16 01:48:11 +00:00
|
|
|
|
import styles from "./Forge.module.css";
|
|
|
|
|
|
|
|
|
|
|
|
export function Forge() {
|
2024-10-24 00:02:29 +00:00
|
|
|
|
const [isPending, startTransition] = useTransition();
|
|
|
|
|
|
const [loadingCount, setLoadingCount] = useState(0);
|
2024-10-16 01:48:11 +00:00
|
|
|
|
const [actionLog, setActionLog] = useState(() => []);
|
|
|
|
|
|
const [files, setFiles] = useState(() => new Map());
|
|
|
|
|
|
|
|
|
|
|
|
const onDrop = useCallback(async (acceptedFiles) => {
|
2024-10-24 00:02:29 +00:00
|
|
|
|
setLoadingCount((count) => count + 1);
|
2024-10-16 01:48:11 +00:00
|
|
|
|
const actionLog = [];
|
|
|
|
|
|
const finalMap = new Map();
|
|
|
|
|
|
const allFiles: Array<Map<string, any>> = await Promise.all(
|
|
|
|
|
|
acceptedFiles.map((file) => handleInputFile(file))
|
|
|
|
|
|
);
|
|
|
|
|
|
allFiles.forEach((map) => {
|
|
|
|
|
|
map.forEach((file, path) => {
|
|
|
|
|
|
if (finalMap.has(path)) {
|
|
|
|
|
|
actionLog.push({
|
|
|
|
|
|
type: "overwrite",
|
|
|
|
|
|
path,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
finalMap.set(path, file);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2024-10-24 00:02:29 +00:00
|
|
|
|
startTransition(() => {
|
|
|
|
|
|
setFiles((prevMap) => {
|
|
|
|
|
|
return new Map([
|
|
|
|
|
|
...Array.from(prevMap.entries()),
|
|
|
|
|
|
...Array.from(finalMap.entries()),
|
|
|
|
|
|
]);
|
|
|
|
|
|
});
|
|
|
|
|
|
setActionLog((prevLog) => [...prevLog, ...actionLog]);
|
2024-10-16 01:48:11 +00:00
|
|
|
|
});
|
2024-10-24 00:02:29 +00:00
|
|
|
|
setLoadingCount((count) => count - 1);
|
2024-10-16 01:48:11 +00:00
|
|
|
|
}, []);
|
|
|
|
|
|
const { getRootProps, getInputProps, open, isDragActive } = useDropzone({
|
|
|
|
|
|
noClick: true,
|
|
|
|
|
|
onDrop,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const fileList = useMemo(() => {
|
|
|
|
|
|
const paths = orderBy(
|
|
|
|
|
|
Array.from(files.keys()),
|
|
|
|
|
|
[(path) => path.toLowerCase()],
|
|
|
|
|
|
["asc"]
|
|
|
|
|
|
);
|
|
|
|
|
|
return paths.map((path) => files.get(path));
|
|
|
|
|
|
}, [files]);
|
|
|
|
|
|
|
|
|
|
|
|
const addButton = (
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
className={styles.AddButton}
|
|
|
|
|
|
aria-label="Add files"
|
|
|
|
|
|
title="Add files"
|
|
|
|
|
|
onClick={open}
|
|
|
|
|
|
>
|
|
|
|
|
|
+
|
|
|
|
|
|
</button>
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const handleDelete = useCallback((path) => {
|
|
|
|
|
|
setFiles((files) => {
|
|
|
|
|
|
const newFiles = new Map(files);
|
|
|
|
|
|
newFiles.delete(path);
|
|
|
|
|
|
return newFiles;
|
|
|
|
|
|
});
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
2024-10-16 03:15:45 +00:00
|
|
|
|
const handleRename = useCallback((oldPath, newPath) => {
|
|
|
|
|
|
setFiles((files) => {
|
|
|
|
|
|
const file = files.get(oldPath);
|
|
|
|
|
|
const newFile = { ...file, path: newPath };
|
|
|
|
|
|
const newFiles = new Map(files);
|
|
|
|
|
|
newFiles.delete(oldPath);
|
|
|
|
|
|
newFiles.set(newPath, newFile);
|
|
|
|
|
|
return newFiles;
|
|
|
|
|
|
});
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
2024-10-24 00:02:29 +00:00
|
|
|
|
const showLoading = isPending || loadingCount > 0;
|
|
|
|
|
|
|
2024-10-16 01:48:11 +00:00
|
|
|
|
return (
|
2024-10-24 00:02:29 +00:00
|
|
|
|
<section className={styles.Forge} {...getRootProps()}>
|
|
|
|
|
|
<header className={styles.Header}>
|
|
|
|
|
|
<a
|
|
|
|
|
|
className={styles.HeaderLink}
|
|
|
|
|
|
href="https://github.com/exogen/vl2-forge"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaGithub aria-label="GitHub" />
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<img
|
|
|
|
|
|
className={styles.Logo}
|
|
|
|
|
|
width={210}
|
|
|
|
|
|
height={188}
|
|
|
|
|
|
src="/vl2-forge/logo-md.png"
|
|
|
|
|
|
alt="VL2 Forge"
|
|
|
|
|
|
/>
|
|
|
|
|
|
{addButton}
|
|
|
|
|
|
</header>
|
|
|
|
|
|
<input {...getInputProps()} />
|
|
|
|
|
|
<div className={styles.ListArea}>
|
|
|
|
|
|
{showLoading ? <Loading /> : null}
|
|
|
|
|
|
{fileList.length ? (
|
|
|
|
|
|
<ul className={styles.FileList}>
|
|
|
|
|
|
{fileList.map((file) => {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<li key={file.path}>
|
|
|
|
|
|
<FileItem
|
|
|
|
|
|
file={file}
|
|
|
|
|
|
onDelete={handleDelete}
|
|
|
|
|
|
onRename={handleRename}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
);
|
|
|
|
|
|
})}
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
) : showLoading ? null : (
|
|
|
|
|
|
<div className={styles.EmptyMessage}>
|
|
|
|
|
|
Drop files onto the page or press the add button. No need to extract
|
|
|
|
|
|
existing .vl2 files first – just drop ‘em in and it’ll take care of
|
|
|
|
|
|
that!
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
2024-10-16 01:48:11 +00:00
|
|
|
|
<footer className={styles.Footer}>
|
2024-10-16 08:10:21 +00:00
|
|
|
|
<a
|
|
|
|
|
|
className={styles.FooterLink}
|
|
|
|
|
|
href="https://github.com/exogen/vl2-forge"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaGithub aria-label="GitHub" />
|
|
|
|
|
|
</a>
|
2024-10-24 00:02:29 +00:00
|
|
|
|
<DownloadForm fileList={fileList} />
|
2024-10-16 01:48:11 +00:00
|
|
|
|
</footer>
|
2024-10-24 00:02:29 +00:00
|
|
|
|
</section>
|
2024-10-16 01:48:11 +00:00
|
|
|
|
);
|
|
|
|
|
|
}
|