From afc8b038c841896d4ea955b168b44588b3b97844 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Wed, 25 Jun 2025 15:14:08 -0700 Subject: [PATCH] Sketch of server rendered table --- .../app/(modern)/table/Table.tsx | 161 ++++++++++++++++++ .../skin-database/app/(modern)/table/page.tsx | 17 ++ 2 files changed, 178 insertions(+) create mode 100644 packages/skin-database/app/(modern)/table/Table.tsx create mode 100644 packages/skin-database/app/(modern)/table/page.tsx diff --git a/packages/skin-database/app/(modern)/table/Table.tsx b/packages/skin-database/app/(modern)/table/Table.tsx new file mode 100644 index 00000000..6e4ef60e --- /dev/null +++ b/packages/skin-database/app/(modern)/table/Table.tsx @@ -0,0 +1,161 @@ +"use client"; +import { + HEADING_HEIGHT, + SCREENSHOT_WIDTH, + SKIN_RATIO, +} from "../../../legacy-client/src/constants.js"; +import { + useScrollbarWidth, + useWindowSize, +} from "../../../legacy-client/src/hooks.js"; +import React, { useEffect, useMemo, useState } from "react"; +import { FixedSizeGrid as Grid } from "react-window"; + +function ClientOnly({ children }: { children: React.ReactNode }) { + const [mounted, setMounted] = useState(false); + useEffect(() => setMounted(true), []); + return mounted ? <>{children} : null; +} + +export default function WrappedTable({ initialSkins, skinCount }) { + return ( + + + + ); +} + +function Table({ initialSkins, skinCount }) { + const [skins, setSkins] = useState(initialSkins); + + const scale = 0.5; // This can be adjusted based on your needs + function getSkinData(data: { + columnIndex: number; + rowIndex: number; + columnCount: number; + }) { + const index = data.rowIndex * columnCount + data.columnIndex; + const skin = skins[index]; + return { requestToken: skin?.md5, skin }; + } + const scrollbarWidth = useScrollbarWidth(); + const { windowWidth: windowWidthWithScrollabar, windowHeight } = + useWindowSize(); + + const { columnWidth, rowHeight, columnCount } = getTableDimensions( + windowWidthWithScrollabar - scrollbarWidth, + scale + ); + function Cell(props) { + const index = props.rowIndex * columnCount + props.columnIndex; + const skin = skins[index]; + if (skin == null) { + if (index < skinCount) { + // Fetch more skins! + } + return
; + } + if (skin == null) { + return
Loading...
; + } + const imageUrl = `https://r2.webampskins.org/screenshots/${skin.md5}.png`; + return ( +
+ +
+ ); + } + return ( +
+ +
+ ); +} +const getTableDimensions = (windowWidth: number, scale: number) => { + const columnCount = Math.round(windowWidth / (SCREENSHOT_WIDTH * scale)); + const columnWidth = windowWidth / columnCount; // TODO: Consider flooring this to get things aligned to the pixel + const rowHeight = columnWidth * SKIN_RATIO; + return { columnWidth, rowHeight, columnCount }; +}; + +function SkinTableUnbound({ + columnCount, + columnWidth, + rowHeight, + windowHeight, + skinCount, + windowWidth, + getSkinData, + Cell, +}) { + function itemKey({ columnIndex, rowIndex }) { + const { requestToken, data: skin } = getSkinData({ + columnIndex, + rowIndex, + columnCount, + }); + if (skin == null && requestToken == null) { + return `empty-cell-${columnIndex}-${rowIndex}`; + } + return skin ? skin.hash : `unfectched-index-${requestToken}`; + } + const gridRef = React.useRef(); + const itemRef = React.useRef(); + React.useLayoutEffect(() => { + if (gridRef.current == null) { + return; + } + gridRef.current.scrollTo({ scrollLeft: 0, scrollTop: 0 }); + }, [skinCount]); + + React.useLayoutEffect(() => { + if (gridRef.current == null) { + return; + } + + const itemRow = Math.floor(itemRef.current / columnCount); + + gridRef.current.scrollTo({ scrollLeft: 0, scrollTop: rowHeight * itemRow }); + }, [rowHeight, columnCount]); + + const onScroll = useMemo(() => { + const half = Math.round(columnCount / 2); + return (scrollData) => { + itemRef.current = + Math.round(scrollData.scrollTop / rowHeight) * columnCount + half; + }; + }, [columnCount, rowHeight]); + + return ( +
+ + {Cell} + +
+ ); +} diff --git a/packages/skin-database/app/(modern)/table/page.tsx b/packages/skin-database/app/(modern)/table/page.tsx new file mode 100644 index 00000000..d6339102 --- /dev/null +++ b/packages/skin-database/app/(modern)/table/page.tsx @@ -0,0 +1,17 @@ +import * as Skins from "../../../data/skins"; +import Table from "./Table"; + +export default async function TablePage() { + const skins = await Skins.getMuseumPage({ + offset: 0, + first: 100, + }); + + const skinCount = await Skins.getClassicSkinCount(); + + return ( +
+
+ + ); +}