diff --git a/components/system/Taskbar/AI/AIButton.tsx b/components/system/Taskbar/AI/AIButton.tsx index cd091b15..7a31038b 100644 --- a/components/system/Taskbar/AI/AIButton.tsx +++ b/components/system/Taskbar/AI/AIButton.tsx @@ -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 = ({ aiVisible, toggleAI }) => { + const menuPreloadHandler = useMenuPreload(importAIChat); const { removeFromStack } = useSession(); return ( @@ -26,6 +29,7 @@ const AIButton: FC = ({ aiVisible, toggleAI }) => { {...DIV_BUTTON_PROPS} {...label(AI_DISPLAY_TITLE)} {...useTaskbarContextMenu()} + {...menuPreloadHandler} > diff --git a/components/system/Taskbar/Clock/index.tsx b/components/system/Taskbar/Clock/index.tsx index 5841d808..f84bbd76 100644 --- a/components/system/Taskbar/Clock/index.tsx +++ b/components/system/Taskbar/Clock/index.tsx @@ -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 = ({ }, [toggleCalendar] ); + const menuPreloadHandler = useMenuPreload(importCalendar); useEffect(() => { offScreenClockCanvas.current = undefined; @@ -212,6 +215,7 @@ const Clock: FC = ({ suppressHydrationWarning {...clockContextMenu} {...FOCUSABLE_ELEMENT} + {...menuPreloadHandler} > {supportsOffscreenCanvas ? undefined : time} diff --git a/components/system/Taskbar/Search/SearchButton.tsx b/components/system/Taskbar/Search/SearchButton.tsx index df0855f2..163d58c6 100644 --- a/components/system/Taskbar/Search/SearchButton.tsx +++ b/components/system/Taskbar/Search/SearchButton.tsx @@ -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 = ({ {...DIV_BUTTON_PROPS} {...label(SEARCH_BUTTON_TITLE)} {...useTaskbarContextMenu()} + {...useMenuPreload(importSearch)} > diff --git a/components/system/Taskbar/Search/index.tsx b/components/system/Taskbar/Search/index.tsx index 53caa41d..f3cd8cb4 100644 --- a/components/system/Taskbar/Search/index.tsx +++ b/components/system/Taskbar/Search/index.tsx @@ -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; +const Details = dynamic( + () => import("components/system/Taskbar/Search/Details") +); +const ResultSection = dynamic( + () => import("components/system/Taskbar/Search/ResultSection") +); + const Search: FC = ({ toggleSearch }) => { const inputRef = useRef(null); const menuRef = useRef(null); diff --git a/components/system/Taskbar/StartButton/index.tsx b/components/system/Taskbar/StartButton/index.tsx index b3713b65..34a5d259 100644 --- a/components/system/Taskbar/StartButton/index.tsx +++ b/components/system/Taskbar/StartButton/index.tsx @@ -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 => { + const preloadedStartMenu = importStartMenu(); + const { default: startMenuIcons } = + (await import("public/.index/startMenuIcons.json")) || {}; + + startMenuIcons?.forEach((icon) => preloadImage(icon)); + + await preloadedStartMenu; +}; + const StartButton: FC = ({ startMenuVisible, toggleStartMenu, }) => { - const [preloaded, setPreloaded] = useState(false); - const initalizedPreload = useRef(false); - const preloadIcons = useCallback(async (): Promise => { - 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 => { - if (!preloaded) preloadIcons(); - toggleStartMenu(); if (ctrlKey && shiftKey) { @@ -40,18 +39,18 @@ const StartButton: FC = ({ spawnSheep(); } }, - [preloadIcons, preloaded, toggleStartMenu] + [toggleStartMenu] ); return ( diff --git a/components/system/Taskbar/functions.ts b/components/system/Taskbar/functions.ts index a411997a..61a03135 100644 --- a/components/system/Taskbar/functions.ts +++ b/components/system/Taskbar/functions.ts @@ -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, menuElement: HTMLElement | null, diff --git a/components/system/Taskbar/index.tsx b/components/system/Taskbar/index.tsx index 62f43202..dc464b73 100644 --- a/components/system/Taskbar/index.tsx +++ b/components/system/Taskbar/index.tsx @@ -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); diff --git a/hooks/useMenuPreload.ts b/hooks/useMenuPreload.ts new file mode 100644 index 00000000..a131b841 --- /dev/null +++ b/hooks/useMenuPreload.ts @@ -0,0 +1,19 @@ +import { useState, useRef, useCallback } from "react"; + +export const useMenuPreload = ( + preloadCallback: () => Promise +): { + onMouseOver?: React.MouseEventHandler; +} => { + 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 }; +};