Skip to content

Commit 0705e9d

Browse files
committed
Stub out shorts
1 parent 340e224 commit 0705e9d

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
"use client";
2+
3+
import { useState, useLayoutEffect } from "react";
4+
5+
export type ClientSkin = {
6+
screenshotUrl: string;
7+
fileName: string;
8+
md5: string;
9+
readmeStart: string;
10+
};
11+
12+
type Props = {
13+
initialSkins: ClientSkin[];
14+
getSkins: (offset: number) => Promise<ClientSkin[]>;
15+
};
16+
17+
export default function SkinScroller({ initialSkins, getSkins }: Props) {
18+
const [skins, setSkins] = useState<ClientSkin[]>(initialSkins);
19+
const [visibleSkinIndex, setVisibleSkinIndex] = useState(0);
20+
const [fetching, setFetching] = useState(false);
21+
const [containerRef, setContainerRef] = useState<HTMLDivElement | null>(null);
22+
23+
useLayoutEffect(() => {
24+
if (containerRef == null) {
25+
return;
26+
}
27+
28+
function onSnap(e) {
29+
const md5 = e.snapTargetBlock.getAttribute("skin-md5");
30+
const index = parseInt(e.snapTargetBlock.getAttribute("skin-index"));
31+
setVisibleSkinIndex(index);
32+
}
33+
34+
containerRef.addEventListener("scrollsnapchange", onSnap);
35+
36+
return () => {
37+
containerRef.removeEventListener("scrollsnapchange", onSnap);
38+
};
39+
}, [containerRef]);
40+
41+
useLayoutEffect(() => {
42+
if (fetching) {
43+
return;
44+
}
45+
if (visibleSkinIndex + 5 >= skins.length) {
46+
setFetching(true);
47+
getSkins(skins.length).then((newSkins) => {
48+
setSkins([...skins, ...newSkins]);
49+
setFetching(false);
50+
});
51+
}
52+
}, [visibleSkinIndex, skins, fetching]);
53+
54+
return (
55+
<div
56+
ref={setContainerRef}
57+
style={{
58+
height: "100vh",
59+
overflowY: "scroll",
60+
scrollSnapType: "y mandatory",
61+
}}
62+
>
63+
{skins.map((skin, i) => {
64+
return (
65+
<div
66+
key={skin.md5}
67+
skin-md5={skin.md5}
68+
skin-index={i}
69+
style={{
70+
display: "flex",
71+
flexDirection: "column",
72+
width: "100%",
73+
height: "100vh",
74+
scrollSnapAlign: "start",
75+
scrollSnapStop: "always",
76+
}}
77+
>
78+
<img
79+
src={skin.screenshotUrl}
80+
alt={skin.fileName}
81+
style={{
82+
paddingTop: "4rem",
83+
boxSizing: "border-box",
84+
width: "100%",
85+
imageRendering: "pixelated",
86+
}}
87+
/>
88+
<div
89+
style={{
90+
color: "white",
91+
flexGrow: 1,
92+
paddingLeft: "0.5rem",
93+
paddingTop: "0.5rem",
94+
}}
95+
>
96+
<h2
97+
style={{
98+
marginBottom: 0,
99+
fontSize: "0.9rem",
100+
paddingBottom: "0",
101+
fontFamily: 'Arial, "Helvetica Neue", Helvetica, sans-serif',
102+
color: "#ccc",
103+
}}
104+
>
105+
{skin.fileName}
106+
</h2>
107+
<p
108+
style={{
109+
marginTop: "0.5rem",
110+
fontSize: "0.75rem",
111+
paddingTop: "0",
112+
color: "#999",
113+
fontFamily: 'monospace, "Courier New", Courier, monospace',
114+
}}
115+
>
116+
{skin.readmeStart}
117+
</p>
118+
</div>
119+
</div>
120+
);
121+
})}
122+
</div>
123+
);
124+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import ClassicSkinResolver from "../../../api/graphql/resolvers/ClassicSkinResolver";
2+
import SkinsConnection from "../../../api/graphql/SkinsConnection";
3+
import UserContext from "../../../data/UserContext";
4+
import "./scroll.css";
5+
import SkinScroller, { ClientSkin } from "./SkinScroller";
6+
7+
async function getClientSkins(offset: number): Promise<ClientSkin[]> {
8+
"use server";
9+
const ctx = new UserContext();
10+
const connection = new SkinsConnection(10, offset, "MUSEUM", null);
11+
const skins = await connection.nodes(ctx);
12+
if (skins == null) {
13+
return [];
14+
}
15+
const classicSkins = skins.filter(
16+
(skin): skin is ClassicSkinResolver => skin instanceof ClassicSkinResolver
17+
);
18+
const clientSkins: ClientSkin[] = await Promise.all(
19+
classicSkins.map(async (skin) => {
20+
const url = await skin.screenshot_url();
21+
const readmeText = await skin.readme_text();
22+
return {
23+
screenshotUrl: url,
24+
md5: skin.md5(),
25+
fileName: await skin.filename(true),
26+
readmeStart: readmeText ? readmeText.slice(0, 200) : "",
27+
};
28+
})
29+
);
30+
return clientSkins;
31+
}
32+
33+
/**
34+
* A tik-tok style scroll page where we display one skin at a time in full screen
35+
*/
36+
export default async function ScrollPage() {
37+
const initialSkins = await getClientSkins(0);
38+
39+
return <SkinScroller initialSkins={initialSkins} getSkins={getClientSkins} />;
40+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
body {
2+
margin: 0; /* Remove default margin */
3+
height: 100vh; /* Set body height to viewport height */
4+
background-color: rgb(0, 0, 0);
5+
}

0 commit comments

Comments
 (0)