mirror of
https://github.com/DustinBrett/daedalOS.git
synced 2025-12-06 00:20:05 +01:00
Memoize more stuff
This commit is contained in:
parent
5a2edfd7c7
commit
14731ccda6
|
|
@ -58,10 +58,12 @@ const Run: FC<ComponentProcessProps> = ({ id }) => {
|
|||
const [isInputFocused, setIsInputFocused] = useState(true);
|
||||
const [isEmptyInput, setIsEmptyInput] = useState(!runHistory[0]);
|
||||
const [running, setRunning] = useState(false);
|
||||
const checkIsEmpty: React.KeyboardEventHandler | React.ChangeEventHandler = ({
|
||||
target,
|
||||
}: React.KeyboardEvent | React.ChangeEvent): void =>
|
||||
setIsEmptyInput(!(target as HTMLInputElement)?.value);
|
||||
const checkIsEmpty: React.KeyboardEventHandler | React.ChangeEventHandler =
|
||||
useCallback(
|
||||
({ target }: React.KeyboardEvent | React.ChangeEvent): void =>
|
||||
setIsEmptyInput(!(target as HTMLInputElement)?.value),
|
||||
[]
|
||||
);
|
||||
const runResource = useCallback(
|
||||
async (resource?: string) => {
|
||||
if (!resource) return;
|
||||
|
|
|
|||
|
|
@ -93,7 +93,10 @@ const useFileContextMenu = (
|
|||
updateRecentFiles,
|
||||
} = useSession();
|
||||
const baseName = basename(path);
|
||||
const isFocusedEntry = focusedEntries.includes(baseName);
|
||||
const isFocusedEntry = useMemo(
|
||||
() => focusedEntries.includes(baseName),
|
||||
[baseName, focusedEntries]
|
||||
);
|
||||
const openFile = useFile(url, path);
|
||||
const {
|
||||
copyEntries,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
getInfoWithExtension,
|
||||
getInfoWithoutExtension,
|
||||
|
|
@ -32,10 +32,10 @@ const useFileInfo = (
|
|||
): [FileInfo, React.Dispatch<React.SetStateAction<FileInfo>>] => {
|
||||
const [info, setInfo] = useState<FileInfo>(INITIAL_FILE_INFO);
|
||||
const updatingInfo = useRef(false);
|
||||
const updateInfo = (newInfo: FileInfo): void => {
|
||||
const updateInfo = useCallback((newInfo: FileInfo): void => {
|
||||
setInfo(newInfo);
|
||||
updatingInfo.current = false;
|
||||
};
|
||||
}, []);
|
||||
const { fs, rootFs } = useFileSystem();
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -68,7 +68,16 @@ const useFileInfo = (
|
|||
getInfoWithExtension(fs, path, extension, updateInfo);
|
||||
}
|
||||
}
|
||||
}, [fs, hasNewFolderIcon, info, isDirectory, isVisible, path, rootFs]);
|
||||
}, [
|
||||
fs,
|
||||
hasNewFolderIcon,
|
||||
info,
|
||||
isDirectory,
|
||||
isVisible,
|
||||
path,
|
||||
rootFs,
|
||||
updateInfo,
|
||||
]);
|
||||
|
||||
return [info, setInfo];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -48,127 +48,154 @@ const useDraggableEntries = (
|
|||
const dragPositionRef = useRef<DragPosition>(
|
||||
Object.create(null) as DragPosition
|
||||
);
|
||||
const onDragging = ({ clientX: x, clientY: y }: DragEvent): void => {
|
||||
dragPositionRef.current = { ...dragPositionRef.current, x, y };
|
||||
};
|
||||
const onDragging = useCallback(
|
||||
({ clientX: x, clientY: y }: DragEvent): void => {
|
||||
dragPositionRef.current = { ...dragPositionRef.current, x, y };
|
||||
},
|
||||
[]
|
||||
);
|
||||
const isMainContainer =
|
||||
fileManagerRef.current?.parentElement?.tagName === "MAIN";
|
||||
const onDragEnd =
|
||||
const onDragEnd = useCallback(
|
||||
(entryUrl: string): React.DragEventHandler =>
|
||||
(event) => {
|
||||
haltEvent(event);
|
||||
(event) => {
|
||||
haltEvent(event);
|
||||
|
||||
if (allowMoving && focusedEntries.length > 0) {
|
||||
updateIconPositions(
|
||||
entryUrl,
|
||||
fileManagerRef.current,
|
||||
iconPositions,
|
||||
sortOrders,
|
||||
dragPositionRef.current,
|
||||
focusedEntries,
|
||||
setIconPositions,
|
||||
exists
|
||||
);
|
||||
fileManagerRef.current?.removeEventListener("dragover", onDragging);
|
||||
} else if (dropIndex !== -1) {
|
||||
setSortOrder(entryUrl, (currentSortOrders) => {
|
||||
const sortedEntries = currentSortOrders.filter(
|
||||
(entry) => !focusedEntries.includes(entry)
|
||||
if (allowMoving && focusedEntries.length > 0) {
|
||||
updateIconPositions(
|
||||
entryUrl,
|
||||
fileManagerRef.current,
|
||||
iconPositions,
|
||||
sortOrders,
|
||||
dragPositionRef.current,
|
||||
focusedEntries,
|
||||
setIconPositions,
|
||||
exists
|
||||
);
|
||||
fileManagerRef.current?.removeEventListener("dragover", onDragging);
|
||||
} else if (dropIndex !== -1) {
|
||||
setSortOrder(entryUrl, (currentSortOrders) => {
|
||||
const sortedEntries = currentSortOrders.filter(
|
||||
(entry) => !focusedEntries.includes(entry)
|
||||
);
|
||||
|
||||
sortedEntries.splice(dropIndex, 0, ...focusedEntries);
|
||||
sortedEntries.splice(dropIndex, 0, ...focusedEntries);
|
||||
|
||||
return sortedEntries;
|
||||
});
|
||||
}
|
||||
};
|
||||
const onDragOver =
|
||||
return sortedEntries;
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
allowMoving,
|
||||
dropIndex,
|
||||
exists,
|
||||
fileManagerRef,
|
||||
focusedEntries,
|
||||
iconPositions,
|
||||
onDragging,
|
||||
setIconPositions,
|
||||
setSortOrder,
|
||||
sortOrders,
|
||||
]
|
||||
);
|
||||
const onDragOver = useCallback(
|
||||
(file: string): React.DragEventHandler =>
|
||||
({ target }) => {
|
||||
if (!allowMoving && target instanceof HTMLLIElement) {
|
||||
const { children = [] } = target.parentElement || {};
|
||||
const dragOverFocused = focusedEntries.includes(file);
|
||||
({ target }) => {
|
||||
if (!allowMoving && target instanceof HTMLLIElement) {
|
||||
const { children = [] } = target.parentElement || {};
|
||||
const dragOverFocused = focusedEntries.includes(file);
|
||||
|
||||
setDropIndex(dragOverFocused ? -1 : [...children].indexOf(target));
|
||||
}
|
||||
};
|
||||
const onDragStart =
|
||||
setDropIndex(dragOverFocused ? -1 : [...children].indexOf(target));
|
||||
}
|
||||
},
|
||||
[allowMoving, focusedEntries]
|
||||
);
|
||||
const onDragStart = useCallback(
|
||||
(
|
||||
entryUrl: string,
|
||||
file: string,
|
||||
renaming: boolean
|
||||
): React.DragEventHandler =>
|
||||
(event) => {
|
||||
if (renaming || "ontouchstart" in window) {
|
||||
haltEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
focusEntry(file);
|
||||
|
||||
const singleFile = focusedEntries.length <= 1;
|
||||
|
||||
event.nativeEvent.dataTransfer?.setData(
|
||||
"application/json",
|
||||
JSON.stringify(
|
||||
singleFile
|
||||
? [join(entryUrl, file)]
|
||||
: focusedEntries.map((entryFile) => join(entryUrl, entryFile))
|
||||
)
|
||||
);
|
||||
|
||||
if (singleFile) {
|
||||
event.nativeEvent.dataTransfer?.setData(
|
||||
"DownloadURL",
|
||||
`${getMimeType(file) || "application/octet-stream"}:${file}:${
|
||||
window.location.href
|
||||
}${join(entryUrl, file)}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!singleFile && dragImageRef.current) {
|
||||
if (!adjustedCaptureOffsetRef.current) {
|
||||
adjustedCaptureOffsetRef.current = true;
|
||||
|
||||
const hasCapturedImageOffset =
|
||||
capturedImageOffset.current.x || capturedImageOffset.current.y;
|
||||
|
||||
capturedImageOffset.current = {
|
||||
x: hasCapturedImageOffset
|
||||
? event.nativeEvent.clientX - capturedImageOffset.current.x
|
||||
: event.nativeEvent.offsetX,
|
||||
y: hasCapturedImageOffset
|
||||
? event.nativeEvent.clientY - capturedImageOffset.current.y
|
||||
: event.nativeEvent.offsetY + FILE_MANAGER_TOP_PADDING,
|
||||
};
|
||||
(event) => {
|
||||
if (renaming || "ontouchstart" in window) {
|
||||
haltEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
event.nativeEvent.dataTransfer?.setDragImage(
|
||||
dragImageRef.current,
|
||||
isMainContainer
|
||||
? capturedImageOffset.current.x
|
||||
: event.nativeEvent.offsetX,
|
||||
isMainContainer
|
||||
? capturedImageOffset.current.y
|
||||
: event.nativeEvent.offsetY
|
||||
focusEntry(file);
|
||||
|
||||
const singleFile = focusedEntries.length <= 1;
|
||||
|
||||
event.nativeEvent.dataTransfer?.setData(
|
||||
"application/json",
|
||||
JSON.stringify(
|
||||
singleFile
|
||||
? [join(entryUrl, file)]
|
||||
: focusedEntries.map((entryFile) => join(entryUrl, entryFile))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(event.dataTransfer, { effectAllowed: "move" });
|
||||
if (singleFile) {
|
||||
event.nativeEvent.dataTransfer?.setData(
|
||||
"DownloadURL",
|
||||
`${getMimeType(file) || "application/octet-stream"}:${file}:${
|
||||
window.location.href
|
||||
}${join(entryUrl, file)}`
|
||||
);
|
||||
}
|
||||
|
||||
if (allowMoving) {
|
||||
dragPositionRef.current =
|
||||
focusedEntries.length > 1
|
||||
? {
|
||||
offsetX: event.nativeEvent.offsetX,
|
||||
offsetY: event.nativeEvent.offsetY,
|
||||
}
|
||||
: (Object.create(null) as DragPosition);
|
||||
fileManagerRef.current?.addEventListener("dragover", onDragging, {
|
||||
passive: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
if (!singleFile && dragImageRef.current) {
|
||||
if (!adjustedCaptureOffsetRef.current) {
|
||||
adjustedCaptureOffsetRef.current = true;
|
||||
|
||||
const hasCapturedImageOffset =
|
||||
capturedImageOffset.current.x || capturedImageOffset.current.y;
|
||||
|
||||
capturedImageOffset.current = {
|
||||
x: hasCapturedImageOffset
|
||||
? event.nativeEvent.clientX - capturedImageOffset.current.x
|
||||
: event.nativeEvent.offsetX,
|
||||
y: hasCapturedImageOffset
|
||||
? event.nativeEvent.clientY - capturedImageOffset.current.y
|
||||
: event.nativeEvent.offsetY + FILE_MANAGER_TOP_PADDING,
|
||||
};
|
||||
}
|
||||
|
||||
event.nativeEvent.dataTransfer?.setDragImage(
|
||||
dragImageRef.current,
|
||||
isMainContainer
|
||||
? capturedImageOffset.current.x
|
||||
: event.nativeEvent.offsetX,
|
||||
isMainContainer
|
||||
? capturedImageOffset.current.y
|
||||
: event.nativeEvent.offsetY
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(event.dataTransfer, { effectAllowed: "move" });
|
||||
|
||||
if (allowMoving) {
|
||||
dragPositionRef.current =
|
||||
focusedEntries.length > 1
|
||||
? {
|
||||
offsetX: event.nativeEvent.offsetX,
|
||||
offsetY: event.nativeEvent.offsetY,
|
||||
}
|
||||
: (Object.create(null) as DragPosition);
|
||||
fileManagerRef.current?.addEventListener("dragover", onDragging, {
|
||||
passive: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
allowMoving,
|
||||
fileManagerRef,
|
||||
focusEntry,
|
||||
focusedEntries,
|
||||
isMainContainer,
|
||||
onDragging,
|
||||
]
|
||||
);
|
||||
const updateDragImage = useCallback(async () => {
|
||||
if (fileManagerRef.current) {
|
||||
const focusedElements = [
|
||||
|
|
|
|||
|
|
@ -73,62 +73,65 @@ const useFocusableEntries = (
|
|||
});
|
||||
}, []);
|
||||
const mouseDownPositionRef = useRef({ x: 0, y: 0 });
|
||||
const focusableEntry = (file: string): FocusedEntryProps => {
|
||||
const isFocused = focusedEntries.includes(file);
|
||||
const isOnlyFocusedEntry =
|
||||
focusedEntries.length === 1 && focusedEntries[0] === file;
|
||||
const className = clsx({
|
||||
"focus-within": isFocused,
|
||||
"only-focused": isOnlyFocusedEntry,
|
||||
});
|
||||
const onMouseDown: React.MouseEventHandler = ({
|
||||
ctrlKey,
|
||||
pageX,
|
||||
pageY,
|
||||
}) => {
|
||||
mouseDownPositionRef.current = { x: pageX, y: pageY };
|
||||
const focusableEntry = useCallback(
|
||||
(file: string): FocusedEntryProps => {
|
||||
const isFocused = focusedEntries.includes(file);
|
||||
const isOnlyFocusedEntry =
|
||||
focusedEntries.length === 1 && focusedEntries[0] === file;
|
||||
const className = clsx({
|
||||
"focus-within": isFocused,
|
||||
"only-focused": isOnlyFocusedEntry,
|
||||
});
|
||||
const onMouseDown: React.MouseEventHandler = ({
|
||||
ctrlKey,
|
||||
pageX,
|
||||
pageY,
|
||||
}) => {
|
||||
mouseDownPositionRef.current = { x: pageX, y: pageY };
|
||||
|
||||
if (ctrlKey) {
|
||||
if (isFocused) {
|
||||
blurEntry(file);
|
||||
} else {
|
||||
if (ctrlKey) {
|
||||
if (isFocused) {
|
||||
blurEntry(file);
|
||||
} else {
|
||||
focusEntry(file);
|
||||
}
|
||||
} else if (!isFocused) {
|
||||
blurEntry();
|
||||
focusEntry(file);
|
||||
}
|
||||
} else if (!isFocused) {
|
||||
blurEntry();
|
||||
focusEntry(file);
|
||||
}
|
||||
};
|
||||
const onMouseUp: React.MouseEventHandler = ({
|
||||
ctrlKey,
|
||||
pageX,
|
||||
pageY,
|
||||
button,
|
||||
}) => {
|
||||
const { x, y } = mouseDownPositionRef.current;
|
||||
};
|
||||
const onMouseUp: React.MouseEventHandler = ({
|
||||
ctrlKey,
|
||||
pageX,
|
||||
pageY,
|
||||
button,
|
||||
}) => {
|
||||
const { x, y } = mouseDownPositionRef.current;
|
||||
|
||||
if (
|
||||
!ctrlKey &&
|
||||
!isOnlyFocusedEntry &&
|
||||
button === 0 &&
|
||||
x === pageX &&
|
||||
y === pageY
|
||||
) {
|
||||
blurEntry();
|
||||
focusEntry(file);
|
||||
}
|
||||
if (
|
||||
!ctrlKey &&
|
||||
!isOnlyFocusedEntry &&
|
||||
button === 0 &&
|
||||
x === pageX &&
|
||||
y === pageY
|
||||
) {
|
||||
blurEntry();
|
||||
focusEntry(file);
|
||||
}
|
||||
|
||||
mouseDownPositionRef.current = { x: 0, y: 0 };
|
||||
};
|
||||
mouseDownPositionRef.current = { x: 0, y: 0 };
|
||||
};
|
||||
|
||||
return {
|
||||
className,
|
||||
onBlurCapture,
|
||||
onFocusCapture,
|
||||
onMouseDown,
|
||||
onMouseUp,
|
||||
};
|
||||
};
|
||||
return {
|
||||
className,
|
||||
onBlurCapture,
|
||||
onFocusCapture,
|
||||
onMouseDown,
|
||||
onMouseUp,
|
||||
};
|
||||
},
|
||||
[blurEntry, focusEntry, focusedEntries, onBlurCapture, onFocusCapture]
|
||||
);
|
||||
|
||||
return { blurEntry, focusEntry, focusableEntry, focusedEntries };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -66,31 +66,38 @@ const MenuItemEntry: FC<MenuItemEntryProps> = ({
|
|||
TRANSITIONS_IN_MILLISECONDS.MOUSE_IN_OUT
|
||||
);
|
||||
}, []);
|
||||
const onMouseEnter: React.MouseEventHandler = () => {
|
||||
const onMouseEnter: React.MouseEventHandler = useCallback(() => {
|
||||
setMouseOver(true);
|
||||
if (menu) setDelayedShowSubMenu(true);
|
||||
};
|
||||
const onMouseLeave: React.MouseEventHandler = ({ relatedTarget, type }) => {
|
||||
if (
|
||||
!(relatedTarget instanceof HTMLElement) ||
|
||||
!entryRef.current?.contains(relatedTarget)
|
||||
) {
|
||||
setMouseOver(false);
|
||||
}, [menu, setDelayedShowSubMenu]);
|
||||
const onMouseLeave: React.MouseEventHandler = useCallback(
|
||||
({ relatedTarget, type }) => {
|
||||
if (
|
||||
!(relatedTarget instanceof HTMLElement) ||
|
||||
!entryRef.current?.contains(relatedTarget)
|
||||
) {
|
||||
setMouseOver(false);
|
||||
|
||||
if (type === "mouseleave") {
|
||||
setDelayedShowSubMenu(false);
|
||||
} else {
|
||||
setShowSubMenu(false);
|
||||
if (type === "mouseleave") {
|
||||
setDelayedShowSubMenu(false);
|
||||
} else {
|
||||
setShowSubMenu(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const subMenuEvents = menu
|
||||
? {
|
||||
onBlur: onMouseLeave as unknown as React.FocusEventHandler,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
}
|
||||
: {};
|
||||
},
|
||||
[setDelayedShowSubMenu]
|
||||
);
|
||||
const subMenuEvents = useMemo(
|
||||
() =>
|
||||
menu
|
||||
? {
|
||||
onBlur: onMouseLeave as unknown as React.FocusEventHandler,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
}
|
||||
: {},
|
||||
[menu, onMouseEnter, onMouseLeave]
|
||||
);
|
||||
const triggerAction = useCallback<React.MouseEventHandler>(
|
||||
(event) => {
|
||||
haltEvent(event);
|
||||
|
|
|
|||
|
|
@ -44,80 +44,86 @@ const Sidebar: FC<SidebarProps> = ({ height }) => {
|
|||
const clearTimer = (): void => {
|
||||
if (expandTimer.current) clearTimeout(expandTimer.current);
|
||||
};
|
||||
const topButtons: SidebarButtons = [
|
||||
{
|
||||
heading: true,
|
||||
icon: <SideMenu />,
|
||||
name: "START",
|
||||
...(collapsed && { tooltip: "Expand" }),
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
icon: <AllApps />,
|
||||
name: "All apps",
|
||||
...(collapsed && { tooltip: "All apps" }),
|
||||
},
|
||||
];
|
||||
const topButtons: SidebarButtons = useMemo(
|
||||
() => [
|
||||
{
|
||||
heading: true,
|
||||
icon: <SideMenu />,
|
||||
name: "START",
|
||||
...(collapsed && { tooltip: "Expand" }),
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
icon: <AllApps />,
|
||||
name: "All apps",
|
||||
...(collapsed && { tooltip: "All apps" }),
|
||||
},
|
||||
],
|
||||
[collapsed]
|
||||
);
|
||||
const { sizes } = useTheme();
|
||||
const vh = viewHeight();
|
||||
const buttonAreaCount = useMemo(
|
||||
() => Math.floor((vh - TASKBAR_HEIGHT) / sizes.startMenu.sideBar.width),
|
||||
[sizes.startMenu.sideBar.width, vh]
|
||||
);
|
||||
const bottomButtons = useMemo(
|
||||
() =>
|
||||
[
|
||||
buttonAreaCount > 3
|
||||
? {
|
||||
action: () =>
|
||||
open(
|
||||
"FileExplorer",
|
||||
{ url: `${HOME}/Documents` },
|
||||
"/System/Icons/documents.webp"
|
||||
),
|
||||
icon: <Documents />,
|
||||
name: "Documents",
|
||||
...(collapsed && { tooltip: "Documents" }),
|
||||
}
|
||||
: undefined,
|
||||
buttonAreaCount > 4
|
||||
? {
|
||||
action: () =>
|
||||
open(
|
||||
"FileExplorer",
|
||||
{ url: `${HOME}/Pictures` },
|
||||
"/System/Icons/pictures.webp"
|
||||
),
|
||||
icon: <Pictures />,
|
||||
name: "Pictures",
|
||||
...(collapsed && { tooltip: "Pictures" }),
|
||||
}
|
||||
: undefined,
|
||||
buttonAreaCount > 5
|
||||
? {
|
||||
action: () =>
|
||||
open(
|
||||
"FileExplorer",
|
||||
{ url: `${HOME}/Videos` },
|
||||
"/System/Icons/videos.webp"
|
||||
),
|
||||
icon: <Videos />,
|
||||
name: "Videos",
|
||||
...(collapsed && { tooltip: "Videos" }),
|
||||
}
|
||||
: undefined,
|
||||
{
|
||||
action: () => {
|
||||
setHaltSession(true);
|
||||
|
||||
const bottomButtons = [
|
||||
buttonAreaCount > 3
|
||||
? {
|
||||
action: () =>
|
||||
open(
|
||||
"FileExplorer",
|
||||
{ url: `${HOME}/Documents` },
|
||||
"/System/Icons/documents.webp"
|
||||
),
|
||||
icon: <Documents />,
|
||||
name: "Documents",
|
||||
...(collapsed && { tooltip: "Documents" }),
|
||||
}
|
||||
: undefined,
|
||||
buttonAreaCount > 4
|
||||
? {
|
||||
action: () =>
|
||||
open(
|
||||
"FileExplorer",
|
||||
{ url: `${HOME}/Pictures` },
|
||||
"/System/Icons/pictures.webp"
|
||||
),
|
||||
icon: <Pictures />,
|
||||
name: "Pictures",
|
||||
...(collapsed && { tooltip: "Pictures" }),
|
||||
}
|
||||
: undefined,
|
||||
buttonAreaCount > 5
|
||||
? {
|
||||
action: () =>
|
||||
open(
|
||||
"FileExplorer",
|
||||
{ url: `${HOME}/Videos` },
|
||||
"/System/Icons/videos.webp"
|
||||
),
|
||||
icon: <Videos />,
|
||||
name: "Videos",
|
||||
...(collapsed && { tooltip: "Videos" }),
|
||||
}
|
||||
: undefined,
|
||||
{
|
||||
action: () => {
|
||||
setHaltSession(true);
|
||||
|
||||
import("contexts/fileSystem/functions").then(({ resetStorage }) =>
|
||||
resetStorage(rootFs).finally(() => window.location.reload())
|
||||
);
|
||||
},
|
||||
icon: <Power />,
|
||||
name: "Power",
|
||||
tooltip: "Clears session data and reloads the page.",
|
||||
},
|
||||
].filter(Boolean) as SidebarButtons;
|
||||
import("contexts/fileSystem/functions").then(({ resetStorage }) =>
|
||||
resetStorage(rootFs).finally(() => window.location.reload())
|
||||
);
|
||||
},
|
||||
icon: <Power />,
|
||||
name: "Power",
|
||||
tooltip: "Clears session data and reloads the page.",
|
||||
},
|
||||
].filter(Boolean) as SidebarButtons,
|
||||
[buttonAreaCount, collapsed, open, rootFs, setHaltSession]
|
||||
);
|
||||
|
||||
useEffect(() => clearTimer, []);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useTheme } from "styled-components";
|
||||
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Down, Up } from "components/system/Taskbar/Calendar/Icons";
|
||||
import StyledCalendar from "components/system/Taskbar/Calendar/StyledCalendar";
|
||||
import {
|
||||
|
|
@ -32,22 +32,25 @@ const Calendar: FC<CalendarProps> = ({ toggleCalendar }) => {
|
|||
date.getFullYear() === today.getFullYear(),
|
||||
[date, today]
|
||||
);
|
||||
const changeMonth = (direction: number): void => {
|
||||
const newDate = new Date(date);
|
||||
const newMonth = newDate.getMonth() + direction;
|
||||
const changeMonth = useCallback(
|
||||
(direction: number): void => {
|
||||
const newDate = new Date(date);
|
||||
const newMonth = newDate.getMonth() + direction;
|
||||
|
||||
newDate.setDate(1);
|
||||
newDate.setMonth(newMonth);
|
||||
newDate.setDate(1);
|
||||
newDate.setMonth(newMonth);
|
||||
|
||||
const isCurrentMonth =
|
||||
(newMonth === 12 ? 0 : newMonth === -1 ? 11 : newMonth) ===
|
||||
today.getMonth();
|
||||
const isCurrentMonth =
|
||||
(newMonth === 12 ? 0 : newMonth === -1 ? 11 : newMonth) ===
|
||||
today.getMonth();
|
||||
|
||||
if (isCurrentMonth) newDate.setDate(today.getDate());
|
||||
if (isCurrentMonth) newDate.setDate(today.getDate());
|
||||
|
||||
setDate(newDate);
|
||||
setCalendar(createCalendar(newDate));
|
||||
};
|
||||
setDate(newDate);
|
||||
setCalendar(createCalendar(newDate));
|
||||
},
|
||||
[date, today]
|
||||
);
|
||||
const calendarRef = useRef<HTMLTableElement>(null);
|
||||
const {
|
||||
sizes: {
|
||||
|
|
@ -55,7 +58,7 @@ const Calendar: FC<CalendarProps> = ({ toggleCalendar }) => {
|
|||
},
|
||||
} = useTheme();
|
||||
const calendarTransition = useTaskbarItemTransition(maxHeight, false);
|
||||
const finePointer = hasFinePointer();
|
||||
const finePointer = useMemo(() => hasFinePointer(), []);
|
||||
|
||||
useEffect(() => {
|
||||
calendarRef.current?.addEventListener("blur", ({ relatedTarget }) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { basename, dirname, extname } from "path";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { basename, dirname } from "path";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import type Stats from "browserfs/dist/node/core/node_fs_stats";
|
||||
import { getModifiedTime } from "components/system/Files/FileEntry/functions";
|
||||
import { UNKNOWN_ICON } from "components/system/Files/FileManager/icons";
|
||||
|
|
@ -20,7 +20,7 @@ import { useSession } from "contexts/session";
|
|||
import Button from "styles/common/Button";
|
||||
import Icon from "styles/common/Icon";
|
||||
import { DEFAULT_LOCALE, ROOT_NAME, SHORTCUT_EXTENSION } from "utils/constants";
|
||||
import { isYouTubeUrl } from "utils/functions";
|
||||
import { getExtension, isYouTubeUrl } from "utils/functions";
|
||||
import SubIcons from "components/system/Files/FileEntry/SubIcons";
|
||||
|
||||
const Details: FC<{
|
||||
|
|
@ -34,28 +34,46 @@ const Details: FC<{
|
|||
const [info, setInfo] = useState<ResultInfo>({
|
||||
icon: UNKNOWN_ICON,
|
||||
} as ResultInfo);
|
||||
const extension = extname(info?.url || url);
|
||||
const extension = useMemo(
|
||||
() => getExtension(info?.url || url),
|
||||
[info?.url, url]
|
||||
);
|
||||
const { updateRecentFiles } = useSession();
|
||||
const openFile = useCallback(() => {
|
||||
openApp(info?.pid, { url: info?.url });
|
||||
if (info?.url && info?.pid) updateRecentFiles(info?.url, info?.pid);
|
||||
}, [info?.pid, info?.url, openApp, updateRecentFiles]);
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
const isYTUrl = info?.url ? isYouTubeUrl(info.url) : false;
|
||||
const isNostrUrl = info?.url ? info.url.startsWith("nostr:") : false;
|
||||
const isAppShortcut = info?.pid
|
||||
? url === info.url && extname(url) === SHORTCUT_EXTENSION
|
||||
: false;
|
||||
const isDirectory =
|
||||
stats?.isDirectory() || (!extension && !isYTUrl && !isNostrUrl);
|
||||
const isYTUrl = useMemo(
|
||||
() => (info?.url ? isYouTubeUrl(info.url) : false),
|
||||
[info?.url]
|
||||
);
|
||||
const isNostrUrl = useMemo(
|
||||
() => (info?.url ? info.url.startsWith("nostr:") : false),
|
||||
[info?.url]
|
||||
);
|
||||
const isAppShortcut = useMemo(
|
||||
() =>
|
||||
info?.pid
|
||||
? url === info.url && getExtension(url) === SHORTCUT_EXTENSION
|
||||
: false,
|
||||
[info?.pid, info?.url, url]
|
||||
);
|
||||
const isDirectory = useMemo(
|
||||
() => stats?.isDirectory() || (!extension && !isYTUrl && !isNostrUrl),
|
||||
[extension, isNostrUrl, isYTUrl, stats]
|
||||
);
|
||||
const baseUrl = isYTUrl || isNostrUrl ? url : info?.url;
|
||||
const currentUrlRef = useRef(url);
|
||||
const name =
|
||||
baseUrl === "/"
|
||||
? ROOT_NAME
|
||||
: baseUrl
|
||||
? basename(baseUrl, SHORTCUT_EXTENSION)
|
||||
: "";
|
||||
const name = useMemo(
|
||||
() =>
|
||||
baseUrl === "/"
|
||||
? ROOT_NAME
|
||||
: baseUrl
|
||||
? basename(baseUrl, SHORTCUT_EXTENSION)
|
||||
: "",
|
||||
[baseUrl]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
stat(url).then(
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { type ProcessArguments } from "contexts/process/types";
|
|||
import { useSession } from "contexts/session";
|
||||
import Icon from "styles/common/Icon";
|
||||
import { DEFAULT_LOCALE, SHORTCUT_EXTENSION } from "utils/constants";
|
||||
import { isYouTubeUrl } from "utils/functions";
|
||||
import { getExtension, isYouTubeUrl } from "utils/functions";
|
||||
import { useIsVisible } from "hooks/useIsVisible";
|
||||
import SubIcons from "components/system/Files/FileEntry/SubIcons";
|
||||
|
||||
|
|
@ -47,8 +47,11 @@ const ResultEntry: FC<ResultEntryProps> = ({
|
|||
const { updateRecentFiles } = useSession();
|
||||
const [stats, setStats] = useState<Stats>();
|
||||
const [info, setInfo] = useState<ResultInfo>(INITIAL_INFO);
|
||||
const extension = extname(info?.url || url);
|
||||
const baseName = basename(url, SHORTCUT_EXTENSION);
|
||||
const extension = useMemo(
|
||||
() => getExtension(info?.url || url),
|
||||
[info?.url, url]
|
||||
);
|
||||
const baseName = useMemo(() => basename(url, SHORTCUT_EXTENSION), [url]);
|
||||
const name = useMemo(() => {
|
||||
let text = baseName;
|
||||
|
||||
|
|
@ -63,7 +66,10 @@ const ResultEntry: FC<ResultEntryProps> = ({
|
|||
|
||||
return text;
|
||||
}, [baseName, searchTerm]);
|
||||
const isYTUrl = info?.url ? isYouTubeUrl(info.url) : false;
|
||||
const isYTUrl = useMemo(
|
||||
() => (info?.url ? isYouTubeUrl(info.url) : false),
|
||||
[info?.url]
|
||||
);
|
||||
const baseUrl = isYTUrl ? url : url || info?.url;
|
||||
const lastModified = useMemo(
|
||||
() =>
|
||||
|
|
@ -80,11 +86,21 @@ const ResultEntry: FC<ResultEntryProps> = ({
|
|||
const [hovered, setHovered] = useState(false);
|
||||
const elementRef = useRef<HTMLLIElement | null>(null);
|
||||
const isVisible = useIsVisible(elementRef, ".list");
|
||||
const isAppShortcut = info?.pid
|
||||
? url === info.url && extname(url) === SHORTCUT_EXTENSION
|
||||
: false;
|
||||
const isDirectory = stats?.isDirectory() || (!extension && !isYTUrl);
|
||||
const isNostrUrl = info?.url ? info.url.startsWith("nostr:") : false;
|
||||
const isAppShortcut = useMemo(
|
||||
() =>
|
||||
info?.pid
|
||||
? url === info.url && getExtension(url) === SHORTCUT_EXTENSION
|
||||
: false,
|
||||
[info?.pid, info?.url, url]
|
||||
);
|
||||
const isDirectory = useMemo(
|
||||
() => stats?.isDirectory() || (!extension && !isYTUrl),
|
||||
[extension, isYTUrl, stats]
|
||||
);
|
||||
const isNostrUrl = useMemo(
|
||||
() => (info?.url ? info.url.startsWith("nostr:") : false),
|
||||
[info?.url]
|
||||
);
|
||||
const { onContextMenuCapture } = useResultsContextMenu(info?.url);
|
||||
const abortController = useRef<AbortController>(undefined);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
|
|
@ -49,11 +50,11 @@ const PeekWindow: FC<PeekWindowProps> = ({ id }) => {
|
|||
);
|
||||
const peekTransition = usePeekTransition(showControls);
|
||||
const peekRef = useRef<HTMLDivElement | null>(null);
|
||||
const onClick = (): void => {
|
||||
const onClick = useCallback((): void => {
|
||||
if (minimized) minimize(id);
|
||||
|
||||
setForegroundId(id);
|
||||
};
|
||||
}, [id, minimize, minimized, setForegroundId]);
|
||||
const [isPaused, setIsPaused] = useState(false);
|
||||
const monitoringPaused = useRef(false);
|
||||
|
||||
|
|
|
|||
|
|
@ -39,13 +39,13 @@ const TaskbarEntry: FC<TaskbarEntryProps> = ({ icon, id, title }) => {
|
|||
[id, linkElement]
|
||||
);
|
||||
const [isPeekVisible, setIsPeekVisible] = useState(false);
|
||||
const hidePeek = (): void => setIsPeekVisible(false);
|
||||
const showPeek = (): void => setIsPeekVisible(true);
|
||||
const onClick = (): void => {
|
||||
const hidePeek = useCallback((): void => setIsPeekVisible(false), []);
|
||||
const showPeek = useCallback((): void => setIsPeekVisible(true), []);
|
||||
const onClick = useCallback((): void => {
|
||||
if (minimized || isForeground) minimize(id);
|
||||
|
||||
setForegroundId(isForeground ? nextFocusableId : id);
|
||||
};
|
||||
}, [id, isForeground, minimize, minimized, nextFocusableId, setForegroundId]);
|
||||
const focusable = useMemo(() => (isSafari() ? DIV_BUTTON_PROPS : {}), []);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { type MotionProps } from "motion/react";
|
||||
import { useMemo } from "react";
|
||||
import { TASKBAR_HEIGHT, TRANSITIONS_IN_SECONDS } from "utils/constants";
|
||||
import { viewHeight } from "utils/functions";
|
||||
|
||||
|
|
@ -8,7 +9,10 @@ const useTaskbarItemTransition = (
|
|||
paddingOffset = 0.5,
|
||||
heightOffset = 0.75
|
||||
): MotionProps => {
|
||||
const height = Math.min(maxHeight, viewHeight() - TASKBAR_HEIGHT);
|
||||
const height = useMemo(
|
||||
() => Math.min(maxHeight, viewHeight() - TASKBAR_HEIGHT),
|
||||
[maxHeight]
|
||||
);
|
||||
|
||||
return {
|
||||
animate: "active",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
import { useMemo } from "react";
|
||||
import { useProcesses } from "contexts/process";
|
||||
import { useSession } from "contexts/session";
|
||||
|
||||
const useNextFocusable = (id: string): string => {
|
||||
const { stackOrder = [] } = useSession();
|
||||
const { processes } = useProcesses();
|
||||
const nextFocusableId = stackOrder.find(
|
||||
(stackId) => stackId !== id && !processes?.[stackId]?.minimized
|
||||
const nextFocusableId = useMemo(
|
||||
() =>
|
||||
stackOrder.find(
|
||||
(stackId) => stackId !== id && !processes?.[stackId]?.minimized
|
||||
),
|
||||
[id, processes, stackOrder]
|
||||
);
|
||||
|
||||
return nextFocusableId || "";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback } from "react";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useProcesses } from "contexts/process";
|
||||
import processDirectory from "contexts/process/directory";
|
||||
import { PROCESS_DELIMITER, SAVE_TITLE_CHAR } from "utils/constants";
|
||||
|
|
@ -14,7 +14,7 @@ type Title = {
|
|||
|
||||
const useTitle = (id: string): Title => {
|
||||
const { title } = useProcesses();
|
||||
const [pid] = id.split(PROCESS_DELIMITER);
|
||||
const [pid] = useMemo(() => id.split(PROCESS_DELIMITER), [id]);
|
||||
const { title: originalTitle } = processDirectory[pid] || {};
|
||||
const appendFileToTitle = useCallback(
|
||||
(url: string, unSaved?: boolean) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
type FullscreenDocument,
|
||||
type FullscreenElement,
|
||||
|
|
@ -63,23 +63,26 @@ const useViewportContextState = (): ViewportContextState => {
|
|||
// eslint-disable-next-line unicorn/no-null
|
||||
null
|
||||
);
|
||||
const toggleFullscreen = async (
|
||||
element?: HTMLElement | null,
|
||||
navigationUI?: FullscreenNavigationUI
|
||||
): Promise<void> => {
|
||||
if (fullscreenElement && (!element || element === fullscreenElement)) {
|
||||
await exitFullscreen();
|
||||
} else {
|
||||
// Only Chrome switches full screen elements without exiting
|
||||
if (fullscreenElement && (isFirefox() || isSafari())) {
|
||||
const toggleFullscreen = useCallback(
|
||||
async (
|
||||
element?: HTMLElement | null,
|
||||
navigationUI?: FullscreenNavigationUI
|
||||
): Promise<void> => {
|
||||
if (fullscreenElement && (!element || element === fullscreenElement)) {
|
||||
await exitFullscreen();
|
||||
}
|
||||
} else {
|
||||
// Only Chrome switches full screen elements without exiting
|
||||
if (fullscreenElement && (isFirefox() || isSafari())) {
|
||||
await exitFullscreen();
|
||||
}
|
||||
|
||||
await enterFullscreen(element || document.documentElement, {
|
||||
navigationUI: navigationUI || "hide",
|
||||
});
|
||||
}
|
||||
};
|
||||
await enterFullscreen(element || document.documentElement, {
|
||||
navigationUI: navigationUI || "hide",
|
||||
});
|
||||
}
|
||||
},
|
||||
[fullscreenElement]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const onFullscreenChange = (): void => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { ProcessProvider } from "contexts/process";
|
|||
import { SessionProvider } from "contexts/session";
|
||||
import { ViewportProvider } from "contexts/viewport";
|
||||
|
||||
const App = ({ Component, pageProps }: AppProps): React.ReactElement => (
|
||||
const App = ({ Component: Index, pageProps }: AppProps): React.ReactElement => (
|
||||
<ViewportProvider>
|
||||
<ProcessProvider>
|
||||
<FileSystemProvider>
|
||||
|
|
@ -17,7 +17,7 @@ const App = ({ Component, pageProps }: AppProps): React.ReactElement => (
|
|||
<Metadata />
|
||||
<StyledApp>
|
||||
<MenuProvider>
|
||||
<Component {...pageProps} />
|
||||
<Index {...pageProps} />
|
||||
</MenuProvider>
|
||||
</StyledApp>
|
||||
</ErrorBoundary>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user