Fast fold following (#51)

* fast follow folds

* FastFoldFollowing component on homepage
This commit is contained in:
mantikoros 2022-02-19 17:17:36 -06:00 committed by GitHub
parent 693652935d
commit 13727bb19f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 144 additions and 36 deletions

View File

@ -0,0 +1,107 @@
import clsx from 'clsx'
import { useState } from 'react'
import { SearchIcon } from '@heroicons/react/outline'
import { User } from '../../common/user'
import { followFoldFromSlug, unfollowFoldFromSlug } from '../lib/firebase/folds'
import { Row } from './layout/row'
import { Spacer } from './layout/spacer'
function FollowFoldButton(props: {
fold: { slug: string; name: string }
user: User | null | undefined
isFollowed?: boolean
}) {
const { fold, user, isFollowed } = props
const { slug, name } = fold
const [followed, setFollowed] = useState(isFollowed)
const onClick = async () => {
if (followed) {
if (user) await unfollowFoldFromSlug(slug, user.id)
setFollowed(false)
} else {
if (user) await followFoldFromSlug(slug, user.id)
setFollowed(true)
}
}
return (
<div
className={clsx(
'rounded-full border-2 px-4 py-1 shadow-md',
'cursor-pointer',
followed ? 'bg-gray-300 border-gray-300' : 'bg-white'
)}
onClick={onClick}
>
<span className="text-sm text-gray-500">{name}</span>
</div>
)
}
function FollowFolds(props: {
folds: { slug: string; name: string }[]
followedFoldSlugs: string[]
noLabel?: boolean
className?: string
user: User | null | undefined
}) {
const { folds, noLabel, className, user, followedFoldSlugs } = props
return (
<Row className={clsx('flex-wrap items-center gap-2', className)}>
{folds.length > 0 && (
<>
{!noLabel && <div className="mr-1 text-gray-500">Communities</div>}
{folds.map((fold) => (
<FollowFoldButton
key={fold.slug + followedFoldSlugs.length}
user={user}
fold={fold}
isFollowed={followedFoldSlugs.includes(fold.slug)}
/>
))}
</>
)}
</Row>
)
}
export const FastFoldFollowing = (props: {
followedFoldSlugs: string[]
user: User | null | undefined
}) => {
const { followedFoldSlugs, user } = props
return (
<>
<Row className="mx-3 mb-3 items-center gap-2 text-sm text-gray-800">
<SearchIcon className="inline h-5 w-5" aria-hidden="true" />
Personalize your feed click on a community to follow
</Row>
<FollowFolds
className="mx-2"
noLabel
user={user}
followedFoldSlugs={followedFoldSlugs}
folds={[
{ name: 'Politics', slug: 'politics' },
{ name: 'Crypto', slug: 'crypto' },
{ name: 'Sports', slug: 'sports' },
{ name: 'Science', slug: 'science' },
{ name: 'Covid', slug: 'covid' },
{ name: 'AI', slug: 'ai' },
{
name: 'Manifold Markets',
slug: 'manifold-markets',
},
]}
/>
<Spacer h={10} />
</>
)
}

View File

@ -11,7 +11,7 @@ export function FollowFoldButton(props: { fold: Fold; className?: string }) {
const following = useFollowingFold(fold, user)
const onFollow = () => {
if (user) followFold(fold, user)
if (user) followFold(fold.id, user.id)
}
const onUnfollow = () => {

View File

@ -1,4 +1,5 @@
import clsx from 'clsx'
import { Row } from './layout/row'
import { SiteLink } from './site-link'
@ -47,6 +48,7 @@ export function TagsList(props: {
export function FoldTag(props: { fold: { slug: string; name: string } }) {
const { fold } = props
const { slug, name } = fold
return (
<SiteLink href={`/fold/${slug}`} className="flex items-center">
<div

View File

@ -118,9 +118,9 @@ export function listenForFold(
return listenForValue(doc(foldCollection, foldId), setFold)
}
export function followFold(fold: Fold, user: User) {
const followDoc = doc(foldCollection, fold.id, 'followers', user.id)
return setDoc(followDoc, { userId: user.id })
export function followFold(foldId: string, userId: string) {
const followDoc = doc(foldCollection, foldId, 'followers', userId)
return setDoc(followDoc, { userId })
}
export function unfollowFold(fold: Fold, user: User) {
@ -128,6 +128,26 @@ export function unfollowFold(fold: Fold, user: User) {
return deleteDoc(followDoc)
}
export async function followFoldFromSlug(slug: string, userId: string) {
const snap = await getDocs(query(foldCollection, where('slug', '==', slug)))
if (snap.empty) return undefined
const foldDoc = snap.docs[0]
const followDoc = doc(foldDoc.ref, 'followers', userId)
return setDoc(followDoc, { userId })
}
export async function unfollowFoldFromSlug(slug: string, userId: string) {
const snap = await getDocs(query(foldCollection, where('slug', '==', slug)))
if (snap.empty) return undefined
const foldDoc = snap.docs[0]
const followDoc = doc(foldDoc.ref, 'followers', userId)
return deleteDoc(followDoc)
}
export function listenForFollow(
fold: Fold,
user: User,

View File

@ -73,7 +73,8 @@ export default function Folds(props: {
<div className="mb-6 text-gray-500">
Communities on Manifold are centered around a collection of
markets.
markets. Follow a community to personalize your feed and receive
relevant updates.
</div>
</Col>

View File

@ -17,12 +17,10 @@ import { Fold } from '../../common/fold'
import { filterDefined } from '../../common/util/array'
import { useUserBetContracts } from '../hooks/use-user-bets'
import { LoadingIndicator } from '../components/loading-indicator'
import { FoldTagList } from '../components/tags-list'
import { SearchIcon } from '@heroicons/react/outline'
import { Row } from '../components/layout/row'
import { SparklesIcon } from '@heroicons/react/solid'
import { useFollowedFolds } from '../hooks/use-fold'
import { SiteLink } from '../components/site-link'
import { FastFoldFollowing } from '../components/fast-fold-following'
export async function getStaticProps() {
let [contracts, folds] = await Promise.all([
@ -116,39 +114,19 @@ const Home = (props: {
<Col className="w-full max-w-3xl">
<FeedCreate user={user ?? undefined} />
<Spacer h={6} />
<Row className="mx-3 mb-3 items-center gap-2 text-sm text-gray-800">
<SearchIcon className="inline h-5 w-5" aria-hidden="true" />
Explore our communities
</Row>
<FoldTagList
className="mx-2"
noLabel
folds={[
{ name: 'Politics', slug: 'politics' },
{ name: 'Crypto', slug: 'crypto' },
{ name: 'Sports', slug: 'sports' },
{ name: 'Science', slug: 'science' },
{ name: 'Covid', slug: 'covid' },
{ name: 'AI', slug: 'ai' },
{
name: 'Manifold Markets',
slug: 'manifold-markets',
},
]}
/>
<Spacer h={10} />
{followedFolds.length === 0 && (
<FastFoldFollowing
user={user}
followedFoldSlugs={followedFolds.map((f) => f.slug)}
/>
)}
<Col className="mx-3 mb-3 gap-2 text-sm text-gray-800 sm:flex-row">
<Row className="gap-2">
<SparklesIcon className="inline h-5 w-5" aria-hidden="true" />
<span className="whitespace-nowrap">Recent activity</span>
<span className="hidden sm:flex"></span>
</Row>
<div className="text-gray-500 sm:text-gray-800">
<SiteLink href="/folds" className="font-semibold">
Follow a community
</SiteLink>{' '}
to personalize
</div>
</Col>
{activeContracts ? (