Listen for charity txns
This commit is contained in:
parent
7aa7271c9f
commit
f5cce0c24e
13
web/hooks/use-charity-txns.ts
Normal file
13
web/hooks/use-charity-txns.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { Txn } from '../../common/txn'
|
||||||
|
import { listenForCharityTxns } from '../lib/firebase/txns'
|
||||||
|
|
||||||
|
export const useCharityTxns = (charityId: string) => {
|
||||||
|
const [txns, setTxns] = useState<Txn[]>([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return listenForCharityTxns(charityId, setTxns)
|
||||||
|
}, [charityId])
|
||||||
|
|
||||||
|
return txns
|
||||||
|
}
|
23
web/lib/firebase/txns.ts
Normal file
23
web/lib/firebase/txns.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { collection, query, where, orderBy } from 'firebase/firestore'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { Txn } from '../../../common/txn'
|
||||||
|
|
||||||
|
import { db } from './init'
|
||||||
|
import { listenForValues } from './utils'
|
||||||
|
|
||||||
|
const txnCollection = collection(db, 'txns')
|
||||||
|
|
||||||
|
const getCharityQuery = (charityId: string) =>
|
||||||
|
query(
|
||||||
|
txnCollection,
|
||||||
|
where('toType', '==', 'charity'),
|
||||||
|
where('toId', '==', charityId),
|
||||||
|
orderBy('createdTime', 'desc')
|
||||||
|
)
|
||||||
|
|
||||||
|
export function listenForCharityTxns(
|
||||||
|
charityId: string,
|
||||||
|
setTxns: (txns: Txn[]) => void
|
||||||
|
) {
|
||||||
|
return listenForValues<Txn>(getCharityQuery(charityId), setTxns)
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import _ from 'lodash'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { Col } from '../../components/layout/col'
|
import { Col } from '../../components/layout/col'
|
||||||
|
@ -13,6 +14,7 @@ import { transact } from '../../lib/firebase/api-call'
|
||||||
import { charities, Charity } from '../../../common/charity'
|
import { charities, Charity } from '../../../common/charity'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import Custom404 from '../404'
|
import Custom404 from '../404'
|
||||||
|
import { useCharityTxns } from '../../hooks/use-charity-txns'
|
||||||
|
|
||||||
const manaToUSD = (mana: number) =>
|
const manaToUSD = (mana: number) =>
|
||||||
(mana / 100).toLocaleString('en-US', { style: 'currency', currency: 'USD' })
|
(mana / 100).toLocaleString('en-US', { style: 'currency', currency: 'USD' })
|
||||||
|
@ -21,9 +23,7 @@ export default function CharityPageWrapper() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { charitySlug } = router.query as { charitySlug: string }
|
const { charitySlug } = router.query as { charitySlug: string }
|
||||||
|
|
||||||
const charity = charities.find(
|
const charity = charities.find((c) => c.slug === charitySlug?.toLowerCase())
|
||||||
(c) => c.slug.toLowerCase() === charitySlug?.toLowerCase()
|
|
||||||
)
|
|
||||||
if (!router.isReady) return <></>
|
if (!router.isReady) return <></>
|
||||||
if (!charity) {
|
if (!charity) {
|
||||||
return <Custom404 />
|
return <Custom404 />
|
||||||
|
@ -38,15 +38,23 @@ function CharityPage(props: { charity: Charity }) {
|
||||||
// TODO: why not just useUser inside Donation Box rather than passing in?
|
// TODO: why not just useUser inside Donation Box rather than passing in?
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
|
||||||
|
const txns = useCharityTxns(charity.id)
|
||||||
|
const totalRaised = _.sumBy(txns, (txn) => txn.amount)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page rightSidebar={<DonationBox user={user} />}>
|
<Page rightSidebar={<DonationBox user={user} charity={charity} />}>
|
||||||
<Col className="mx-1 w-full items-center sm:px-0">
|
<Col className="mx-1 w-full items-center sm:px-0">
|
||||||
<Col className="max-w-2xl rounded bg-white px-8">
|
<Col className="max-w-2xl rounded bg-white px-8 py-6">
|
||||||
<Title text={name} />
|
<Title className="!mt-0" text={name} />
|
||||||
{/* TODO: donations over time chart */}
|
{/* TODO: donations over time chart */}
|
||||||
<Row className="justify-between">
|
<Row className="justify-between">
|
||||||
{photo && <img src={photo} alt="" className="w-40 rounded-2xl" />}
|
{photo && <img src={photo} alt="" className="w-40 rounded-2xl" />}
|
||||||
<Details charity={charity} userDonated={4} numSupporters={1} />
|
<Details
|
||||||
|
charity={charity}
|
||||||
|
userDonated={4}
|
||||||
|
numSupporters={1}
|
||||||
|
totalRaised={totalRaised}
|
||||||
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
<h2 className="mt-7 mb-2 text-xl text-indigo-700">About</h2>
|
<h2 className="mt-7 mb-2 text-xl text-indigo-700">About</h2>
|
||||||
<Blurb text={blurb} />
|
<Blurb text={blurb} />
|
||||||
|
@ -93,13 +101,14 @@ function Details(props: {
|
||||||
charity: Charity
|
charity: Charity
|
||||||
userDonated?: number
|
userDonated?: number
|
||||||
numSupporters: number
|
numSupporters: number
|
||||||
|
totalRaised: number
|
||||||
}) {
|
}) {
|
||||||
const { charity, userDonated, numSupporters } = props
|
const { charity, userDonated, numSupporters, totalRaised } = props
|
||||||
const { raised, website } = charity
|
const { website } = charity
|
||||||
return (
|
return (
|
||||||
<Col className="gap-1 text-right">
|
<Col className="gap-1 text-right">
|
||||||
<div className="text-primary mb-2 text-4xl">
|
<div className="text-primary mb-2 text-4xl">
|
||||||
{manaToUSD(raised ?? 0)} raised
|
{manaToUSD(totalRaised ?? 0)} raised
|
||||||
</div>
|
</div>
|
||||||
{userDonated && (
|
{userDonated && (
|
||||||
<div className="text-primary text-xl">
|
<div className="text-primary text-xl">
|
||||||
|
@ -114,8 +123,8 @@ function Details(props: {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function DonationBox(props: { user?: User | null }) {
|
function DonationBox(props: { user?: User | null; charity: Charity }) {
|
||||||
const { user } = props
|
const { user, charity } = props
|
||||||
const [amount, setAmount] = useState<number | undefined>()
|
const [amount, setAmount] = useState<number | undefined>()
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
const [error, setError] = useState<string | undefined>()
|
const [error, setError] = useState<string | undefined>()
|
||||||
|
@ -123,6 +132,8 @@ function DonationBox(props: { user?: User | null }) {
|
||||||
const donateDisabled = isSubmitting || !amount || error
|
const donateDisabled = isSubmitting || !amount || error
|
||||||
|
|
||||||
const onSubmit: React.FormEventHandler = async (e) => {
|
const onSubmit: React.FormEventHandler = async (e) => {
|
||||||
|
if (!user) return
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
setIsSubmitting(true)
|
setIsSubmitting(true)
|
||||||
setError(undefined)
|
setError(undefined)
|
||||||
|
@ -130,12 +141,12 @@ function DonationBox(props: { user?: User | null }) {
|
||||||
amount,
|
amount,
|
||||||
// TODO hardcode in Manifold Markets official account.
|
// TODO hardcode in Manifold Markets official account.
|
||||||
// Or should we just have it go into a void?
|
// Or should we just have it go into a void?
|
||||||
toId: 'igi2zGXsfxYPgB0DJTXVJVmwCOr2', // akrolsmir@gmail in Dev env
|
fromId: user.id,
|
||||||
|
fromType: 'user',
|
||||||
|
toId: charity.id,
|
||||||
|
toType: 'charity',
|
||||||
category: 'TO_CHARITY',
|
category: 'TO_CHARITY',
|
||||||
description: `${user?.name} donated M$ ${amount} to wellgive`,
|
description: `${user.name} donated M$ ${amount} to ${charity.name}`,
|
||||||
txnData: {
|
|
||||||
charityId: 'wellgive', // TODO fill in
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
setIsSubmitting(false)
|
setIsSubmitting(false)
|
||||||
setAmount(undefined)
|
setAmount(undefined)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user