From f5cce0c24e3953ce0af00f8a9068dd582a34ab9b Mon Sep 17 00:00:00 2001 From: James Grugett Date: Mon, 25 Apr 2022 22:30:49 -0400 Subject: [PATCH] Listen for charity txns --- web/hooks/use-charity-txns.ts | 13 +++++++++ web/lib/firebase/txns.ts | 23 +++++++++++++++ web/pages/charity/[charitySlug].tsx | 45 ++++++++++++++++++----------- 3 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 web/hooks/use-charity-txns.ts create mode 100644 web/lib/firebase/txns.ts diff --git a/web/hooks/use-charity-txns.ts b/web/hooks/use-charity-txns.ts new file mode 100644 index 00000000..5636e720 --- /dev/null +++ b/web/hooks/use-charity-txns.ts @@ -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([]) + + useEffect(() => { + return listenForCharityTxns(charityId, setTxns) + }, [charityId]) + + return txns +} diff --git a/web/lib/firebase/txns.ts b/web/lib/firebase/txns.ts new file mode 100644 index 00000000..7d8a1dfb --- /dev/null +++ b/web/lib/firebase/txns.ts @@ -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(getCharityQuery(charityId), setTxns) +} diff --git a/web/pages/charity/[charitySlug].tsx b/web/pages/charity/[charitySlug].tsx index 87f3f157..c6997b9e 100644 --- a/web/pages/charity/[charitySlug].tsx +++ b/web/pages/charity/[charitySlug].tsx @@ -1,3 +1,4 @@ +import _ from 'lodash' import clsx from 'clsx' import { useEffect, useRef, useState } from 'react' import { Col } from '../../components/layout/col' @@ -13,6 +14,7 @@ import { transact } from '../../lib/firebase/api-call' import { charities, Charity } from '../../../common/charity' import { useRouter } from 'next/router' import Custom404 from '../404' +import { useCharityTxns } from '../../hooks/use-charity-txns' const manaToUSD = (mana: number) => (mana / 100).toLocaleString('en-US', { style: 'currency', currency: 'USD' }) @@ -21,9 +23,7 @@ export default function CharityPageWrapper() { const router = useRouter() const { charitySlug } = router.query as { charitySlug: string } - const charity = charities.find( - (c) => c.slug.toLowerCase() === charitySlug?.toLowerCase() - ) + const charity = charities.find((c) => c.slug === charitySlug?.toLowerCase()) if (!router.isReady) return <> if (!charity) { return @@ -38,15 +38,23 @@ function CharityPage(props: { charity: Charity }) { // TODO: why not just useUser inside Donation Box rather than passing in? const user = useUser() + const txns = useCharityTxns(charity.id) + const totalRaised = _.sumBy(txns, (txn) => txn.amount) + return ( - }> + }> - - + <Col className="max-w-2xl rounded bg-white px-8 py-6"> + <Title className="!mt-0" text={name} /> {/* TODO: donations over time chart */} <Row className="justify-between"> {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> <h2 className="mt-7 mb-2 text-xl text-indigo-700">About</h2> <Blurb text={blurb} /> @@ -93,13 +101,14 @@ function Details(props: { charity: Charity userDonated?: number numSupporters: number + totalRaised: number }) { - const { charity, userDonated, numSupporters } = props - const { raised, website } = charity + const { charity, userDonated, numSupporters, totalRaised } = props + const { website } = charity return ( <Col className="gap-1 text-right"> <div className="text-primary mb-2 text-4xl"> - {manaToUSD(raised ?? 0)} raised + {manaToUSD(totalRaised ?? 0)} raised </div> {userDonated && ( <div className="text-primary text-xl"> @@ -114,8 +123,8 @@ function Details(props: { ) } -function DonationBox(props: { user?: User | null }) { - const { user } = props +function DonationBox(props: { user?: User | null; charity: Charity }) { + const { user, charity } = props const [amount, setAmount] = useState<number | undefined>() const [isSubmitting, setIsSubmitting] = useState(false) const [error, setError] = useState<string | undefined>() @@ -123,6 +132,8 @@ function DonationBox(props: { user?: User | null }) { const donateDisabled = isSubmitting || !amount || error const onSubmit: React.FormEventHandler = async (e) => { + if (!user) return + e.preventDefault() setIsSubmitting(true) setError(undefined) @@ -130,12 +141,12 @@ function DonationBox(props: { user?: User | null }) { amount, // TODO hardcode in Manifold Markets official account. // 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', - description: `${user?.name} donated M$ ${amount} to wellgive`, - txnData: { - charityId: 'wellgive', // TODO fill in - }, + description: `${user.name} donated M$ ${amount} to ${charity.name}`, }) setIsSubmitting(false) setAmount(undefined)