mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-17 16:46:04 +00:00
Stub out shorts
This commit is contained in:
parent
340e2249ae
commit
0705e9d89e
3 changed files with 169 additions and 0 deletions
124
packages/skin-database/app/(modern)/scroll/SkinScroller.tsx
Normal file
124
packages/skin-database/app/(modern)/scroll/SkinScroller.tsx
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useLayoutEffect } from "react";
|
||||
|
||||
export type ClientSkin = {
|
||||
screenshotUrl: string;
|
||||
fileName: string;
|
||||
md5: string;
|
||||
readmeStart: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
initialSkins: ClientSkin[];
|
||||
getSkins: (offset: number) => Promise<ClientSkin[]>;
|
||||
};
|
||||
|
||||
export default function SkinScroller({ initialSkins, getSkins }: Props) {
|
||||
const [skins, setSkins] = useState<ClientSkin[]>(initialSkins);
|
||||
const [visibleSkinIndex, setVisibleSkinIndex] = useState(0);
|
||||
const [fetching, setFetching] = useState(false);
|
||||
const [containerRef, setContainerRef] = useState<HTMLDivElement | null>(null);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (containerRef == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
function onSnap(e) {
|
||||
const md5 = e.snapTargetBlock.getAttribute("skin-md5");
|
||||
const index = parseInt(e.snapTargetBlock.getAttribute("skin-index"));
|
||||
setVisibleSkinIndex(index);
|
||||
}
|
||||
|
||||
containerRef.addEventListener("scrollsnapchange", onSnap);
|
||||
|
||||
return () => {
|
||||
containerRef.removeEventListener("scrollsnapchange", onSnap);
|
||||
};
|
||||
}, [containerRef]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (fetching) {
|
||||
return;
|
||||
}
|
||||
if (visibleSkinIndex + 5 >= skins.length) {
|
||||
setFetching(true);
|
||||
getSkins(skins.length).then((newSkins) => {
|
||||
setSkins([...skins, ...newSkins]);
|
||||
setFetching(false);
|
||||
});
|
||||
}
|
||||
}, [visibleSkinIndex, skins, fetching]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={setContainerRef}
|
||||
style={{
|
||||
height: "100vh",
|
||||
overflowY: "scroll",
|
||||
scrollSnapType: "y mandatory",
|
||||
}}
|
||||
>
|
||||
{skins.map((skin, i) => {
|
||||
return (
|
||||
<div
|
||||
key={skin.md5}
|
||||
skin-md5={skin.md5}
|
||||
skin-index={i}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
height: "100vh",
|
||||
scrollSnapAlign: "start",
|
||||
scrollSnapStop: "always",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={skin.screenshotUrl}
|
||||
alt={skin.fileName}
|
||||
style={{
|
||||
paddingTop: "4rem",
|
||||
boxSizing: "border-box",
|
||||
width: "100%",
|
||||
imageRendering: "pixelated",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
color: "white",
|
||||
flexGrow: 1,
|
||||
paddingLeft: "0.5rem",
|
||||
paddingTop: "0.5rem",
|
||||
}}
|
||||
>
|
||||
<h2
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
fontSize: "0.9rem",
|
||||
paddingBottom: "0",
|
||||
fontFamily: 'Arial, "Helvetica Neue", Helvetica, sans-serif',
|
||||
color: "#ccc",
|
||||
}}
|
||||
>
|
||||
{skin.fileName}
|
||||
</h2>
|
||||
<p
|
||||
style={{
|
||||
marginTop: "0.5rem",
|
||||
fontSize: "0.75rem",
|
||||
paddingTop: "0",
|
||||
color: "#999",
|
||||
fontFamily: 'monospace, "Courier New", Courier, monospace',
|
||||
}}
|
||||
>
|
||||
{skin.readmeStart}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
40
packages/skin-database/app/(modern)/scroll/page.tsx
Normal file
40
packages/skin-database/app/(modern)/scroll/page.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import ClassicSkinResolver from "../../../api/graphql/resolvers/ClassicSkinResolver";
|
||||
import SkinsConnection from "../../../api/graphql/SkinsConnection";
|
||||
import UserContext from "../../../data/UserContext";
|
||||
import "./scroll.css";
|
||||
import SkinScroller, { ClientSkin } from "./SkinScroller";
|
||||
|
||||
async function getClientSkins(offset: number): Promise<ClientSkin[]> {
|
||||
"use server";
|
||||
const ctx = new UserContext();
|
||||
const connection = new SkinsConnection(10, offset, "MUSEUM", null);
|
||||
const skins = await connection.nodes(ctx);
|
||||
if (skins == null) {
|
||||
return [];
|
||||
}
|
||||
const classicSkins = skins.filter(
|
||||
(skin): skin is ClassicSkinResolver => skin instanceof ClassicSkinResolver
|
||||
);
|
||||
const clientSkins: ClientSkin[] = await Promise.all(
|
||||
classicSkins.map(async (skin) => {
|
||||
const url = await skin.screenshot_url();
|
||||
const readmeText = await skin.readme_text();
|
||||
return {
|
||||
screenshotUrl: url,
|
||||
md5: skin.md5(),
|
||||
fileName: await skin.filename(true),
|
||||
readmeStart: readmeText ? readmeText.slice(0, 200) : "",
|
||||
};
|
||||
})
|
||||
);
|
||||
return clientSkins;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tik-tok style scroll page where we display one skin at a time in full screen
|
||||
*/
|
||||
export default async function ScrollPage() {
|
||||
const initialSkins = await getClientSkins(0);
|
||||
|
||||
return <SkinScroller initialSkins={initialSkins} getSkins={getClientSkins} />;
|
||||
}
|
||||
5
packages/skin-database/app/(modern)/scroll/scroll.css
Normal file
5
packages/skin-database/app/(modern)/scroll/scroll.css
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
body {
|
||||
margin: 0; /* Remove default margin */
|
||||
height: 100vh; /* Set body height to viewport height */
|
||||
background-color: rgb(0, 0, 0);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue