mirror of
https://github.com/cupcakearmy/fantus.git
synced 2025-09-05 21:50:40 +00:00
update, formatting and vercel
This commit is contained in:
264
screens/form.tsx
264
screens/form.tsx
@@ -1,153 +1,141 @@
|
||||
import React, { useState, useRef, useCallback } from 'react'
|
||||
import React, { useState, useCallback } from 'react'
|
||||
import { useForm } from 'formhero'
|
||||
import axios from 'axios'
|
||||
import getConfig from 'next/config'
|
||||
|
||||
const { RECAPTCHA_CLIENT } = getConfig().publicRuntimeConfig
|
||||
|
||||
const initial = {
|
||||
contact: '',
|
||||
description: '',
|
||||
}
|
||||
|
||||
const ab2str = (buf: any): string => {
|
||||
return String.fromCharCode.apply(null, buf);
|
||||
contact: '',
|
||||
description: '',
|
||||
}
|
||||
|
||||
const Form: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [message, setMessage] = useState({
|
||||
title: '',
|
||||
error: false,
|
||||
})
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [message, setMessage] = useState({
|
||||
title: '',
|
||||
error: false,
|
||||
})
|
||||
|
||||
const { field, form, setForm } = useForm(initial)
|
||||
const [files, setFiles] = useState([] as File[])
|
||||
const { field, form, setForm } = useForm(initial)
|
||||
const [files, setFiles] = useState([] as File[])
|
||||
|
||||
const _onFileChange = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setMessage({ title: '', error: false })
|
||||
const uploaded = Array.from(e.target.files || [])
|
||||
const _onFileChange = useCallback(
|
||||
async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setMessage({ title: '', error: false })
|
||||
const uploaded = Array.from(e.target.files || [])
|
||||
|
||||
const nonAudio = uploaded.find(file => !/^audio\//.test(file.type))
|
||||
if (nonAudio) {
|
||||
setMessage({
|
||||
title: `Error: ${nonAudio.name} You can only select audio files.`,
|
||||
error: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
setFiles([...files, ...uploaded])
|
||||
}, [files])
|
||||
|
||||
const _clear = useCallback(() => {
|
||||
setFiles([])
|
||||
}, [])
|
||||
|
||||
const _submit = useCallback((e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
if (loading) return
|
||||
|
||||
// @ts-ignore
|
||||
window.grecaptcha.ready(() => {
|
||||
// @ts-ignore
|
||||
window.grecaptcha.execute(RECAPTCHA_CLIENT, { action: 'homepage' }).then(function (token) {
|
||||
setLoading(true)
|
||||
setMessage({ title: '', error: false })
|
||||
|
||||
|
||||
const body = new FormData()
|
||||
|
||||
body.append('json', JSON.stringify({
|
||||
...form,
|
||||
token,
|
||||
}))
|
||||
|
||||
for (const file of files) {
|
||||
body.append(file.name, file)
|
||||
}
|
||||
|
||||
fetch('/api/form', {
|
||||
method: 'POST',
|
||||
body
|
||||
})
|
||||
.then(() => {
|
||||
setForm(initial)
|
||||
setFiles([])
|
||||
setMessage({ title: 'Uploaded 🚀', error: false })
|
||||
})
|
||||
.catch(() => setMessage({ title: 'Something went wrong 😥', error: true }))
|
||||
.finally(() => setLoading(false))
|
||||
})
|
||||
const nonAudio = uploaded.find((file) => !/^audio\//.test(file.type))
|
||||
if (nonAudio) {
|
||||
setMessage({
|
||||
title: `Error: ${nonAudio.name} You can only select audio files.`,
|
||||
error: true,
|
||||
})
|
||||
}, [form, files, loading])
|
||||
return
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={_submit}>
|
||||
<div className='body'>
|
||||
<h3 className='ma0 mb3'>submit track</h3>
|
||||
<label className='text'>
|
||||
<small>contact email</small>
|
||||
<input
|
||||
type='email'
|
||||
disabled={loading}
|
||||
placeholder={'me@example.org'}
|
||||
{...field('contact')}
|
||||
/>
|
||||
</label>
|
||||
<br />
|
||||
<label className='text'>
|
||||
<small>description</small>
|
||||
<textarea
|
||||
rows={2}
|
||||
placeholder='reference trakcs, comments, ...'
|
||||
disabled={loading}
|
||||
{...field('description')}
|
||||
/>
|
||||
</label>
|
||||
<br />
|
||||
<label className='file'>
|
||||
<input
|
||||
type='file'
|
||||
multiple
|
||||
disabled={loading}
|
||||
onChange={_onFileChange}
|
||||
/>
|
||||
upload tracks [max. 300MiB]
|
||||
</label>
|
||||
{files.length > 0 && (
|
||||
<div>
|
||||
<input onClick={_clear} type='button' value='clear all' />
|
||||
<ul>
|
||||
{files.map((file, i) => <li key={i}>
|
||||
{file.name}
|
||||
</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
<br />
|
||||
<input
|
||||
type='submit'
|
||||
value={loading ? 'uploading...' : 'submit'}
|
||||
disabled={loading}
|
||||
/>
|
||||
setFiles([...files, ...uploaded])
|
||||
},
|
||||
[files]
|
||||
)
|
||||
|
||||
{!!message.title && (
|
||||
<div>
|
||||
<br />
|
||||
<div className={`pa2 ba br1 ${message.error ? 'b--red' : 'b--light-blue'}`}>{message.title}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
const _clear = useCallback(() => {
|
||||
setFiles([])
|
||||
}, [])
|
||||
|
||||
<div className='grc'>
|
||||
This site is protected by reCAPTCHA and the Google
|
||||
<a href="https://policies.google.com/privacy">Privacy Policy</a> and
|
||||
<a href="https://policies.google.com/terms">Terms of Service</a> apply.
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
const _submit = useCallback(
|
||||
(e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
if (loading) return
|
||||
|
||||
// @ts-ignore
|
||||
window.grecaptcha.ready(() => {
|
||||
// @ts-ignore
|
||||
window.grecaptcha
|
||||
.execute(process.env.NEXT_PUBLIC_RECAPTCHA_CLIENT, { action: 'homepage' })
|
||||
.then(function (token: string) {
|
||||
setLoading(true)
|
||||
setMessage({ title: '', error: false })
|
||||
|
||||
const body = new FormData()
|
||||
|
||||
body.append(
|
||||
'json',
|
||||
JSON.stringify({
|
||||
...form,
|
||||
token,
|
||||
})
|
||||
)
|
||||
|
||||
for (const file of files) {
|
||||
body.append(file.name, file)
|
||||
}
|
||||
|
||||
fetch('/api/form', {
|
||||
method: 'POST',
|
||||
body,
|
||||
})
|
||||
.then(() => {
|
||||
setForm(initial)
|
||||
setFiles([])
|
||||
setMessage({ title: 'Uploaded 🚀', error: false })
|
||||
})
|
||||
.catch(() => setMessage({ title: 'Something went wrong 😥', error: true }))
|
||||
.finally(() => setLoading(false))
|
||||
})
|
||||
})
|
||||
},
|
||||
[form, files, loading]
|
||||
)
|
||||
|
||||
return (
|
||||
<form onSubmit={_submit}>
|
||||
<div className="body">
|
||||
<h3 className="ma0 mb3">submit track</h3>
|
||||
<label className="text">
|
||||
<small>contact email</small>
|
||||
<input type="email" disabled={loading} placeholder={'me@example.org'} {...field('contact')} />
|
||||
</label>
|
||||
<br />
|
||||
<label className="text">
|
||||
<small>description</small>
|
||||
<textarea
|
||||
rows={2}
|
||||
placeholder="reference trakcs, comments, ..."
|
||||
disabled={loading}
|
||||
{...field('description')}
|
||||
/>
|
||||
</label>
|
||||
<br />
|
||||
<label className="file">
|
||||
<input type="file" multiple disabled={loading} onChange={_onFileChange} />
|
||||
upload tracks [max. 300MiB]
|
||||
</label>
|
||||
{files.length > 0 && (
|
||||
<div>
|
||||
<input onClick={_clear} type="button" value="clear all" />
|
||||
<ul>
|
||||
{files.map((file, i) => (
|
||||
<li key={i}>{file.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
<br />
|
||||
<input type="submit" value={loading ? 'uploading...' : 'submit'} disabled={loading} />
|
||||
|
||||
{!!message.title && (
|
||||
<div>
|
||||
<br />
|
||||
<div className={`pa2 ba br1 ${message.error ? 'b--red' : 'b--light-blue'}`}>{message.title}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grc">
|
||||
This site is protected by reCAPTCHA and the Google
|
||||
<a href="https://policies.google.com/privacy">Privacy Policy</a> and
|
||||
<a href="https://policies.google.com/terms">Terms of Service</a> apply.
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
export default Form
|
||||
export default Form
|
||||
|
@@ -4,45 +4,48 @@ import Link from '../components/link'
|
||||
|
||||
import '../styles/menu.styl'
|
||||
|
||||
const HomeLink = () => <div className='home'>
|
||||
<Link href='/'>
|
||||
<div>fantus</div>
|
||||
const HomeLink = () => (
|
||||
<div className="home">
|
||||
<Link href="/">
|
||||
<div>fantus</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const Menu: React.FC = () => {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
const _close = useCallback(() => {
|
||||
setOpen(false)
|
||||
}, [])
|
||||
|
||||
const _close = useCallback(() => {
|
||||
setOpen(false)
|
||||
}, [])
|
||||
const _toggle = useCallback(() => {
|
||||
setOpen(!open)
|
||||
}, [open])
|
||||
|
||||
const _toggle = useCallback(() => {
|
||||
setOpen(!open)
|
||||
}, [open])
|
||||
return (
|
||||
<nav className="main flex justify-between items-center">
|
||||
<HomeLink />
|
||||
|
||||
return <nav className='main flex justify-between items-center'>
|
||||
<img id="icon" src="/assets/menu.svg" onClick={_toggle} />
|
||||
|
||||
<div className={`links flex ${open ? 'open' : ''}`.trim()} onClick={_close}>
|
||||
<HomeLink />
|
||||
|
||||
<img id='icon' src='/assets/menu.svg' onClick={_toggle} />
|
||||
|
||||
<div className={`links flex ${open ? 'open' : ''}`.trim()} onClick={_close}>
|
||||
<HomeLink />
|
||||
<Link href='/works'>
|
||||
<div>works</div>
|
||||
</Link>
|
||||
<Link href='/sets'>
|
||||
<div>sets</div>
|
||||
</Link>
|
||||
<Link href='/mastering'>
|
||||
<div>mastering</div>
|
||||
</Link>
|
||||
<Link href='/about'>
|
||||
<div>about</div>
|
||||
</Link>
|
||||
</div>
|
||||
<Link href="/works">
|
||||
<div>works</div>
|
||||
</Link>
|
||||
<Link href="/sets">
|
||||
<div>sets</div>
|
||||
</Link>
|
||||
<Link href="/mastering">
|
||||
<div>mastering</div>
|
||||
</Link>
|
||||
<Link href="/about">
|
||||
<div>about</div>
|
||||
</Link>
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
export default Menu
|
||||
export default Menu
|
||||
|
Reference in New Issue
Block a user