Perf improvements to taskbar items loading

This commit is contained in:
Dustin Brett
2025-02-18 18:17:49 -08:00
parent 1ce2ecd737
commit b2f10c3bee
8 changed files with 81 additions and 27 deletions

View File

@@ -1,3 +1,4 @@
import { importAIChat } from "components/system/Taskbar/functions";
import {
AI_DISPLAY_TITLE,
WINDOW_ID,
@@ -8,6 +9,7 @@ import { DIV_BUTTON_PROPS } from "utils/constants";
import { label } from "utils/functions";
import useTaskbarContextMenu from "components/system/Taskbar/useTaskbarContextMenu";
import { useSession } from "contexts/session";
import { useMenuPreload } from "hooks/useMenuPreload";
type AIButtonProps = {
aiVisible: boolean;
@@ -15,6 +17,7 @@ type AIButtonProps = {
};
const AIButton: FC<AIButtonProps> = ({ aiVisible, toggleAI }) => {
const menuPreloadHandler = useMenuPreload(importAIChat);
const { removeFromStack } = useSession();
return (
@@ -26,6 +29,7 @@ const AIButton: FC<AIButtonProps> = ({ aiVisible, toggleAI }) => {
{...DIV_BUTTON_PROPS}
{...label(AI_DISPLAY_TITLE)}
{...useTaskbarContextMenu()}
{...menuPreloadHandler}
>
<AIIcon />
</StyledAIButton>

View File

@@ -1,5 +1,6 @@
import { useTheme } from "styled-components";
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { importCalendar } from "components/system/Taskbar/functions";
import { measureText } from "components/system/Files/FileEntry/functions";
import StyledClock from "components/system/Taskbar/Clock/StyledClock";
import { type LocaleTimeDate } from "components/system/Taskbar/Clock/functions";
@@ -14,6 +15,7 @@ import {
TASKBAR_HEIGHT,
} from "utils/constants";
import { createOffscreenCanvas } from "utils/functions";
import { useMenuPreload } from "hooks/useMenuPreload";
type ClockWorkerResponse = LocaleTimeDate | "source";
@@ -170,6 +172,7 @@ const Clock: FC<ClockProps> = ({
},
[toggleCalendar]
);
const menuPreloadHandler = useMenuPreload(importCalendar);
useEffect(() => {
offScreenClockCanvas.current = undefined;
@@ -212,6 +215,7 @@ const Clock: FC<ClockProps> = ({
suppressHydrationWarning
{...clockContextMenu}
{...FOCUSABLE_ELEMENT}
{...menuPreloadHandler}
>
{supportsOffscreenCanvas ? undefined : time}
</StyledClock>

View File

@@ -1,10 +1,14 @@
import { useTheme } from "styled-components";
import { Search as SearchIcon } from "components/system/Taskbar/Search/Icons";
import StyledTaskbarButton from "components/system/Taskbar/StyledTaskbarButton";
import { SEARCH_BUTTON_TITLE } from "components/system/Taskbar/functions";
import {
importSearch,
SEARCH_BUTTON_TITLE,
} from "components/system/Taskbar/functions";
import useTaskbarContextMenu from "components/system/Taskbar/useTaskbarContextMenu";
import { DIV_BUTTON_PROPS } from "utils/constants";
import { label } from "utils/functions";
import { useMenuPreload } from "hooks/useMenuPreload";
type StartButtonProps = {
searchVisible: boolean;
@@ -27,6 +31,7 @@ const SearchButton: FC<StartButtonProps> = ({
{...DIV_BUTTON_PROPS}
{...label(SEARCH_BUTTON_TITLE)}
{...useTaskbarContextMenu()}
{...useMenuPreload(importSearch)}
>
<SearchIcon />
</StyledTaskbarButton>

View File

@@ -2,6 +2,7 @@ import { basename, extname } from "path";
import { useTheme } from "styled-components";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { m as motion } from "motion/react";
import dynamic from "next/dynamic";
import { Search as SearchIcon } from "components/apps/FileExplorer/NavigationIcons";
import {
getCachedShortcut,
@@ -14,9 +15,7 @@ import {
Pictures,
Videos,
} from "components/system/StartMenu/Sidebar/SidebarIcons";
import Details from "components/system/Taskbar/Search/Details";
import { Games } from "components/system/Taskbar/Search/Icons";
import ResultSection from "components/system/Taskbar/Search/ResultSection";
import StyledFiles from "components/system/Taskbar/Search/StyledFiles";
import StyledResults from "components/system/Taskbar/Search/StyledResults";
import StyledSearch from "components/system/Taskbar/Search/StyledSearch";
@@ -95,6 +94,13 @@ const METADATA = {
},
} as Record<TabName, TabData>;
const Details = dynamic(
() => import("components/system/Taskbar/Search/Details")
);
const ResultSection = dynamic(
() => import("components/system/Taskbar/Search/ResultSection")
);
const Search: FC<SearchProps> = ({ toggleSearch }) => {
const inputRef = useRef<HTMLInputElement | null>(null);
const menuRef = useRef<HTMLElement | null>(null);

View File

@@ -1,37 +1,36 @@
import { useCallback, useRef, useState } from "react";
import { useCallback } from "react";
import StartButtonIcon from "components/system/Taskbar/StartButton/StartButtonIcon";
import StyledTaskbarButton from "components/system/Taskbar/StyledTaskbarButton";
import { START_BUTTON_TITLE } from "components/system/Taskbar/functions";
import {
importStartMenu,
START_BUTTON_TITLE,
} from "components/system/Taskbar/functions";
import useTaskbarContextMenu from "components/system/Taskbar/useTaskbarContextMenu";
import { DIV_BUTTON_PROPS } from "utils/constants";
import { label, preloadImage } from "utils/functions";
import { useMenuPreload } from "hooks/useMenuPreload";
type StartButtonProps = {
startMenuVisible: boolean;
toggleStartMenu: (showMenu?: boolean) => void;
};
const preloadStartMenu = async (): Promise<void> => {
const preloadedStartMenu = importStartMenu();
const { default: startMenuIcons } =
(await import("public/.index/startMenuIcons.json")) || {};
startMenuIcons?.forEach((icon) => preloadImage(icon));
await preloadedStartMenu;
};
const StartButton: FC<StartButtonProps> = ({
startMenuVisible,
toggleStartMenu,
}) => {
const [preloaded, setPreloaded] = useState(false);
const initalizedPreload = useRef(false);
const preloadIcons = useCallback(async (): Promise<void> => {
if (initalizedPreload.current) return;
initalizedPreload.current = true;
const startMenuIcons = (await import("public/.index/startMenuIcons.json"))
.default;
startMenuIcons?.forEach((icon) => preloadImage(icon));
setPreloaded(true);
}, []);
const onClick = useCallback(
async ({ ctrlKey, shiftKey }: React.MouseEvent): Promise<void> => {
if (!preloaded) preloadIcons();
toggleStartMenu();
if (ctrlKey && shiftKey) {
@@ -40,18 +39,18 @@ const StartButton: FC<StartButtonProps> = ({
spawnSheep();
}
},
[preloadIcons, preloaded, toggleStartMenu]
[toggleStartMenu]
);
return (
<StyledTaskbarButton
$active={startMenuVisible}
onClick={onClick}
onMouseOver={preloaded ? undefined : preloadIcons}
$highlight
{...DIV_BUTTON_PROPS}
{...label(START_BUTTON_TITLE)}
{...useTaskbarContextMenu(true)}
{...useMenuPreload(preloadStartMenu)}
>
<StartButtonIcon />
</StyledTaskbarButton>

View File

@@ -3,6 +3,16 @@ import { PREVENT_SCROLL } from "utils/constants";
export const START_BUTTON_TITLE = "Start";
export const SEARCH_BUTTON_TITLE = "Type here to search";
/* eslint-disable @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types */
export const importAIButton = () =>
import("components/system/Taskbar/AI/AIButton");
export const importAIChat = () => import("components/system/Taskbar/AI/AIChat");
export const importCalendar = () =>
import("components/system/Taskbar/Calendar");
export const importSearch = () => import("components/system/Taskbar/Search");
export const importStartMenu = () => import("components/system/StartMenu");
/* eslint-enable @typescript-eslint/explicit-function-return-type */
export const maybeCloseTaskbarMenu = (
{ relatedTarget: focusedElement }: React.FocusEvent<HTMLElement>,
menuElement: HTMLElement | null,

View File

@@ -1,6 +1,13 @@
import { memo, useCallback, useState } from "react";
import dynamic from "next/dynamic";
import { AnimatePresence } from "motion/react";
import {
importAIButton,
importAIChat,
importCalendar,
importSearch,
importStartMenu,
} from "components/system/Taskbar/functions";
import Clock from "components/system/Taskbar/Clock";
import SearchButton from "components/system/Taskbar/Search/SearchButton";
import StartButton from "components/system/Taskbar/StartButton";
@@ -11,11 +18,11 @@ import { CLOCK_CANVAS_BASE_WIDTH, FOCUSABLE_ELEMENT } from "utils/constants";
import { useWindowAI } from "hooks/useWindowAI";
import { useSession } from "contexts/session";
const AIButton = dynamic(() => import("components/system/Taskbar/AI/AIButton"));
const AIChat = dynamic(() => import("components/system/Taskbar/AI/AIChat"));
const Calendar = dynamic(() => import("components/system/Taskbar/Calendar"));
const Search = dynamic(() => import("components/system/Taskbar/Search"));
const StartMenu = dynamic(() => import("components/system/StartMenu"));
const AIButton = dynamic(importAIButton);
const AIChat = dynamic(importAIChat);
const Calendar = dynamic(importCalendar);
const Search = dynamic(importSearch);
const StartMenu = dynamic(importStartMenu);
const Taskbar: FC = () => {
const [startMenuVisible, setStartMenuVisible] = useState(false);

19
hooks/useMenuPreload.ts Normal file
View File

@@ -0,0 +1,19 @@
import { useState, useRef, useCallback } from "react";
export const useMenuPreload = (
preloadCallback: () => Promise<unknown>
): {
onMouseOver?: React.MouseEventHandler<HTMLButtonElement | HTMLDivElement>;
} => {
const [preloaded, setPreloaded] = useState(false);
const initalizedPreload = useRef(false);
const preloadMenu = useCallback((): void => {
if (initalizedPreload.current) return;
initalizedPreload.current = true;
preloadCallback().then(() => setPreloaded(true));
}, [preloadCallback]);
return preloaded ? {} : { onMouseOver: preloadMenu };
};