mirror of
https://github.com/System-End/site.git
synced 2026-04-19 19:45:07 +00:00
Refactor onboard gallery
This commit is contained in:
parent
378c21aed4
commit
a4459f7687
15 changed files with 466 additions and 499 deletions
147
components/onboard/gallery-paginated.js
Normal file
147
components/onboard/gallery-paginated.js
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
import { useEffect, useRef } from "react"
|
||||
import PaginationButtons from "./pagination-buttons"
|
||||
import Meta from '@hackclub/meta'
|
||||
import Head from 'next/head'
|
||||
import { Box, Button, Flex, Grid, Heading, Text } from 'theme-ui'
|
||||
import Item from './item'
|
||||
import Nav from '../nav'
|
||||
|
||||
const perPage = 10
|
||||
|
||||
export const GalleryPage = ({currentPage, itemCount, currentProjects}) => {
|
||||
const spotlightRef = useRef()
|
||||
useEffect(() => {
|
||||
const handler = event => {
|
||||
spotlightRef.current.style.background = `radial-gradient(
|
||||
circle at ${event.pageX}px ${event.pageY}px,
|
||||
rgba(0, 0, 0, 0) 10px,
|
||||
rgba(0, 0, 0, 0.8) 80px
|
||||
)`
|
||||
}
|
||||
window.addEventListener('mousemove', handler)
|
||||
return () => window.removeEventListener('mousemove', handler)
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
<Meta
|
||||
as={Head}
|
||||
title="Gallery"
|
||||
description="Check out the latest and greatest from the Onboard project."
|
||||
></Meta>
|
||||
<style>{`
|
||||
|
||||
@font-face {
|
||||
font-family: 'Phantom Sans';
|
||||
src: url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Med.woff')
|
||||
format('woff'),
|
||||
url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Med.woff2')
|
||||
format('woff2');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
`}</style>
|
||||
<Head></Head>
|
||||
<Nav />
|
||||
<Box
|
||||
as="header"
|
||||
sx={{
|
||||
bg: '#000000',
|
||||
backgroundImage: `
|
||||
linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)),
|
||||
url('https://cloud-dst3a9oz5-hack-club-bot.vercel.app/0image.png')
|
||||
`,
|
||||
color: '#ffffff',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
ref={spotlightRef}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
zIndex: 2,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
bg: '#000000',
|
||||
pointerEvents: 'none'
|
||||
}}
|
||||
/>
|
||||
<Flex
|
||||
sx={{
|
||||
p: 4,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
zIndex: 5,
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
p: 4,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
zIndex: 5,
|
||||
margin: '3vh auto',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Heading as="h1" variant="title" sx={{ textAlign: 'center' }}>
|
||||
Gallery
|
||||
</Heading>
|
||||
<Text as="p" variant="subtitle" sx={{ textAlign: 'center' }}>
|
||||
Check out the latest and greatest from the OnBoard project.
|
||||
</Text>
|
||||
<Flex sx={{ mt: 16, gap: 10, flexDirection: ['column', 'row'] }}>
|
||||
<Button
|
||||
variant="ctaLg"
|
||||
as="a"
|
||||
href="https://hackclub.com/onboard"
|
||||
target="_blank"
|
||||
sx={{
|
||||
background: t => t.util.gx('#60cc38', '#113b11')
|
||||
}}
|
||||
>
|
||||
Make your own!
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
bg: 'white',
|
||||
py: [4, 5],
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<PaginationButtons currentPage={currentPage} itemCount={itemCount} perPage={perPage} />
|
||||
<Grid
|
||||
gap={4}
|
||||
columns={[null, 2]}
|
||||
sx={{
|
||||
p: 4,
|
||||
maxWidth: 'copyPlus',
|
||||
mx: 'auto',
|
||||
mt: 4,
|
||||
mb: 5,
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
{currentProjects.map(project => (
|
||||
<Item
|
||||
key={project.name}
|
||||
project={project}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
<PaginationButtons currentPage={currentPage} itemCount={itemCount} perPage={perPage} />
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,76 +1,70 @@
|
|||
import {Box, Divider, Flex, Heading, Image, Paragraph} from "theme-ui";
|
||||
import {Link} from "theme-ui";
|
||||
import React, {useContext} from "react";
|
||||
import {OBJECT} from "swr/_internal";
|
||||
import { Box, Flex, Heading, Paragraph } from "theme-ui";
|
||||
import { Link } from "theme-ui";
|
||||
|
||||
function trim(str) {
|
||||
return str.substring(1, str.length - 1)
|
||||
}
|
||||
// const onboardContext = React.createContext({})
|
||||
|
||||
const onboardContext = React.createContext({})
|
||||
|
||||
const Item = ({ title, author_name, author_slack, image, project }) => {
|
||||
//const { projectCtx, setProjectCtx } = React.useContext(onboardContext)
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
bg: '#ffffff',
|
||||
color: 'black',
|
||||
borderRadius: 8,
|
||||
boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
|
||||
p: 4,
|
||||
mt: 4,
|
||||
position: 'relative'
|
||||
}}
|
||||
const Item = ({ project }) => {
|
||||
const { name, imageTop, galleryURL } = project
|
||||
// console.log({p: props})
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
bg: '#ffffff',
|
||||
color: 'black',
|
||||
borderRadius: 8,
|
||||
boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
|
||||
p: 4,
|
||||
mt: 4,
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<object
|
||||
data={imageTop}
|
||||
type={'image/svg+xml'}
|
||||
style={{
|
||||
width: '100%',
|
||||
borderRadius: '8px'
|
||||
}}
|
||||
></object>
|
||||
<Link
|
||||
href={galleryURL}
|
||||
sx={{
|
||||
textDecoration: 'none',
|
||||
color: 'black',
|
||||
':hover': {
|
||||
color: 'primary'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<object
|
||||
data={image}
|
||||
type={'image/svg+xml'}
|
||||
style={{
|
||||
width: '100%',
|
||||
borderRadius: '8px'
|
||||
}}
|
||||
></object>
|
||||
<Link
|
||||
href={`/onboard/board/${project.project_name}`}
|
||||
sx={{
|
||||
textDecoration: 'none',
|
||||
color: 'black',
|
||||
':hover': {
|
||||
color: 'primary'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
as="h2"
|
||||
//variant="title"
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
mt: 3
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Heading>
|
||||
</Link>
|
||||
<Paragraph
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
mt: 2,
|
||||
wordBreak: 'break-word'
|
||||
}}
|
||||
>
|
||||
{`${author_name ? `by ${trim(author_name)}` : ""} ${author_slack ? `(${trim(author_slack)})` : ""}`}
|
||||
</Paragraph>
|
||||
</Flex>
|
||||
</Box>
|
||||
)
|
||||
<Heading
|
||||
as="h2"
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
mt: 3
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</Heading>
|
||||
</Link>
|
||||
<Paragraph
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
mt: 2,
|
||||
wordBreak: 'break-word'
|
||||
}}
|
||||
>
|
||||
{/* {`${author_name ? `by ${trim(author_name)}` : ""} ${author_slack ? `(${trim(author_slack)})` : ""}`} */}
|
||||
</Paragraph>
|
||||
</Flex>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default Item;
|
||||
export { onboardContext };
|
||||
// export { onboardContext };
|
||||
59
components/onboard/pagination-buttons.js
Normal file
59
components/onboard/pagination-buttons.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { Box, Button, Text } from "theme-ui"
|
||||
|
||||
const PaginationButtons = ({ currentPage, itemCount, perPage }) => {
|
||||
const showPreviousPage = currentPage > 1
|
||||
const showNextPage = itemCount > (currentPage * perPage)
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
mt: 5,
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
{showPreviousPage && (
|
||||
<Button
|
||||
as="a"
|
||||
href={`/onboard/gallery/${parseInt(currentPage) - 1}`}
|
||||
sx={{
|
||||
bg: 'black',
|
||||
color: 'white',
|
||||
':hover': {
|
||||
bg: 'white',
|
||||
color: 'black'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{'<'}
|
||||
</Button>
|
||||
)}
|
||||
<Text
|
||||
as="span"
|
||||
sx={{
|
||||
mx: 3,
|
||||
color: 'black'
|
||||
}}
|
||||
>
|
||||
{currentPage}
|
||||
</Text>
|
||||
{showNextPage && (
|
||||
<Button
|
||||
as="a"
|
||||
href={`/onboard/gallery/${parseInt(currentPage) + 1}`}
|
||||
sx={{
|
||||
bg: 'black',
|
||||
color: 'white',
|
||||
':hover': {
|
||||
bg: 'white',
|
||||
color: 'black'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{'>'}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default PaginationButtons
|
||||
|
|
@ -290,7 +290,7 @@ const nextConfig = {
|
|||
]
|
||||
},
|
||||
{
|
||||
source: '/api/board/svg/(.+)',
|
||||
source: '/api/onboard/svg/(.+)',
|
||||
headers: [
|
||||
{
|
||||
key: 'content-type',
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
import {gerberToSvg} from "./svg/[board_url]";
|
||||
|
||||
export const FetchProject = async (name) => {
|
||||
const readme = await fetch(`https://raw.githubusercontent.com/hackclub/OnBoard/main/projects/${name}/README.md`)
|
||||
const text = await readme.text()
|
||||
// parse YAML frontmatter
|
||||
const lines = text.split('\n')
|
||||
const frontmatter = {}
|
||||
let i = 0
|
||||
for (; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('---')) {
|
||||
break
|
||||
}
|
||||
}
|
||||
for (i++; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('---')) {
|
||||
break
|
||||
}
|
||||
const [key, value] = lines[i].split(': ')
|
||||
frontmatter[key] = value
|
||||
}
|
||||
// check for a "thumbnail.png" file in the project directory
|
||||
//console.log(`https://github.com/snoglobe/OnBoard/raw/main/projects/${name}/thumbnail.png`)
|
||||
/*const thumbnail = await fetch(`https://github.com/snoglobe/OnBoard/raw/main/projects/${name}/thumbnail.png`, {mode: 'no-cors'})*/
|
||||
/*console.log(thumbnail)*/
|
||||
const image = /*thumbnail.ok ? `https://github.com/snoglobe/OnBoard/raw/main/projects/${name}/thumbnail.png`
|
||||
:*/ /*`data:image/svg+xml;base64,${btoa((await gerberToSvg(`https://github.com/snoglobe/OnBoard/raw/main/projects/${name}/gerber.zip`)).top)}`*/
|
||||
`/api/board/svg/${encodeURIComponent(`https://github.com/snoglobe/OnBoard/raw/main/projects/${name}/gerber.zip`)}/top`
|
||||
console.log("done")
|
||||
return({
|
||||
project_name: name ?? null,
|
||||
maker_name: frontmatter.name ?? null,
|
||||
slack_handle: frontmatter.slack_handle ?? null,
|
||||
github_handle: frontmatter.github_handle ?? null,
|
||||
tutorial: frontmatter.tutorial ?? null,
|
||||
description: lines.slice(i + 1).join('\n') ?? null,
|
||||
image: image ?? null
|
||||
})
|
||||
}
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { name } = req.query
|
||||
if (!name) {
|
||||
return res.status(400).json({ status: 400, error: 'Must provide name' })
|
||||
}
|
||||
const project = await FetchProject(name)
|
||||
if (!project) {
|
||||
return res.status(404).json({ status: 404, error: 'Project not found' })
|
||||
}
|
||||
return res.status(200).json(project)
|
||||
}
|
||||
52
pages/api/onboard/p/[project]/index.js
Normal file
52
pages/api/onboard/p/[project]/index.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// return a project's metadata
|
||||
|
||||
import { getAllOnboardProjects } from ".."
|
||||
|
||||
async function getReadmeData(url) {
|
||||
const readme = await fetch(url)
|
||||
const text = await readme.text()
|
||||
// parse YAML frontmatter
|
||||
const lines = text.split('\n')
|
||||
const frontmatter = {}
|
||||
let i = 0
|
||||
for (; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('---')) {
|
||||
break
|
||||
}
|
||||
}
|
||||
for (i++; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('---')) {
|
||||
break
|
||||
}
|
||||
const [key, value] = lines[i].split(': ')
|
||||
frontmatter[key] = value || null
|
||||
}
|
||||
const description = lines.slice(i + 1).join('\n')
|
||||
return {
|
||||
frontmatter,
|
||||
description
|
||||
}
|
||||
}
|
||||
|
||||
export const getOnboardProject = async (name) => {
|
||||
// this is not performant to call all projects every time, but we're doing it for now while things load quickly enough
|
||||
// TODO: Speed this up
|
||||
const project = (await getAllOnboardProjects()).find(p => p.name === name)
|
||||
const readmeData = await getReadmeData(project.readmeURL)
|
||||
|
||||
const result = { ...project, readmeData }
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { name } = req.query
|
||||
if (!name) {
|
||||
return res.status(400).json({ status: 400, error: 'Must provide name' })
|
||||
}
|
||||
const project = await getOnboardProject(name)
|
||||
if (!project) {
|
||||
return res.status(404).json({ status: 404, error: 'Project not found' })
|
||||
}
|
||||
return res.status(200).json(project)
|
||||
}
|
||||
12
pages/api/onboard/p/count.js
Normal file
12
pages/api/onboard/p/count.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { getAllOnboardProjects } from "."
|
||||
|
||||
export async function onboardProjectCount() {
|
||||
const projects = await getAllOnboardProjects()
|
||||
return projects.length
|
||||
}
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const count = await onboardProjectCount()
|
||||
|
||||
res.json({ count })
|
||||
}
|
||||
47
pages/api/onboard/p/index.js
Normal file
47
pages/api/onboard/p/index.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// returns a list of all projects
|
||||
|
||||
export const getAllOnboardProjects = async () => {
|
||||
const url = 'https://api.github.com/repos/hackclub/onboard/contents/projects'
|
||||
const fetchOptions = {}
|
||||
if (process.env.GITHUB_TOKEN) {
|
||||
// this field is optional because we do heavy caching in production, but nice to have for local development
|
||||
fetchOptions.headers = {Authorization: `Bearer ${process.env.GITHUB_TOKEN}`}
|
||||
}
|
||||
|
||||
const res = await fetch(url, fetchOptions).then(r => r.json())
|
||||
|
||||
if (res.message && res.message.startsWith('API rate limit exceeded')) {
|
||||
console.error('GitHub API rate limit exceeded')
|
||||
}
|
||||
|
||||
const projects = []
|
||||
|
||||
res.forEach(p => {
|
||||
if (p.type !== 'dir') { return }
|
||||
if (p.name[0] === '.') { return }
|
||||
if (p.name[0] === '!') { return }
|
||||
|
||||
const projectData = {
|
||||
name: p.name,
|
||||
url: `https://github.com/hackclub/OnBoard/tree/main/projects/${p.name}/README.md`,
|
||||
galleryURL: `/onboard/gallery/${p.name}`,
|
||||
githubURL: p.html_url,
|
||||
readmeURL: `https://raw.githubusercontent.com/hackclub/OnBoard/main/projects/${p.name}/README.md`,
|
||||
schematicURL: `https://raw.githubusercontent.com/hackclub/OnBoard/main/projects/${p.name}/schematic.pdf`,
|
||||
gerberURL: `https://raw.githubusercontent.com/hackclub/OnBoard/main/projects/${p.name}/gerber.zip`
|
||||
}
|
||||
|
||||
projectData.imageTop = `/api/onboard/svg/${encodeURIComponent(projectData.gerberURL)}/top`
|
||||
projectData.imageBottom = `/api/onboard/svg/${encodeURIComponent(projectData.gerberURL)}/bottom`
|
||||
|
||||
projects.push(projectData)
|
||||
})
|
||||
|
||||
return projects
|
||||
}
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const projects = await getAllOnboardProjects()
|
||||
|
||||
res.json(projects)
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
async function onboardProjectCount() {
|
||||
const url = 'https://api.github.com/repos/hackclub/onboard/contents/projects'
|
||||
|
||||
const response = await fetch(url).then(r => r.json())
|
||||
const countedProjects = response.filter(
|
||||
folder =>
|
||||
folder.type === 'dir' && folder.name[0] !== '.' && folder.name[0] !== '!'
|
||||
)
|
||||
return countedProjects.length
|
||||
}
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const count = await onboardProjectCount()
|
||||
|
||||
res.json({ count })
|
||||
}
|
||||
|
|
@ -1,33 +1,14 @@
|
|||
import { Box, Button, Flex, Grid, Heading, Image, Link, Text } from 'theme-ui'
|
||||
import { Box, Button, Flex, Heading, Image, Link, Text } from 'theme-ui'
|
||||
import Head from 'next/head'
|
||||
import Meta from '@hackclub/meta'
|
||||
import Nav from '../../../components/nav'
|
||||
import usePrefersReducedMotion from '../../../lib/use-prefers-reduced-motion'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { remark } from 'remark'
|
||||
import html from 'remark-html'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FetchProject } from '../../api/board/[name]'
|
||||
|
||||
// example projects
|
||||
const curated = [
|
||||
'Touch Capacitive Piano',
|
||||
'Small Stepper Motor Breakout',
|
||||
'ShawnsMultipurposeMacropad',
|
||||
'Hall-Effect Sensor Plate',
|
||||
'Gas_Smoke_Detector',
|
||||
'GPS Tracker for GOKART',
|
||||
"Ewoud's Desktop Clock PCB",
|
||||
'Connor Ender 3 Bed Leveler'
|
||||
]
|
||||
|
||||
const BoardPage = ({ projectObj }) => {
|
||||
const prefersReducedMotion = usePrefersReducedMotion()
|
||||
|
||||
// const router = useRouter()
|
||||
// get slug
|
||||
// const name = router.query.slug
|
||||
import { getOnboardProject } from '../../api/onboard/p/[project]'
|
||||
import { getAllOnboardProjects } from '../../api/onboard/p'
|
||||
|
||||
const BoardPage = ({ project }) => {
|
||||
const spotlightRef = useRef()
|
||||
useEffect(() => {
|
||||
const handler = event => {
|
||||
|
|
@ -41,18 +22,6 @@ const BoardPage = ({ projectObj }) => {
|
|||
return () => window.removeEventListener('mousemove', handler)
|
||||
}, [])
|
||||
|
||||
console.log(projectObj)
|
||||
|
||||
const [project, setProject] = useState({})
|
||||
useEffect(() => {
|
||||
/*(async () => {
|
||||
const project = await (await fetch(`/api/board/${name}`)).json()
|
||||
console.log(project)
|
||||
setProject(project)
|
||||
})()*/
|
||||
setProject(projectObj)
|
||||
}, [projectObj])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Meta
|
||||
|
|
@ -123,9 +92,11 @@ const BoardPage = ({ projectObj }) => {
|
|||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Heading as="h1" variant="title" sx={{ textAlign: 'center' }}>
|
||||
Gallery
|
||||
</Heading>
|
||||
<Link as="a" href="/onboard/gallery" sx={{ color: 'white' }}>
|
||||
<Heading as="h1" variant="title" sx={{ textAlign: 'center' }}>
|
||||
Gallery
|
||||
</Heading>
|
||||
</Link>
|
||||
<Text as="p" variant="subtitle" sx={{ textAlign: 'center' }}>
|
||||
Check out the latest and greatest from the OnBoard project.
|
||||
</Text>
|
||||
|
|
@ -171,8 +142,8 @@ const BoardPage = ({ projectObj }) => {
|
|||
}}
|
||||
>
|
||||
<Image
|
||||
src={project.image}
|
||||
alt={project.project_name}
|
||||
src={project.imageTop}
|
||||
alt={project.name}
|
||||
sx={{
|
||||
borderRadius: 8,
|
||||
boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)'
|
||||
|
|
@ -185,15 +156,22 @@ const BoardPage = ({ projectObj }) => {
|
|||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
{process.env.NODE_ENV !== 'production' && (
|
||||
<>
|
||||
<pre>
|
||||
{JSON.stringify(project, null, 2)}
|
||||
</pre>
|
||||
</>
|
||||
)}
|
||||
<Heading as="h2" variant="title" sx={{ textAlign: 'left' }}>
|
||||
{project.project_name}
|
||||
{project.name}
|
||||
</Heading>
|
||||
<Text as="p" variant="subtitle" sx={{ textAlign: 'left' }}>
|
||||
{project.maker_name ? `by ${project.maker_name}` : ''}{' '}
|
||||
{project.slack_handle ? `(${project.slack_handle})` : ''}
|
||||
{project?.readmeData?.frontmatter?.github_handle ? `by ${project?.readmeData?.frontmatter?.github_handle}` : ''}
|
||||
</Text>
|
||||
<Link
|
||||
href={`https://github.com/hackclub/OnBoard/blob/main/projects/${project.project_name}/`}
|
||||
target="_blank"
|
||||
href={`https://github.com/hackclub/OnBoard/blob/main/projects/${project.name}/`}
|
||||
sx={{
|
||||
textDecoration: 'none',
|
||||
color: 'black',
|
||||
|
|
@ -205,7 +183,6 @@ const BoardPage = ({ projectObj }) => {
|
|||
>
|
||||
View on GitHub
|
||||
</Link>
|
||||
{/* custom innerHTML */}
|
||||
<Box
|
||||
sx={{
|
||||
textAlign: 'left'
|
||||
|
|
@ -215,7 +192,7 @@ const BoardPage = ({ projectObj }) => {
|
|||
// render with remark to parse markdown
|
||||
remark()
|
||||
.use(html)
|
||||
.processSync(project.description)
|
||||
.processSync(project?.readmeData?.description)
|
||||
.toString()
|
||||
.replaceAll('h4', 'p')
|
||||
}}
|
||||
|
|
@ -228,19 +205,15 @@ const BoardPage = ({ projectObj }) => {
|
|||
)
|
||||
}
|
||||
|
||||
export async function getStaticPaths(context) {
|
||||
const res = await fetch(
|
||||
'https://api.github.com/repos/hackclub/OnBoard/contents/projects'
|
||||
)
|
||||
const data =
|
||||
await res.json() /*.filter(project => curated.includes(project.name))*/
|
||||
//console.log(data)
|
||||
const projects = data.map(project => project.name)
|
||||
const paths = projects.map(name => ({
|
||||
params: {
|
||||
slug: name
|
||||
}
|
||||
}))
|
||||
export async function getStaticPaths(_context) {
|
||||
const projects = await getAllOnboardProjects()
|
||||
const paths = projects.map(project => {
|
||||
return ({
|
||||
params: {
|
||||
slug: encodeURIComponent(project.name)
|
||||
}
|
||||
})
|
||||
})
|
||||
return {
|
||||
paths,
|
||||
fallback: 'blocking'
|
||||
|
|
@ -248,12 +221,13 @@ export async function getStaticPaths(context) {
|
|||
}
|
||||
|
||||
export async function getStaticProps(context) {
|
||||
let name = context.params.slug
|
||||
let project = await FetchProject(name)
|
||||
const name = context.params.slug
|
||||
const project = await getOnboardProject(name)
|
||||
return {
|
||||
props: {
|
||||
projectObj: project
|
||||
}
|
||||
project
|
||||
},
|
||||
revalidate: 120
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,318 +1,41 @@
|
|||
import { Box, Button, Flex, Grid, Heading, Text } from 'theme-ui'
|
||||
import Head from 'next/head'
|
||||
import Meta from '@hackclub/meta'
|
||||
import Nav from '../../../components/nav'
|
||||
import usePrefersReducedMotion from '../../../lib/use-prefers-reduced-motion'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import Item from '../../../components/onboard/item'
|
||||
import { getUrl } from 'nextjs-current-url/server'
|
||||
import { getURL } from 'next/dist/shared/lib/utils'
|
||||
import { FetchProject } from '../../api/board/[name]'
|
||||
import { useRouter } from 'next/router'
|
||||
import { GalleryPage } from "../../../components/onboard/gallery-paginated";
|
||||
|
||||
/*import pcbStackup from "pcb-stackup";
|
||||
import JSZip from "jszip";
|
||||
import JSZipUtils from "jszip-utils";*/
|
||||
import { getAllOnboardProjects } from "../../api/onboard/p"
|
||||
import { getOnboardProject } from "../../api/onboard/p/[project]"
|
||||
import { onboardProjectCount } from "../../api/onboard/p/count"
|
||||
|
||||
async function get_fallback_image(project) {
|
||||
/*const fileNamesBlobs = {}
|
||||
// load the zip file
|
||||
const zip = new JSZip();
|
||||
await JSZipUtils.getBinaryContent(project, async (err, data) => {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
}
|
||||
try {
|
||||
const zipData = await zip.loadAsync(data)
|
||||
// get the file names and blobs
|
||||
for (const [fileName, file] of Object.entries(zipData.files)) {
|
||||
fileNamesBlobs[fileName] = await file.async('blob')
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
const layers = []
|
||||
for (const [fileName, blob] of Object.entries(fileNamesBlobs)) {
|
||||
if (!fileName.includes('.txt')) { // filter out the text files
|
||||
layers.push({
|
||||
fileName,
|
||||
gerber: blob.stream()
|
||||
})
|
||||
}
|
||||
}
|
||||
return (await pcbStackup(layers)).top.svg*/
|
||||
return 'https://cloud-2jz3jz3jz-hack-club-bot.vercel.app/0image.png'
|
||||
}
|
||||
|
||||
// example projects
|
||||
const curated = [
|
||||
'Touch Capacitive Piano',
|
||||
'Small Stepper Motor Breakout',
|
||||
'ShawnsMultipurposeMacropad',
|
||||
'Hall-Effect Sensor Plate',
|
||||
'Gas_Smoke_Detector',
|
||||
'GPS Tracker for GOKART',
|
||||
"Ewoud's Desktop Clock PCB",
|
||||
'Connor Ender 3 Bed Leveler'
|
||||
]
|
||||
|
||||
const GalleryPage = ({ projects }) => {
|
||||
const prefersReducedMotion = usePrefersReducedMotion()
|
||||
const router = useRouter()
|
||||
const page = router.query.page
|
||||
const spotlightRef = useRef()
|
||||
useEffect(() => {
|
||||
const handler = event => {
|
||||
spotlightRef.current.style.background = `radial-gradient(
|
||||
circle at ${event.pageX}px ${event.pageY}px,
|
||||
rgba(0, 0, 0, 0) 10px,
|
||||
rgba(0, 0, 0, 0.8) 80px
|
||||
)`
|
||||
}
|
||||
window.addEventListener('mousemove', handler)
|
||||
return () => window.removeEventListener('mousemove', handler)
|
||||
}, [])
|
||||
|
||||
// fetch all folders in the https://github.com/hackclub/OnBoard/tree/main/projects directory
|
||||
/*const [projects, setProjects] = useState([])
|
||||
useEffect(() => {
|
||||
const fetchProjects = async () => {
|
||||
const res = await fetch(
|
||||
'https://api.github.com/repos/hackclub/OnBoard/contents/projects'
|
||||
)
|
||||
const data = (await res.json()).filter(project => curated.includes(project.name))
|
||||
console.log(data)
|
||||
const projectData = data.map(async project => {
|
||||
return await (await fetch(`/api/board/${project.name}`)).json()
|
||||
})
|
||||
let projects = await Promise.all(projectData)
|
||||
//console.log(projects)
|
||||
setProjects(projects)
|
||||
}
|
||||
fetchProjects()
|
||||
}, [])*/
|
||||
export default function Page({projects, itemCount, currentPage}) {
|
||||
return (
|
||||
<>
|
||||
<Meta
|
||||
as={Head}
|
||||
title="Gallery"
|
||||
description="Check out the latest and greatest from the Onboard project."
|
||||
></Meta>
|
||||
<style>{`
|
||||
|
||||
@font-face {
|
||||
font-family: 'Phantom Sans';
|
||||
src: url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Med.woff')
|
||||
format('woff'),
|
||||
url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Med.woff2')
|
||||
format('woff2');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
`}</style>
|
||||
<Head></Head>
|
||||
<Nav />
|
||||
<Box
|
||||
as="header"
|
||||
sx={{
|
||||
bg: '#000000',
|
||||
backgroundImage: `
|
||||
linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)),
|
||||
url('https://cloud-dst3a9oz5-hack-club-bot.vercel.app/0image.png')
|
||||
`,
|
||||
color: '#ffffff',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
ref={spotlightRef}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
zIndex: 2,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
bg: '#000000',
|
||||
pointerEvents: 'none'
|
||||
}}
|
||||
/>
|
||||
<Flex
|
||||
sx={{
|
||||
p: 4,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
zIndex: 5,
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
p: 4,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
zIndex: 5,
|
||||
margin: '3vh auto',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Heading as="h1" variant="title" sx={{ textAlign: 'center' }}>
|
||||
Gallery
|
||||
</Heading>
|
||||
<Text as="p" variant="subtitle" sx={{ textAlign: 'center' }}>
|
||||
Check out the latest and greatest from the OnBoard project.
|
||||
</Text>
|
||||
<Flex sx={{ mt: 16, gap: 10, flexDirection: ['column', 'row'] }}>
|
||||
<Button
|
||||
variant="ctaLg"
|
||||
as="a"
|
||||
href="https://hackclub.com/onboard"
|
||||
target="_blank"
|
||||
sx={{
|
||||
background: t => t.util.gx('#60cc38', '#113b11')
|
||||
}}
|
||||
>
|
||||
Make your own!
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
bg: 'white',
|
||||
py: [4, 5],
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
gap={4}
|
||||
columns={[null, 2]}
|
||||
sx={{
|
||||
p: 4,
|
||||
maxWidth: 'copyPlus',
|
||||
mx: 'auto',
|
||||
mt: 4,
|
||||
mb: 5,
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
{projects.map(project => (
|
||||
<Item
|
||||
key={project.project_name}
|
||||
title={project.project_name}
|
||||
author_name={project.maker_name}
|
||||
author_slack={project.slack_handle}
|
||||
image={project.image}
|
||||
project={project}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
<Box
|
||||
sx={{
|
||||
mt: 5,
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
as="a"
|
||||
href={`/onboard/gallery/${parseInt(page) - 1}`}
|
||||
sx={{
|
||||
bg: 'black',
|
||||
color: 'white',
|
||||
':hover': {
|
||||
bg: 'white',
|
||||
color: 'black'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{'<'}
|
||||
</Button>
|
||||
<Text
|
||||
as="span"
|
||||
sx={{
|
||||
mx: 3,
|
||||
color: 'black'
|
||||
}}
|
||||
>
|
||||
{page}
|
||||
</Text>
|
||||
<Button
|
||||
as="a"
|
||||
href={`/onboard/gallery/${parseInt(page) + 1}`}
|
||||
sx={{
|
||||
bg: 'black',
|
||||
color: 'white',
|
||||
':hover': {
|
||||
bg: 'white',
|
||||
color: 'black'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{'>'}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
<GalleryPage currentPage={currentPage} itemCount={itemCount} currentProjects={projects} />
|
||||
)
|
||||
}
|
||||
|
||||
/*export async function getServerSideProps(context) {
|
||||
const res = await fetch(
|
||||
'https://api.github.com/repos/hackclub/OnBoard/contents/projects'
|
||||
)
|
||||
const data = (await res.json())/*.filter(project => curated.includes(project.name))
|
||||
console.log(data)
|
||||
const projectData = data.map(async project => {
|
||||
const url = getUrl({ req: context.req })
|
||||
console.log(url)
|
||||
return await (await fetch(encodeURI(`${url.origin}/api/board/${project.name}`))).json()
|
||||
})
|
||||
let projects = await Promise.all(projectData)
|
||||
return {
|
||||
props: {
|
||||
projects
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
export async function getStaticProps(context) {
|
||||
const res = await fetch(
|
||||
'https://api.github.com/repos/hackclub/OnBoard/contents/projects'
|
||||
const currentPage = parseInt(context.params.page)
|
||||
const allProjects = await getAllOnboardProjects()
|
||||
const data = (allProjects).slice(
|
||||
(currentPage - 1) * 10,
|
||||
currentPage * 10
|
||||
)
|
||||
const data = (await res.json()).slice(
|
||||
(parseInt(context.params.page) - 1) * 10,
|
||||
parseInt(context.params.page) * 10
|
||||
)
|
||||
//console.log(data)
|
||||
const projects = []
|
||||
for (const project of data) {
|
||||
projects.push(await FetchProject(project.name))
|
||||
projects.push(await getOnboardProject(project.name))
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
projects
|
||||
}
|
||||
projects,
|
||||
itemCount: allProjects.length,
|
||||
currentPage,
|
||||
},
|
||||
revalidate: 120 // 2 minutes
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStaticPaths(context) {
|
||||
// divide the projects into chunks of 10
|
||||
const res = await fetch(
|
||||
'https://api.github.com/repos/hackclub/OnBoard/contents/projects'
|
||||
)
|
||||
const data = await res.json()
|
||||
const pages = Math.ceil(data.length / 10)
|
||||
export async function getStaticPaths(_context) {
|
||||
const projectCount = await onboardProjectCount()
|
||||
const pages = Math.min(5, Math.ceil( projectCount / 10 ))
|
||||
const paths = Array(pages)
|
||||
.fill()
|
||||
.map((_, i) => ({ params: { page: (i + 1).toString() } }))
|
||||
return { paths, fallback: false }
|
||||
}
|
||||
|
||||
export default GalleryPage
|
||||
}
|
||||
26
pages/onboard/gallery/index.js
Normal file
26
pages/onboard/gallery/index.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { GalleryPage } from "../../../components/onboard/gallery-paginated";
|
||||
import { getAllOnboardProjects } from "../../api/onboard/p";
|
||||
import { getOnboardProject } from "../../api/onboard/p/[project]";
|
||||
|
||||
export default function Index({projects, itemCount}) {
|
||||
return (
|
||||
<GalleryPage currentPage={1} itemCount={itemCount} currentProjects={projects} />
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export async function getStaticProps(_context) {
|
||||
const allProjects = await getAllOnboardProjects()
|
||||
const data = (allProjects).slice(0, 10)
|
||||
const projects = []
|
||||
for (const project of data) {
|
||||
projects.push(await getOnboardProject(project.name))
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
projects,
|
||||
itemCount: allProjects.length,
|
||||
},
|
||||
revalidate: 120 // 2 minutes
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue