Fast fold following (#51)
* fast follow folds * FastFoldFollowing component on homepage
This commit is contained in:
parent
693652935d
commit
13727bb19f
107
web/components/fast-fold-following.tsx
Normal file
107
web/components/fast-fold-following.tsx
Normal 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} />
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 ? (
|
||||
|
|
Loading…
Reference in New Issue
Block a user