import clsx from 'clsx' import _ from 'lodash' import { useRouter } from 'next/router' import { useEffect, useState } from 'react' import { Fold } from '../../common/fold' import { parseWordsAsTags } from '../../common/util/parse' import { ConfirmationButton } from '../components/confirmation-button' import { Col } from '../components/layout/col' import { Row } from '../components/layout/row' import { Spacer } from '../components/layout/spacer' import { Page } from '../components/page' import { SiteLink } from '../components/site-link' import { TagsList } from '../components/tags-list' import { Title } from '../components/title' import { UserLink } from '../components/user-page' import { useFolds } from '../hooks/use-fold' import { useUser } from '../hooks/use-user' import { createFold } from '../lib/firebase/api-call' import { foldPath, listAllFolds } from '../lib/firebase/folds' import { getUser, User } from '../lib/firebase/users' import { toCamelCase } from '../lib/util/format' export async function getStaticProps() { const folds = await listAllFolds().catch((_) => []) const curators = await Promise.all( folds.map((fold) => getUser(fold.curatorId)) ) const curatorsDict = _.fromPairs( curators.map((curator) => [curator.id, curator]) ) return { props: { folds, curatorsDict, }, revalidate: 60, // regenerate after a minute } } export default function Folds(props: { folds: Fold[] curatorsDict: _.Dictionary }) { const [curatorsDict, setCuratorsDict] = useState(props.curatorsDict) const folds = useFolds() ?? props.folds const user = useUser() useEffect(() => { // Load User object for curator of new Folds. const newFolds = folds.filter(({ curatorId }) => !curatorsDict[curatorId]) if (newFolds.length > 0) { Promise.all(newFolds.map(({ curatorId }) => getUser(curatorId))).then( (newUsers) => { const newUsersDict = _.fromPairs( newUsers.map((user) => [user.id, user]) ) setCuratorsDict({ ...curatorsDict, ...newUsersDict }) } ) } }) return ( {user && <CreateFoldButton />} </Row> <Col className="gap-4"> {folds.map((fold) => ( <Row key={fold.id} className="items-center gap-2"> <SiteLink href={foldPath(fold)}>{fold.name}</SiteLink> <div /> <div className="text-sm text-gray-500">12 followers</div> <div className="text-gray-500">•</div> <Row> <div className="text-sm text-gray-500 mr-1">Curated by</div> <UserLink className="text-sm text-neutral" name={curatorsDict[fold.curatorId]?.name ?? ''} username={curatorsDict[fold.curatorId]?.username ?? ''} /> </Row> </Row> ))} </Col> </Col> </Col> </Page> ) } function CreateFoldButton() { const [name, setName] = useState('') const [tags, setTags] = useState('') const [isSubmitting, setIsSubmitting] = useState(false) const router = useRouter() const updateName = (newName: string) => { setName(newName) setTags(toCamelCase(newName)) } const onSubmit = async () => { setIsSubmitting(true) const result = await createFold({ name, tags: parseWordsAsTags(tags), }).then((r) => r.data || {}) if (result.fold) { await router.push(foldPath(result.fold)).catch((e) => { console.log(e) setIsSubmitting(false) }) } else { console.log(result.status, result.message) setIsSubmitting(false) } } return ( <ConfirmationButton id="create-fold" openModelBtn={{ label: 'Create a fold', className: clsx( isSubmitting ? 'loading btn-disabled' : 'btn-primary', 'btn-sm' ), }} submitBtn={{ label: 'Create', className: clsx(name && tags ? 'btn-primary' : 'btn-disabled'), }} onSubmit={onSubmit} > <Title className="!mt-0" text="Create a fold" /> <Col className="text-gray-500 gap-1"> <div>A fold is a view of markets that match one or more tags.</div> <div>You can further exclude individual markets.</div> </Col> <Spacer h={4} /> <div> <div className="form-control w-full"> <label className="label"> <span className="mb-1">Fold name</span> </label> <input placeholder="Your fold name" className="input input-bordered resize-none" disabled={isSubmitting} value={name} onChange={(e) => updateName(e.target.value || '')} /> </div> <Spacer h={4} /> {/* <div className="form-control w-full"> <label className="label"> <span className="mb-1">Tags</span> </label> <input placeholder="Politics, Economics, Rationality" className="input input-bordered resize-none" disabled={isSubmitting} value={tags} onChange={(e) => setTags(e.target.value || '')} /> </div> */} {tags && ( <> <label className="label"> <span className="mb-1">Primary tag</span> </label> <TagsList tags={parseWordsAsTags(tags).map((tag) => `#${tag}`)} noLink /> </> )} </div> </ConfirmationButton> ) }