From f8e3e20d9da6c898b58631cb5b58c34d13d4f347 Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Thu, 25 Jul 2024 10:02:15 +0200 Subject: [PATCH] refactor: rework assets fetching --- .gitignore | 2 +- build/manifestPlugin.ts | 47 ++++++---- src/components/Music/Cava.tsx | 3 +- src/components/Nvim/NvimEditor.tsx | 42 +-------- src/components/Nvim/NvimStatusBar.tsx | 17 ++-- .../Nvim/NvimTree/NvimTreeChild.tsx | 34 +++++++ .../Nvim/NvimTree/NvimTreeDirectory.tsx | 6 +- src/components/Nvim/NvimTree/NvimTreeFile.tsx | 32 ------- src/components/Nvim/NvimTree/index.tsx | 93 +++++++------------ src/components/Nvim/index.tsx | 31 +++---- src/context/AppContext.tsx | 2 - src/providers/AppProvider.tsx | 45 +-------- src/utils/icons.ts | 40 ++++---- src/utils/tree.ts | 90 ++++++++++++++++++ src/utils/types.ts | 36 ------- 15 files changed, 239 insertions(+), 281 deletions(-) create mode 100644 src/components/Nvim/NvimTree/NvimTreeChild.tsx delete mode 100644 src/components/Nvim/NvimTree/NvimTreeFile.tsx create mode 100644 src/utils/tree.ts diff --git a/.gitignore b/.gitignore index ae4f4a4..156e19c 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,4 @@ yarn-error.log* .notes # Generated -/src/manifest.ts +/src/assets.ts diff --git a/build/manifestPlugin.ts b/build/manifestPlugin.ts index a4bc475..963961b 100644 --- a/build/manifestPlugin.ts +++ b/build/manifestPlugin.ts @@ -4,6 +4,24 @@ import { spawnSync } from "child_process"; import { Octokit } from "@octokit/rest"; import { env } from "./env"; +type Manifest = { + files: string[]; + projects: string[]; + links: { + name: string; + url: string; + icon: string; + }; +}; + +type Project = { + name: string; + content: string; + language: string | null; + url: string; + private: boolean; +}; + export const manifest = (): Plugin => ({ name: "generate-pages-plugin", buildStart: async () => { @@ -15,12 +33,12 @@ export const manifest = (): Plugin => ({ }); try { const storedUpdatedAt = ( - await readFile("./node_modules/.cache/manifest") + await readFile("./node_modules/.cache/assets") ).toString(); if (storedUpdatedAt === manifestRepo.updated_at) return; } catch {} - await writeFile("./node_modules/.cache/manifest", manifestRepo.updated_at); + await writeFile("./node_modules/.cache/assets", manifestRepo.updated_at); const getRepoFileContent = async (repo: string, path: string) => { const { data: file } = await octokit.repos.getContent({ @@ -36,18 +54,9 @@ export const manifest = (): Plugin => ({ const manifest = JSON.parse( await getRepoFileContent(env.GITHUB_USERNAME, "manifest.json"), - ) as { - files: string[]; - projects: string[]; - }; + ) as Manifest; - const projects: Array<{ - name: string; - content: string; - language: string | null; - url: string; - private: boolean; - }> = []; + const projects: Array = []; for (const project of manifest.projects) { const { data: repo } = await octokit.repos.get({ owner: env.GITHUB_USERNAME, @@ -67,18 +76,16 @@ export const manifest = (): Plugin => ({ const code = ` const projects = ${JSON.stringify(projects, null, 2)} as const; - export type Project = typeof projects[number]; + const links = ${JSON.stringify(manifest.links, null, 2)} as const; - const projectsMap = Object.fromEntries(projects.map(project => [project.name, project])) as const; - - export const manifest = { + export const assets = { projects, - projectsMap + links }; `; - await writeFile("./src/manifest.ts", code); + await writeFile("./src/assets.ts", code); - spawnSync("prettier", ["--write", "./src/manifest.ts"]); + spawnSync("prettier", ["--write", "./src/assets.ts"]); }, }); diff --git a/src/components/Music/Cava.tsx b/src/components/Music/Cava.tsx index fa2d444..f2878ef 100644 --- a/src/components/Music/Cava.tsx +++ b/src/components/Music/Cava.tsx @@ -116,7 +116,6 @@ const InnerCava = (props: InnerKittyProps) => { const analyser = audioContext.createAnalyser(); analyser.fftSize = 256; - console.log("ok"); void audioElement.play().then(() => void audioContext.resume()); if (!sourceRef.current) { @@ -132,7 +131,7 @@ const InnerCava = (props: InnerKittyProps) => { return () => { if (requestRef.current) cancelAnimationFrame(requestRef.current); }; - }, [props.cols, props.audio]); + }, [props.cols, props.audio, calculateBarHeights]); return barHeights.map((value, i) => ( diff --git a/src/components/Nvim/NvimEditor.tsx b/src/components/Nvim/NvimEditor.tsx index f3a6f70..b362603 100644 --- a/src/components/Nvim/NvimEditor.tsx +++ b/src/components/Nvim/NvimEditor.tsx @@ -1,41 +1,9 @@ -import axios from "axios"; -import { useEffect, useState } from "react"; - -export const NvimEditor = (props: { source: string | undefined }) => { - const [cache, setCache] = useState(new Map()); - const [data, setData] = useState(); - const [loading, setLoading] = useState(false); +import { useState } from "react"; +export const NvimEditor = (props: { content: string | undefined }) => { const [selectedLine, setSelectedLine] = useState(0); - useEffect(() => { - if (!props.source) return; - - const cached = cache.get(props.source); - if (cached) { - console.log("cache hit"); - setData(cached); - return; - } - - setLoading(true); - axios - .get(props.source) - .then(({ data }) => { - setData(data); - setLoading(false); - - setCache((cache) => { - cache.set(props.source!, data); - return cache; - }); - }) - .catch(() => { - setLoading(false); - }); - }, [props.source]); - - let rows = data?.split("\n") ?? []; + let rows = props.content?.split("\n") ?? []; // trim end empty lines for (let i = rows.length - 1; i >= 0; i--) { if (rows[i].trim().length === 0) { @@ -51,9 +19,7 @@ export const NvimEditor = (props: { source: string | undefined }) => { } } - return loading ? ( -

Loading...

- ) : ( + return ( {rows.map((row, i) => ( diff --git a/src/components/Nvim/NvimStatusBar.tsx b/src/components/Nvim/NvimStatusBar.tsx index 40a456f..b3bf110 100644 --- a/src/components/Nvim/NvimStatusBar.tsx +++ b/src/components/Nvim/NvimStatusBar.tsx @@ -1,10 +1,11 @@ -import { ICONS, getIcon } from "~/utils/icons"; +import { DEFAULT_ICON } from "~/utils/icons"; +import { type Icon } from "~/utils/tree"; export const NvimStatusBar = (props: { label: string; labelColor: string; - fileIcon?: string; - fileName: string; + fileIcon?: Icon; + fileName?: string; }) => (
@@ -15,14 +16,8 @@ export const NvimStatusBar = (props: { {"\ue0ba"} {` ${ - getIcon({ - type: "file", - name: props.fileName, - icon: props.fileIcon, - fileName: props.fileName, - repo: "", - }).char - }${props.fileName} `} + props.fileIcon?.char ?? DEFAULT_ICON.char + }${props.fileName ?? "Empty"} `} {"\ue0ba"}
); diff --git a/src/components/Nvim/NvimTree/NvimTreeChild.tsx b/src/components/Nvim/NvimTree/NvimTreeChild.tsx new file mode 100644 index 0000000..8077b72 --- /dev/null +++ b/src/components/Nvim/NvimTree/NvimTreeChild.tsx @@ -0,0 +1,34 @@ +import { DEFAULT_ICON } from "~/utils/icons"; +import { type Child } from "~/utils/tree"; + +export const NvimTreeChild = (props: { + child: Child; + y: number; + selected: boolean; + inDirectory: boolean | "last"; + onSelect: (y: number) => void; + onOpen: (file: Child) => void; +}) => { + const icon = props.child.icon ?? DEFAULT_ICON; + + return ( +
  • props.onSelect(props.y)} + onDoubleClick={() => props.onOpen(props.child)} + > + {" "} + {props.inDirectory && ( + + {props.inDirectory === "last" ? "└ " : "│ "} + + )} + {`${icon.char}`} + {props.child.name === "README.md" ? ( + README.md + ) : ( + {props.child.name} + )} +
  • + ); +}; diff --git a/src/components/Nvim/NvimTree/NvimTreeDirectory.tsx b/src/components/Nvim/NvimTree/NvimTreeDirectory.tsx index 2461303..d7196e3 100644 --- a/src/components/Nvim/NvimTree/NvimTreeDirectory.tsx +++ b/src/components/Nvim/NvimTree/NvimTreeDirectory.tsx @@ -1,11 +1,11 @@ -import { Directory } from "~/utils/types"; +import { type Folder } from "~/utils/tree"; export const NvimTreeDirectory = (props: { - directory: Directory; + directory: Folder; y: number; selected: boolean; onSelect: (y: number) => void; - onOpen: (directory: Directory) => void; + onOpen: (directory: Folder) => void; }) => (
  • void; - onOpen: (file: File) => void; -}) => ( -
  • props.onSelect(props.y)} - onDoubleClick={() => props.onOpen(props.file)} - > - {" "} - {props.inDirectory && ( - - {props.inDirectory === "last" ? "└ " : "│ "} - - )} - {`${ - getIcon(props.file).char - }`} - {props.file.name === "README.md" ? ( - README.md - ) : ( - {props.file.name} - )} -
  • -); diff --git a/src/components/Nvim/NvimTree/index.tsx b/src/components/Nvim/NvimTree/index.tsx index 01c6507..0f86fce 100644 --- a/src/components/Nvim/NvimTree/index.tsx +++ b/src/components/Nvim/NvimTree/index.tsx @@ -1,65 +1,44 @@ import { useApp } from "~/hooks/useApp"; import { CHAR_HEIGHT, CHAR_WIDTH } from "../../Kitty"; import { type ReactNode, useEffect, useState } from "react"; -import { - type File, - type InnerKittyProps, - type RootManifest, - type Directory, -} from "~/utils/types"; +import { type InnerKittyProps } from "~/utils/types"; import { type Nvim } from ".."; import { NvimTreeDirectory } from "./NvimTreeDirectory"; -import { NvimTreeFile } from "./NvimTreeFile"; +import { NvimTreeChild } from "./NvimTreeChild"; +import { assets } from "~/assets"; +import { getIcon } from "~/utils/icons"; +import { + file, + folder, + project, + sortFiles, + link, + type Child, +} from "~/utils/tree"; -const sortFiles = (files: Array) => - files - .sort((a, b) => a.name.localeCompare(b.name)) - .sort((a, b) => - a.type === "directory" && b.type !== "directory" - ? -1 - : a.type !== "directory" && b.type === "directory" - ? 1 - : 0, - ); - -const manifestToTree = (manifest: RootManifest) => +const buildTree = () => sortFiles([ - { - type: "directory", - name: "links", - opened: false, - files: manifest.links.map((link) => ({ - type: "link" as const, - ...link, - })), - }, - { - type: "directory", - name: "projects", - opened: false, - files: manifest.projects.map((project) => ({ - type: "file" as const, - repo: project.name, - fileName: "README.md", - ...project, - })), - }, - ...manifest.files.map((file) => ({ - type: "file" as const, - name: file, - repo: "pihkaal", - fileName: file, - })), + folder( + "links", + assets.links.map((l) => link(l.name, l.url, l.icon)), + ), + folder( + "projects", + assets.projects.map((p) => + project(p.name, p.content, p.url, p.language, p.private), + ), + ), + file("README.md", "hey", getIcon("README.md")), ]); export const NvimTree = ( props: InnerKittyProps & { - onOpen: (file: File) => void; + onOpen: (file: Child) => void; }, ) => { - const { rootManifest, activeKitty } = useApp(); + const { activeKitty } = useApp(); - const [files, setFiles] = useState(manifestToTree(rootManifest)); + const [files, setFiles] = useState(buildTree()); const [selectedY, setSelectedY] = useState(0); const tree: Array = []; @@ -67,7 +46,7 @@ export const NvimTree = ( let selectedFile = files[0]; for (const file of files) { if (selectedY === y) selectedFile = file; - if (file.type === "directory") { + if (file.type === "folder") { tree.push( { + file.children.forEach((child, i) => { y++; - if (selectedY === y) selectedFile = childFile; + if (selectedY === y) selectedFile = child; tree.push( - { +export const Nvim = (_props: unknown) => { const kitty = useKitty(); return kitty && ; }; const InnerNvimTree = (props: InnerKittyProps) => { - const [activeFile, setActiveFile] = useState<{ + const [activeChild, setActiveChild] = useState<{ name: string; - url: string; - icon?: string; + content: string; + icon: Icon; }>(); - const handleOpenFile = (file: File) => { - if (file.type === "link") { - window.open(file.url, "_blank")?.focus(); + const handleOpenChild = (child: Child) => { + if (child.type === "link") { + window.open(child.url, "_blank")?.focus(); } else { - setActiveFile({ - name: file.repo === "pihkaal" ? file.fileName : file.repo, - icon: file.icon, - url: `https://raw.githubusercontent.com/pihkaal/${file.repo}/main/${file.fileName}`, - }); + setActiveChild(child); } }; @@ -43,7 +40,7 @@ const InnerNvimTree = (props: InnerKittyProps) => { }} >
    - +
    ) => { wordWrap: "break-word", }} > - +
    diff --git a/src/context/AppContext.tsx b/src/context/AppContext.tsx index 8b08632..84f8c51 100644 --- a/src/context/AppContext.tsx +++ b/src/context/AppContext.tsx @@ -1,9 +1,7 @@ import { createContext } from "react"; -import { type RootManifest } from "~/utils/types"; export const AppContext = createContext< | { - rootManifest: RootManifest; activeKitty: string; setActiveKitty: (value: string) => void; } diff --git a/src/providers/AppProvider.tsx b/src/providers/AppProvider.tsx index 0ea0a98..b9cc068 100644 --- a/src/providers/AppProvider.tsx +++ b/src/providers/AppProvider.tsx @@ -1,50 +1,11 @@ -import axios from "axios"; -import { type ReactNode, useState, useEffect } from "react"; +import { type ReactNode, useState } from "react"; import { AppContext } from "~/context/AppContext"; -import { type RootManifest } from "~/utils/types"; export const AppProvider = (props: { children?: ReactNode }) => { const [activeKitty, setActiveKitty] = useState(":r0:"); - const [rootManifest, setRootManifest] = useState({ - files: ["README.md", "pubkey.asc"], - projects: [ - { - name: "me", - icon: "ts", - }, - { - name: "tlock", - icon: "rs", - }, - ], - links: [ - { - name: "github", - url: "https://github.com/pihkaal", - icon: "github", - }, - { - name: "instagram", - url: "https://instagram.com/pihkaal", - icon: "instagram", - }, - ], - }); - - useEffect(() => { - return; - void axios - .get( - "https://raw.githubusercontent.com/pihkaal/pihkaal/main/manifest.json", - ) - .then((x) => setRootManifest(x.data)); - }, []); - - if (!rootManifest) return null; - return ( - - {rootManifest && props.children} + + {props.children} ); }; diff --git a/src/utils/icons.ts b/src/utils/icons.ts index 114215e..39b1b9d 100644 --- a/src/utils/icons.ts +++ b/src/utils/icons.ts @@ -1,48 +1,48 @@ -import { File, Icon } from "./types"; +import { type Child, type Icon } from "./tree"; -export const getIcon = (file: File | string | undefined): Icon => { - if (file === undefined) return ICONS["UNKNOWN"]; +export const getIcon = (file: Child | string | undefined): Icon => { + if (!file) return DEFAULT_ICON; - let iconName; if (typeof file === "string") { const parts = file.split("."); - iconName = parts[parts.length - 1]; - } else { - iconName = file.icon; - if (!iconName) { - const parts = file.name.split("."); - iconName = parts[parts.length - 1]; - } + const iconName = parts[parts.length - 1]; + + return ICONS[EXT_TO_LANGUAGE[iconName]] ?? DEFAULT_ICON; } - if (!ICONS[iconName]) iconName = "UNKNOWN"; - return ICONS[iconName]; + return file.icon ?? DEFAULT_ICON; +}; + +export const EXT_TO_LANGUAGE: Record = { + asc: "Key", + md: "Markdown", }; export const ICONS: Record = { - md: { + Markdown: { char: " ", color: "#89bafa", }, - asc: { + Key: { char: "󰷖 ", color: "#f9e2af", }, - ts: { + TypeScript: { char: " ", color: "#4d86a2", }, - rs: { + Rust: { char: " ", color: "#be8f78", }, - instagram: { + Instagram: { char: " ", color: "#e1306c", }, - github: { + Github: { char: "󰊤 ", color: "#ffffff", }, - UNKNOWN: { char: "󰈚 ", color: "#f599ae" }, }; + +export const DEFAULT_ICON = { char: "󰈚 ", color: "#f599ae" }; diff --git a/src/utils/tree.ts b/src/utils/tree.ts new file mode 100644 index 0000000..2d211fb --- /dev/null +++ b/src/utils/tree.ts @@ -0,0 +1,90 @@ +import { DEFAULT_ICON, ICONS } from "./icons"; + +export type Icon = { + char: string; + color: string; +}; + +export type Project = { + type: "project"; + name: string; + url: string; + private: boolean; + content: string; + icon: Icon; +}; + +export type File = { + type: "file"; + name: string; + content: string; + icon: Icon; +}; + +export type Link = { + type: "link"; + name: string; + url: string; + icon: Icon; +}; + +export type Folder = { + name: string; + type: "folder"; + children: Array; + opened: boolean; +}; + +export type Child = Link | Project | File; + +export const sortFiles = (files: Array) => + files + .sort((a, b) => a.name.localeCompare(b.name)) + .sort((a, b) => + a.type === "folder" && b.type !== "folder" + ? -1 + : a.type !== "folder" && b.type === "folder" + ? 1 + : 0, + ); + +export const folder = (name: string, children: Array): Folder => ({ + type: "folder", + name, + opened: false, + children, +}); + +export const link = (name: string, url: string, icon: Icon): Link => ({ + type: "link", + name, + url, + icon, +}); + +export const file = (name: string, content: string, icon: Icon): File => ({ + type: "file", + name, + content, + icon, +}); + +export const project = ( + name: string, + content: string, + url: string, + language: string, + priv: boolean, +): Project => ({ + type: "project", + name, + content, + url, + icon: ICONS[language] ?? DEFAULT_ICON, + private: priv, +}); + +export const icon = (char: string, color: string): Icon => ({ + char, + color, +}); diff --git a/src/utils/types.ts b/src/utils/types.ts index d758d4c..92455d8 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -6,39 +6,3 @@ export type Prettify = NonNullable<{ [K in keyof T]: T[K] }>; export type InnerKittyProps any> = Prettify< Parameters[0] & KittyContextProps >; - -export type RootManifest = { - files: Array; - projects: Array<{ - name: string; - icon: string; - }>; - links: Array<{ - name: string; - url: string; - icon: string; - }>; -}; - -export type Icon = { - char: string; - color: string; -}; - -export type File = { - name: string; -} & ( - | { - type: "link"; - url: string; - icon: string; - } - | { type: "file"; repo: string; fileName: string; icon?: string } -); - -export type Directory = { - name: string; - type: "directory"; - files: Array; - opened: boolean; -};