178 lines
5.5 KiB
TypeScript
178 lines
5.5 KiB
TypeScript
|
import StyledPaint from "components/apps/Paint/StyledPaint";
|
||
|
import type { ComponentProcessProps } from "components/system/Apps/RenderComponent";
|
||
|
import StyledLoading from "components/system/Files/FileManager/StyledLoading";
|
||
|
import useFileDrop from "components/system/Files/FileManager/useFileDrop";
|
||
|
import useTitle from "components/system/Window/useTitle";
|
||
|
import { useFileSystem } from "contexts/fileSystem";
|
||
|
import { useProcesses } from "contexts/process";
|
||
|
import { useSession } from "contexts/session";
|
||
|
import type { WallpaperFit } from "contexts/session/types";
|
||
|
import { basename, dirname, join } from "path";
|
||
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||
|
import {
|
||
|
DESKTOP_PATH,
|
||
|
IFRAME_CONFIG,
|
||
|
ONE_TIME_PASSIVE_EVENT,
|
||
|
PICUTRES_PATH,
|
||
|
} from "utils/constants";
|
||
|
|
||
|
type JsPaint = {
|
||
|
close: () => void;
|
||
|
file_new: () => void;
|
||
|
open_from_file: (file: File, fileHandle: string) => void;
|
||
|
storage_quota_exceeded: () => void;
|
||
|
systemHooks: {
|
||
|
setWallpaperCentered: (canvas: HTMLCanvasElement) => void;
|
||
|
setWallpaperTiled: (canvas: HTMLCanvasElement) => void;
|
||
|
showOpenFileDialog: (props: {
|
||
|
formats: unknown[];
|
||
|
}) => Promise<{ file: File }>;
|
||
|
showSaveFileDialog: (props: {
|
||
|
defaultFileName: string;
|
||
|
getBlob: (mimeType: string) => Promise<Blob>;
|
||
|
}) => void;
|
||
|
writeBlobToHandle: (fileHandle: string, blob: Blob) => Promise<void>;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
const Paint: FC<ComponentProcessProps> = ({ id }) => {
|
||
|
const {
|
||
|
closeWithTransition,
|
||
|
processes: { [id]: { libs: [paintSrc = ""] = [], url = "" } = {} } = {},
|
||
|
} = useProcesses();
|
||
|
const { createPath, exists, readFile, updateFolder, writeFile } =
|
||
|
useFileSystem();
|
||
|
const { foregroundId, setForegroundId, setWallpaper } = useSession();
|
||
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||
|
const [loaded, setLoaded] = useState(false);
|
||
|
const [jsPaintInstance, setJsPaintInstance] = useState<JsPaint>();
|
||
|
const { prependFileToTitle } = useTitle(id);
|
||
|
const setWallpaperFromCanvas = useCallback(
|
||
|
(fit: WallpaperFit) =>
|
||
|
(canvas: HTMLCanvasElement): void => {
|
||
|
const wallpaperPath = join(PICUTRES_PATH, "wallpaper.png");
|
||
|
|
||
|
canvas.toBlob(async (blob) => {
|
||
|
await writeFile(
|
||
|
wallpaperPath,
|
||
|
Buffer.from((await blob?.arrayBuffer()) as ArrayBuffer),
|
||
|
true
|
||
|
);
|
||
|
setWallpaper(wallpaperPath, fit);
|
||
|
});
|
||
|
},
|
||
|
[setWallpaper, writeFile]
|
||
|
);
|
||
|
const { onDragOver, onDrop } = useFileDrop({ id });
|
||
|
|
||
|
useEffect(() => {
|
||
|
prependFileToTitle("Untitled");
|
||
|
}, [prependFileToTitle]);
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (foregroundId !== id) {
|
||
|
iframeRef.current?.contentWindow?.addEventListener(
|
||
|
"click",
|
||
|
() => setForegroundId(id),
|
||
|
ONE_TIME_PASSIVE_EVENT
|
||
|
);
|
||
|
}
|
||
|
}, [foregroundId, id, setForegroundId]);
|
||
|
|
||
|
useEffect(() => {
|
||
|
const { contentWindow } = iframeRef.current || {};
|
||
|
|
||
|
if (loaded && contentWindow && !jsPaintInstance) {
|
||
|
const jsPaint = contentWindow as unknown as JsPaint;
|
||
|
const previousOpenFileDialog = jsPaint.systemHooks.showOpenFileDialog;
|
||
|
const previousFileNew = jsPaint.file_new;
|
||
|
|
||
|
setJsPaintInstance(jsPaint);
|
||
|
|
||
|
jsPaint.file_new = () => {
|
||
|
prependFileToTitle("Untitled");
|
||
|
previousFileNew();
|
||
|
};
|
||
|
jsPaint.systemHooks.setWallpaperTiled = setWallpaperFromCanvas("tile");
|
||
|
jsPaint.systemHooks.setWallpaperCentered =
|
||
|
setWallpaperFromCanvas("center");
|
||
|
jsPaint.systemHooks.showOpenFileDialog = async (props) => {
|
||
|
const { file } = await previousOpenFileDialog(props);
|
||
|
|
||
|
prependFileToTitle(file.name);
|
||
|
|
||
|
return { file };
|
||
|
};
|
||
|
jsPaint.close = () => closeWithTransition(id);
|
||
|
jsPaint.storage_quota_exceeded = () => {
|
||
|
// Ignore Storage Warning
|
||
|
};
|
||
|
jsPaint.systemHooks.showSaveFileDialog = async ({
|
||
|
defaultFileName,
|
||
|
getBlob,
|
||
|
}) =>
|
||
|
updateFolder(
|
||
|
DESKTOP_PATH,
|
||
|
await createPath(
|
||
|
`${defaultFileName}.png`,
|
||
|
DESKTOP_PATH,
|
||
|
Buffer.from(await (await getBlob("image/png")).arrayBuffer())
|
||
|
)
|
||
|
);
|
||
|
jsPaint.systemHooks.writeBlobToHandle = async (fileHandle, blob) => {
|
||
|
if (await exists(fileHandle)) {
|
||
|
await writeFile(
|
||
|
fileHandle,
|
||
|
Buffer.from(await blob.arrayBuffer()),
|
||
|
true
|
||
|
);
|
||
|
updateFolder(dirname(fileHandle), basename(fileHandle));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
contentWindow.addEventListener("dragover", onDragOver);
|
||
|
contentWindow.addEventListener("drop", onDrop);
|
||
|
}
|
||
|
}, [
|
||
|
closeWithTransition,
|
||
|
createPath,
|
||
|
exists,
|
||
|
id,
|
||
|
jsPaintInstance,
|
||
|
loaded,
|
||
|
onDragOver,
|
||
|
onDrop,
|
||
|
prependFileToTitle,
|
||
|
setWallpaperFromCanvas,
|
||
|
updateFolder,
|
||
|
writeFile,
|
||
|
]);
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (jsPaintInstance && url) {
|
||
|
readFile(url).then((buffer) => {
|
||
|
jsPaintInstance.open_from_file(new File([buffer], url), url);
|
||
|
prependFileToTitle(basename(url));
|
||
|
});
|
||
|
}
|
||
|
}, [jsPaintInstance, prependFileToTitle, readFile, url]);
|
||
|
|
||
|
return (
|
||
|
<StyledPaint $loaded={loaded}>
|
||
|
{!loaded && <StyledLoading className="loading" />}
|
||
|
<iframe
|
||
|
ref={iframeRef}
|
||
|
height="100%"
|
||
|
id="jspaint-iframe"
|
||
|
onLoad={() => setLoaded(true)}
|
||
|
src={paintSrc}
|
||
|
title={id}
|
||
|
width="100%"
|
||
|
{...IFRAME_CONFIG}
|
||
|
/>
|
||
|
</StyledPaint>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
export default Paint;
|