manifold/web/pages/charity/[...slugs]/index.tsx

173 lines
5.4 KiB
TypeScript
Raw Normal View History

import clsx from 'clsx'
import { useEffect, useMemo, useRef, useState } from 'react'
import { Charity } from '../../../components/charity/charity-card'
import { Col } from '../../../components/layout/col'
import { Row } from '../../../components/layout/row'
import { Page } from '../../../components/page'
import { Title } from '../../../components/title'
import { BuyAmountInput } from '../../../components/amount-input'
import { Spacer } from '../../../components/layout/spacer'
import { User } from '../../../../common/user'
import { useUser } from '../../../hooks/use-user'
import { Linkify } from '../../../components/linkify'
2022-04-21 09:47:13 +00:00
const manaToUSD = (mana: number) =>
(mana / 100).toLocaleString('en-US', { style: 'currency', currency: 'USD' })
// TODO: replace with props
const data: Charity = {
name: 'QRI',
slug: 'qri',
website: 'https://www.google.com',
ein: '123456789',
photo: 'https://placekitten.com/200/200',
blurb:
'Lorem Ipsum is simply dummy text of Lorem Ipsum is simply dummy text ofLorem Ipsum is simply dummy text ofLorem Ipsum is simply dLorem Ipsum is simply dummy text ofLorem Ipsum is simply dummy text ofLorem Ipsum is simply dummy text ofLorem Ipsum is simply dummy text ofLorem Ipsum is simply dummy text ofLorem Ipsum is simply dummy text ofLorem Ipsum is simply dummy text ofLorem Ipsum isLorem Ipsum is simply dummy text ofLorem Ipsum is simply dummy text of simply dummy text ofummy text of ',
raised: 23450,
}
export default function CharityPage() {
const { name, photo, blurb } = data
// TODO: why not just useUser inside Donation Box rather than passing in?
const user = useUser()
return (
<Page rightSidebar={<DonationBox user={user} />}>
<Col className="mx-1 w-full items-center sm:px-0">
<Col className="max-w-2xl rounded bg-white px-8">
<Title text={name} />
{/* TODO: donations over time chart */}
<Row className="justify-between">
{photo && <img src={photo} alt="" className="w-40 rounded-2xl" />}
<Details userDonated={4} numSupporters={1} />
</Row>
<h2 className="mt-7 mb-2 text-xl text-indigo-700">About</h2>
<Blurb text={blurb} />
</Col>
</Col>
</Page>
)
}
function Blurb({ text }: { text: string }) {
const [open, setOpen] = useState(false)
// Calculate whether the full blurb is already shown
const ref = useRef<HTMLDivElement>(null)
const [hideExpander, setHideExpander] = useState(false)
useEffect(() => {
if (ref.current) {
setHideExpander(ref.current.scrollHeight <= ref.current.clientHeight)
}
}, [])
return (
<>
<div
className={clsx(' text-gray-500', !open && 'line-clamp-5')}
ref={ref}
>
{text}
</div>
<button
onClick={() => setOpen(!open)}
className={clsx(
'btn btn-link capitalize-none my-3 normal-case text-indigo-700',
hideExpander && 'hidden'
)}
>
{open ? 'Hide' : 'Read more'}
</button>
</>
)
}
function Details(props: { userDonated?: number; numSupporters: number }) {
const { userDonated, numSupporters } = props
const { raised, website } = data
return (
<Col className="gap-1 text-right">
2022-04-21 09:47:13 +00:00
<div className="text-primary mb-2 text-4xl">
{manaToUSD(raised)} raised
</div>
{userDonated && (
<div className="text-primary text-xl">
2022-04-21 09:47:13 +00:00
{manaToUSD(userDonated)} from you!
</div>
)}
{numSupporters > 0 && (
<div className="text-gray-500">{numSupporters} supporters</div>
)}
<Linkify text={website} />
</Col>
)
}
function DonationBox(props: { user?: User | null }) {
const { user } = props
const [amount, setAmount] = useState<number | undefined>()
const [isSubmitting, setIsSubmitting] = useState(false)
const [error, setError] = useState<string | undefined>()
const donateDisabled = isSubmitting || !amount || error
const onSubmit: React.FormEventHandler = async (e) => {
e.preventDefault()
setIsSubmitting(true)
setError(undefined)
// TODO await sending to db
await new Promise((resolve) => setTimeout(resolve, 1000))
setIsSubmitting(false)
setAmount(undefined)
}
return (
<div className="rounded-lg bg-white py-6 px-8 shadow-lg">
<div className="mb-6 text-2xl text-gray-700">Donate</div>
<form onSubmit={onSubmit}>
<label
className="mb-2 block text-sm text-gray-500"
htmlFor="donate-input"
>
Amount
</label>
<BuyAmountInput
inputClassName="w-full donate-input"
amount={amount}
onChange={setAmount}
error={error}
setError={setError}
/>
<Col className="mt-3 w-full gap-3">
<Row className="items-center justify-between text-sm">
<span className="text-gray-500">Conversion</span>
<span>
{amount || 0} Mana
<span className="mx-2"></span>
2022-04-21 09:47:13 +00:00
{manaToUSD(amount || 0)}
</span>
</Row>
{/* TODO: matching pool */}
</Col>
<Spacer h={8} />
{user && (
<button
type="submit"
className={clsx(
'btn w-full',
donateDisabled ? 'btn-disabled' : 'btn-primary',
isSubmitting && 'loading'
)}
>
Donate
</button>
)}
</form>
</div>
)
}