mirror of
https://github.com/System-End/site.git
synced 2026-04-19 23:22:49 +00:00
commit
378c21aed4
10 changed files with 1807 additions and 8 deletions
76
components/onboard/item.js
Normal file
76
components/onboard/item.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
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";
|
||||
|
||||
function trim(str) {
|
||||
return str.substring(1, str.length - 1)
|
||||
}
|
||||
|
||||
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'
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
export default Item;
|
||||
export { onboardContext };
|
||||
|
|
@ -288,6 +288,15 @@ const nextConfig = {
|
|||
},
|
||||
{ key: 'Access-Control-Allow-Headers', value: 'Content-Type' }
|
||||
]
|
||||
},
|
||||
{
|
||||
source: '/api/board/svg/(.+)',
|
||||
headers: [
|
||||
{
|
||||
key: 'content-type',
|
||||
value: 'image/svg+xml'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
13
package.json
13
package.json
|
|
@ -25,6 +25,12 @@
|
|||
"@octokit/auth-app": "^6.0.1",
|
||||
"@octokit/core": "^5.1.0",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@tracespace/core": "^5.0.0-alpha.0",
|
||||
"@tracespace/identify-layers": "^5.0.0-alpha.0",
|
||||
"@tracespace/parser": "^5.0.0-next.0",
|
||||
"@tracespace/plotter": "^5.0.0-alpha.0",
|
||||
"@tracespace/renderer": "^5.0.0-alpha.0",
|
||||
"@tracespace/xml-id": "^4.2.7",
|
||||
"@sendgrid/mail": "^8.1.1",
|
||||
"add": "^2.0.6",
|
||||
"airtable-plus": "^1.0.4",
|
||||
|
|
@ -43,10 +49,14 @@
|
|||
"globby": "^11.0.4",
|
||||
"graphql": "^16.8.1",
|
||||
"js-confetti": "^0.12.0",
|
||||
"jszip": "^3.10.1",
|
||||
"jszip-utils": "^0.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"million": "^2.6.4",
|
||||
"next": "^12.3.1",
|
||||
"next-transpile-modules": "^10.0.1",
|
||||
"nextjs-current-url": "^1.0.3",
|
||||
"pcb-stackup": "^4.2.8",
|
||||
"react": "^17.0.2",
|
||||
"react-before-after-slider-component": "^1.1.8",
|
||||
"react-datepicker": "^4.24.0",
|
||||
|
|
@ -58,6 +68,7 @@
|
|||
"react-page-visibility": "^7.0.0",
|
||||
"react-relative-time": "^0.0.9",
|
||||
"react-reveal": "^1.2.2",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-scrolllock": "^5.0.1",
|
||||
"react-snowfall": "^1.2.1",
|
||||
"react-ticker": "^1.3.2",
|
||||
|
|
@ -66,6 +77,8 @@
|
|||
"react-use-websocket": "^4.7.0",
|
||||
"react-wrap-balancer": "^1.1.0",
|
||||
"recharts": "2.12.2",
|
||||
"remark": "^15.0.1",
|
||||
"remark-html": "^16.0.1",
|
||||
"styled-components": "^6.1.8",
|
||||
"swr": "^2.2.4",
|
||||
"theme-ui": "^0.14",
|
||||
|
|
|
|||
51
pages/api/board/[name].js
Normal file
51
pages/api/board/[name].js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
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)
|
||||
}
|
||||
17
pages/api/board/svg/[board_url]/bottom.js
Normal file
17
pages/api/board/svg/[board_url]/bottom.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// test me with: curl http://localhost:3000/api/board/svg/https%3A%2F%2Fgithub.com%2Fhackclub%2FOnBoard%2Fraw%2Fmain%2Fprojects%2F2_Switch_Keyboard%2Fgerber.zip/bottom
|
||||
|
||||
import { gerberToSvg } from '.'
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { board_url } = req.query
|
||||
if (!board_url) {
|
||||
return res.status(404).json({ status: 404, error: 'Must provide file' })
|
||||
}
|
||||
// ensure valid file url is included
|
||||
const parsed_url = new URL(decodeURI(board_url))
|
||||
if (!parsed_url) {
|
||||
return res.status(404).json({ status: 404, error: 'Invalid file' })
|
||||
}
|
||||
const svg = await gerberToSvg(parsed_url)
|
||||
return res.status(200).send(svg.bottom)
|
||||
}
|
||||
93
pages/api/board/svg/[board_url]/index.js
Normal file
93
pages/api/board/svg/[board_url]/index.js
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import JSZip from 'jszip'
|
||||
import {
|
||||
read,
|
||||
plot,
|
||||
renderLayers,
|
||||
renderBoard,
|
||||
stringifySvg
|
||||
} from '@tracespace/core'
|
||||
import fs from 'fs'
|
||||
|
||||
export const gerberToSvg = async gerberURL => {
|
||||
const data = await fetch(gerberURL).then(res => res.arrayBuffer())
|
||||
const files = []
|
||||
const zip = new JSZip()
|
||||
|
||||
const zippedData = await new Promise((resolve, _reject) => {
|
||||
zip.loadAsync(data).then(resolve, e => {
|
||||
console.error(e)
|
||||
resolve({
|
||||
files: {} // TODO: actually handle this error (bad or nonexistent gerber.zip)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const allowedExtensions = [
|
||||
'gbr', // gerber
|
||||
'drl', // drillfile
|
||||
'gko', // gerber board outline
|
||||
'gbl', // gerber bottom layer
|
||||
'gbp', // gerber bottom paste
|
||||
'gbs', // gerber bottom solder mask
|
||||
'gbo', // gerber bottom silk
|
||||
'gtl', // gerber top layer
|
||||
'gto', // gerber top silk
|
||||
'gts' // gerber top soldermask
|
||||
]
|
||||
const unzipJobs = Object.entries(zippedData.files).map(
|
||||
async ([filename, file]) => {
|
||||
const extension = filename.split('.').pop().toLowerCase()
|
||||
if (allowedExtensions.includes(extension)) {
|
||||
const filePath = `/tmp/${filename}`
|
||||
await new Promise((resolve, _reject) => {
|
||||
file.async('uint8array').then(function (fileData) {
|
||||
fs.writeFileSync(filePath, fileData)
|
||||
files.push(filePath)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
await Promise.all(unzipJobs)
|
||||
|
||||
let readResult
|
||||
try {
|
||||
readResult = await read(files)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return {}
|
||||
}
|
||||
const plotResult = plot(readResult)
|
||||
const renderLayersResult = renderLayers(plotResult)
|
||||
const renderBoardResult = renderBoard(renderLayersResult)
|
||||
for (const file of files) {
|
||||
if (fs.existsSync(file)) {
|
||||
fs.unlinkSync(file)
|
||||
}
|
||||
}
|
||||
return {
|
||||
top: stringifySvg(renderBoardResult.top),
|
||||
bottom: stringifySvg(renderBoardResult.bottom)
|
||||
// all: stringifySvg(renderLayersResult)
|
||||
}
|
||||
}
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { file, format } = req.query
|
||||
if (!file) {
|
||||
return res.status(400).json({ status: 400, error: 'Must provide file' })
|
||||
}
|
||||
// ensure valid file url is included
|
||||
const url = new URL(decodeURI(file))
|
||||
const svg = await gerberToSvg(url)
|
||||
if (format === 'top') {
|
||||
res.contentType('image/svg')
|
||||
return res.status(200).send(svg.top)
|
||||
}
|
||||
if (format === 'json') return res.status(200).json(svg)
|
||||
|
||||
return res.status(200).json(svg)
|
||||
}
|
||||
// test me with: curl http://localhost:3000/api/board/svg/https%3A%2F%2Fgithub.com%2Fhackclub%2FOnBoard%2Fraw%2Fmain%2Fprojects%2F2_Switch_Keyboard%2Fgerber.zip
|
||||
17
pages/api/board/svg/[board_url]/top.js
Normal file
17
pages/api/board/svg/[board_url]/top.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// test me with: curl http://localhost:3000/api/board/svg/https%3A%2F%2Fgithub.com%2Fhackclub%2FOnBoard%2Fraw%2Fmain%2Fprojects%2F2_Switch_Keyboard%2Fgerber.zip/top
|
||||
|
||||
import { gerberToSvg } from '.'
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { board_url } = req.query
|
||||
if (!board_url) {
|
||||
return res.status(404).json({ status: 404, error: 'Must provide file' })
|
||||
}
|
||||
// ensure valid file url is included
|
||||
const parsed_url = new URL(decodeURI(board_url))
|
||||
if (!parsed_url) {
|
||||
return res.status(404).json({ status: 404, error: 'Invalid file' })
|
||||
}
|
||||
const svg = await gerberToSvg(parsed_url)
|
||||
return res.status(200).send(svg.top)
|
||||
}
|
||||
260
pages/onboard/board/[slug].js
Normal file
260
pages/onboard/board/[slug].js
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
import { Box, Button, Flex, Grid, 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
|
||||
|
||||
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)
|
||||
}, [])
|
||||
|
||||
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
|
||||
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'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: 'copyUltra',
|
||||
mx: 'auto',
|
||||
px: 3
|
||||
}}
|
||||
>
|
||||
{
|
||||
// two-column layout - image on left, title + desc on right
|
||||
}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'grid',
|
||||
gap: 4,
|
||||
gridTemplateColumns: ['1fr', 'repeat(2, 1fr)'],
|
||||
color: 'black'
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={project.image}
|
||||
alt={project.project_name}
|
||||
sx={{
|
||||
borderRadius: 8,
|
||||
boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)'
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<Heading as="h2" variant="title" sx={{ textAlign: 'left' }}>
|
||||
{project.project_name}
|
||||
</Heading>
|
||||
<Text as="p" variant="subtitle" sx={{ textAlign: 'left' }}>
|
||||
{project.maker_name ? `by ${project.maker_name}` : ''}{' '}
|
||||
{project.slack_handle ? `(${project.slack_handle})` : ''}
|
||||
</Text>
|
||||
<Link
|
||||
href={`https://github.com/hackclub/OnBoard/blob/main/projects/${project.project_name}/`}
|
||||
sx={{
|
||||
textDecoration: 'none',
|
||||
color: 'black',
|
||||
':hover': {
|
||||
color: 'primary'
|
||||
},
|
||||
textAlign: 'left'
|
||||
}}
|
||||
>
|
||||
View on GitHub
|
||||
</Link>
|
||||
{/* custom innerHTML */}
|
||||
<Box
|
||||
sx={{
|
||||
textAlign: 'left'
|
||||
}}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
// render with remark to parse markdown
|
||||
remark()
|
||||
.use(html)
|
||||
.processSync(project.description)
|
||||
.toString()
|
||||
.replaceAll('h4', 'p')
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}))
|
||||
return {
|
||||
paths,
|
||||
fallback: 'blocking'
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStaticProps(context) {
|
||||
let name = context.params.slug
|
||||
let project = await FetchProject(name)
|
||||
return {
|
||||
props: {
|
||||
projectObj: project
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default BoardPage
|
||||
318
pages/onboard/gallery/[page].js
Normal file
318
pages/onboard/gallery/[page].js
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
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 pcbStackup from "pcb-stackup";
|
||||
import JSZip from "jszip";
|
||||
import JSZipUtils from "jszip-utils";*/
|
||||
|
||||
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()
|
||||
}, [])*/
|
||||
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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/*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 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))
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
projects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
const paths = Array(pages)
|
||||
.fill()
|
||||
.map((_, i) => ({ params: { page: (i + 1).toString() } }))
|
||||
return { paths, fallback: false }
|
||||
}
|
||||
|
||||
export default GalleryPage
|
||||
Loading…
Add table
Reference in a new issue