securityos/components/system/Desktop/Wallpapers/useWallpaper.ts

310 lines
9.3 KiB
TypeScript

import {
BASE_CANVAS_SELECTOR,
bgPositionSize,
WALLPAPER_PATHS,
WALLPAPER_WORKERS,
} from "components/system/Desktop/Wallpapers/constants";
import type { WallpaperConfig } from "components/system/Desktop/Wallpapers/types";
import { config as vantaConfig } from "components/system/Desktop/Wallpapers/vantaWaves/config";
import { useFileSystem } from "contexts/fileSystem";
import { useSession } from "contexts/session";
import useWorker from "hooks/useWorker";
import { extname, join } from "path";
import { useCallback, useEffect, useRef } from "react";
import { useTheme } from "styled-components";
import {
DEFAULT_LOCALE,
HIGH_PRIORITY_REQUEST,
IMAGE_FILE_EXTENSIONS,
MILLISECONDS_IN_DAY,
MILLISECONDS_IN_MINUTE,
PICTURES_FOLDER,
SLIDESHOW_FILE,
UNSUPPORTED_BACKGROUND_EXTENSIONS,
} from "utils/constants";
import {
bufferToUrl,
cleanUpBufferUrl,
createOffscreenCanvas,
getYouTubeUrlId,
isYouTubeUrl,
jsonFetch,
viewWidth,
} from "utils/functions";
declare global {
interface Window {
WallpaperDestroy: () => void;
}
}
const WALLPAPER_WORKER_NAMES = Object.keys(WALLPAPER_WORKERS);
let slideshowFiles: string[];
const useWallpaper = (
desktopRef: React.MutableRefObject<HTMLElement | null>
): void => {
const { exists, lstat, readFile, readdir, updateFolder, writeFile } =
useFileSystem();
const { sessionLoaded, setWallpaper, wallpaperImage, wallpaperFit } =
useSession();
const { colors } = useTheme();
const [wallpaperName] = wallpaperImage.split(" ");
const vantaWireframe = wallpaperImage === "VANTA WIREFRAME";
const wallpaperWorker = useWorker<void>(
WALLPAPER_WORKERS[wallpaperName],
undefined,
vantaWireframe ? "Wireframe" : ""
);
const resizeListener = useCallback(() => {
if (!desktopRef.current) return;
const desktopRect = desktopRef.current.getBoundingClientRect();
wallpaperWorker.current?.postMessage(desktopRect);
const canvasElement =
desktopRef.current.querySelector(BASE_CANVAS_SELECTOR);
if (canvasElement instanceof HTMLCanvasElement) {
canvasElement.style.width = `${desktopRect.width}px`;
canvasElement.style.height = `${desktopRect.height}px`;
}
}, [desktopRef, wallpaperWorker]);
const loadWallpaper = useCallback(() => {
if (!desktopRef.current) return;
let config: WallpaperConfig | undefined;
if (wallpaperName === "VANTA") {
config = { ...vantaConfig };
vantaConfig.material.options.wireframe = vantaWireframe;
} else if (wallpaperImage === "SLIDESHOW") {
config = { volumetric: true };
}
document.documentElement.style.setProperty("background", "");
desktopRef.current.querySelector(BASE_CANVAS_SELECTOR)?.remove();
window.WallpaperDestroy?.();
if (window.OffscreenCanvas !== undefined && wallpaperWorker.current) {
const offscreen = createOffscreenCanvas(desktopRef.current);
wallpaperWorker.current.postMessage(
{ canvas: offscreen, config, devicePixelRatio: 1 },
[offscreen]
);
window.removeEventListener("resize", resizeListener);
window.addEventListener("resize", resizeListener, { passive: true });
} else if (WALLPAPER_PATHS[wallpaperName]) {
WALLPAPER_PATHS[wallpaperName]().then(({ default: wallpaper }) =>
wallpaper?.(desktopRef.current, config)
);
} else {
setWallpaper("VANTA");
}
}, [
desktopRef,
resizeListener,
setWallpaper,
vantaWireframe,
wallpaperImage,
wallpaperName,
wallpaperWorker,
]);
const slideshowTimerRef = useRef<number>();
const getAllImages = useCallback(
async (baseDirectory: string): Promise<string[]> =>
(await readdir(baseDirectory)).reduce<Promise<string[]>>(
async (images, entry) => {
const entryPath = join(baseDirectory, entry);
return [
...(await images),
...((await lstat(entryPath)).isDirectory()
? await getAllImages(entryPath)
: [
IMAGE_FILE_EXTENSIONS.has(extname(entryPath)) &&
!UNSUPPORTED_BACKGROUND_EXTENSIONS.has(extname(entryPath))
? entryPath
: "",
]),
].filter(Boolean);
},
Promise.resolve([])
),
[readdir, lstat]
);
const loadFileWallpaper = useCallback(async () => {
const [, currentWallpaperUrl] =
document.documentElement.style.background.match(/"(.*?)"/) || [];
if (currentWallpaperUrl?.startsWith("blob:")) {
cleanUpBufferUrl(currentWallpaperUrl);
}
desktopRef.current?.querySelector(BASE_CANVAS_SELECTOR)?.remove();
let wallpaperUrl = "";
let fallbackBackground = "";
let newWallpaperFit = wallpaperFit;
const isSlideshow = wallpaperName === "SLIDESHOW";
if (isSlideshow) {
const slideshowFilePath = `${PICTURES_FOLDER}/${SLIDESHOW_FILE}`;
if (!(await exists(slideshowFilePath))) {
await writeFile(
slideshowFilePath,
JSON.stringify(
(await exists(PICTURES_FOLDER))
? await getAllImages(PICTURES_FOLDER)
: "[]"
)
);
updateFolder(PICTURES_FOLDER, SLIDESHOW_FILE);
}
slideshowFiles ||= [
...new Set(
JSON.parse(
(
await readFile(`${PICTURES_FOLDER}/${SLIDESHOW_FILE}`)
)?.toString() || "[]"
) as string[]
),
];
do {
wallpaperUrl =
slideshowFiles[Math.floor(Math.random() * slideshowFiles.length)];
if (wallpaperUrl.startsWith("/")) {
wallpaperUrl = `${window.location.origin}${wallpaperUrl}`;
}
} while (
currentWallpaperUrl === wallpaperUrl &&
slideshowFiles.length > 1
);
newWallpaperFit = "fill";
} else if (wallpaperName === "APOD") {
document.documentElement.style.setProperty("background", "");
const [, currentUrl, currentDate] = wallpaperImage.split(" ");
const [month, , day, , year] = new Intl.DateTimeFormat(DEFAULT_LOCALE, {
timeZone: "US/Eastern",
})
.formatToParts(Date.now())
.map(({ value }) => value);
if (currentDate === `${year}-${month}-${day}`) {
wallpaperUrl = currentUrl;
} else {
const {
date = "",
hdurl = "",
url = "",
} = await jsonFetch(
"https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"
);
if (hdurl || url) {
wallpaperUrl = ((viewWidth() > 1024 ? hdurl : url) || url) as string;
newWallpaperFit = "fit";
if (isYouTubeUrl(wallpaperUrl)) {
const ytBaseUrl = `https://i.ytimg.com/vi/${getYouTubeUrlId(
wallpaperUrl
)}`;
wallpaperUrl = `${ytBaseUrl}/maxresdefault.jpg`;
fallbackBackground = `${ytBaseUrl}/hqdefault.jpg`;
} else if (hdurl && url && hdurl !== url) {
fallbackBackground = (wallpaperUrl === url ? hdurl : url) as string;
}
const newWallpaperImage = `APOD ${wallpaperUrl} ${date as string}`;
if (newWallpaperImage !== wallpaperImage) {
setWallpaper(newWallpaperImage, newWallpaperFit);
setTimeout(loadWallpaper, MILLISECONDS_IN_DAY);
}
}
}
} else if (await exists(wallpaperImage)) {
wallpaperUrl = bufferToUrl(await readFile(wallpaperImage));
}
if (wallpaperUrl) {
const applyWallpaper = (url: string): void => {
const repeat = newWallpaperFit === "tile" ? "repeat" : "no-repeat";
const positionSize = bgPositionSize[newWallpaperFit];
document.documentElement.style.setProperty(
"background",
`url(${CSS.escape(
url
)}) ${positionSize} ${repeat} fixed border-box border-box ${
colors.background
}`
);
};
if (fallbackBackground) {
fetch(wallpaperUrl, {
...HIGH_PRIORITY_REQUEST,
mode: "no-cors",
})
.then(({ ok }) => {
if (!ok) throw new Error("Failed to load url");
})
.catch(() => applyWallpaper(fallbackBackground));
} else {
applyWallpaper(wallpaperUrl);
if (isSlideshow) {
slideshowTimerRef.current = window.setTimeout(
loadFileWallpaper,
MILLISECONDS_IN_MINUTE
);
}
}
} else {
loadWallpaper();
}
}, [
colors.background,
desktopRef,
exists,
getAllImages,
loadWallpaper,
readFile,
setWallpaper,
updateFolder,
wallpaperFit,
wallpaperImage,
wallpaperName,
writeFile,
]);
useEffect(() => {
if (sessionLoaded) {
if (slideshowTimerRef.current) {
window.clearTimeout(slideshowTimerRef.current);
}
if (wallpaperName && !WALLPAPER_WORKER_NAMES.includes(wallpaperName)) {
loadFileWallpaper().catch(loadWallpaper);
} else {
loadWallpaper();
}
}
}, [loadFileWallpaper, loadWallpaper, sessionLoaded, wallpaperName]);
};
export default useWallpaper;