pagination + a bit better board pages + working previews !!!!!!!!

This commit is contained in:
snwy 2024-03-26 13:13:22 -04:00
parent 71c35a9c4c
commit e7ed2aa421
4 changed files with 390 additions and 296 deletions

View file

@ -2,60 +2,82 @@ import JSZip from "jszip";
import {read, plot, renderLayers, renderBoard, stringifySvg} from '@tracespace/core'
import fs from 'fs'
export const GenerateSVG = async (zipFile) => {
async function asyncLoadZip(data) {
return await new Promise((resolve, reject) => {
JSZip.loadAsync(data).then(function (zip) {
resolve(zip)
}, function (e) {
reject(e)
});
})
}
async function asyncUnzip (file) {
const zip = new JSZip();
return await new Promise((resolve, reject) => {
zip.file(file).async("uint8array").then(function (data) {
resolve(data)
}, function (e) {
reject(e)
});
})
}
export const GenerateSVG = async (zipFile) => {
//const zip = new JSZip();
const data = await fetch(zipFile)
.then((res) => res.arrayBuffer());
let files = [];
try { await (zip.loadAsync(data).then(function (zip) {
Object.keys(zip.files).forEach(function (filename) {
zip.files[filename].async('uint8array').then(function (fileData) {
filename = filename.replace(/\//g, '_');
const extension = filename.split('.').pop().toLowerCase();
if (extension === 'gbr' || // gerber
extension === 'drl' || // drillfile
extension === 'gko' || // gerber board outline
extension === 'gbl' || // gerber bottom layer
extension === 'gbp' || // gerber bottom paste
extension === 'gbs' || // gerber bottom solder mask
extension === 'gbo' || // gerber bottom silk
extension === 'gtl' || // gerber top layer
extension === 'gto' || // gerber top silk
extension === 'gts' // gerber top soldermask
) {
console.log(filename)
files.push(filename);
fs.writeFileSync(filename, fileData);
}
});
})
})) } catch (e) {
if(!fs.existsSync('gerber'))
fs.mkdirSync('gerber');
try {
const zip = await asyncLoadZip(data)
await new Promise(async (resolve, reject) => {
for(let filename of Object.keys(zip.files)) {
await (zip.files[filename].async('uint8array').then(function (fileData) {
filename = filename.replace(/\//g, '_');
const extension = filename.split('.').pop().toLowerCase();
if (extension === 'gbr' || // gerber
extension === 'drl' || // drillfile
extension === 'gko' || // gerber board outline
extension === 'gbl' || // gerber bottom layer
extension === 'gbp' || // gerber bottom paste
extension === 'gbs' || // gerber bottom solder mask
extension === 'gbo' || // gerber bottom silk
extension === 'gtl' || // gerber top layer
extension === 'gto' || // gerber top silk
extension === 'gts' // gerber top soldermask
) {
//console.log('gerber/' + filename)
files.push('gerber/' + filename);
if(!fs.existsSync('gerber/' + filename))
fs.writeFileSync('gerber/' + filename, fileData);
}
}));
//console.log(zip.files[filename])
}
resolve();
});
} catch (e) {
console.error(e)
fs.rmdirSync('gerber', { recursive: true });
return {}
}
// wait for files to be written
await new Promise((resolve) => {
setTimeout(resolve, 1000); // very hacky but works!!!!
});
console.log(files)
//console.log(files)
let readResult;
try {
readResult = await read(files)
} catch (e) {
console.error(e)
fs.rmdirSync('gerber', { recursive: true });
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);
}*/
fs.rmdirSync('gerber', { recursive: true });
return {
top: stringifySvg(renderBoardResult.top),

View file

@ -1,4 +1,4 @@
import {Box, Button, Flex, Grid, Heading, Image, Text} from 'theme-ui'
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'
@ -183,11 +183,29 @@ const BoardPage = ({ projectObj }) => {
justifyContent: 'center'
}}
>
<Heading as="h2" variant="title" sx={{ textAlign: '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()

View file

@ -1,260 +0,0 @@
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 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 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>
</>
)
}
/*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() {
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)*/
// 100ms delay to avoid rate limiting
await new Promise(resolve => setTimeout(resolve, 100))
return FetchProject(project.name)
})
let projects = await Promise.all(projectData)
return {
props: {
projects
}
}
}
export default GalleryPage

View file

@ -0,0 +1,314 @@
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