Follow and unfollow folds
This commit is contained in:
parent
c3f49c44a0
commit
76841e53b1
|
@ -47,5 +47,10 @@ service cloud.firestore {
|
|||
allow read;
|
||||
allow update: if request.auth.uid == resource.data.curatorId;
|
||||
}
|
||||
|
||||
match /folds/{foldId}/followers/{userId} {
|
||||
allow read;
|
||||
allow write: if request.auth.uid == userId;
|
||||
}
|
||||
}
|
||||
}
|
50
web/components/follow-fold-button.tsx
Normal file
50
web/components/follow-fold-button.tsx
Normal file
|
@ -0,0 +1,50 @@
|
|||
import clsx from 'clsx'
|
||||
import { Fold } from '../../common/fold'
|
||||
import { useFollowingFold } from '../hooks/use-fold'
|
||||
import { useUser } from '../hooks/use-user'
|
||||
import { followFold, unfollowFold } from '../lib/firebase/folds'
|
||||
|
||||
export function FollowFoldButton(props: { fold: Fold; className?: string }) {
|
||||
const { fold, className } = props
|
||||
|
||||
const user = useUser()
|
||||
const following = useFollowingFold(fold, user)
|
||||
|
||||
const onFollow = () => {
|
||||
if (user) followFold(fold, user)
|
||||
}
|
||||
|
||||
const onUnfollow = () => {
|
||||
if (user) unfollowFold(fold, user)
|
||||
}
|
||||
|
||||
if (!user || following === undefined)
|
||||
return (
|
||||
<button className={clsx('btn btn-sm invisible', className)}>
|
||||
Follow
|
||||
</button>
|
||||
)
|
||||
|
||||
if (following) {
|
||||
return (
|
||||
<button
|
||||
className={clsx(
|
||||
'btn btn-primary btn-sm hover:bg-red-500 hover:border-red-500',
|
||||
className
|
||||
)}
|
||||
onClick={onUnfollow}
|
||||
>
|
||||
Following
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={clsx('btn btn-secondary bg-indigo-500 btn-sm', className)}
|
||||
onClick={onFollow}
|
||||
>
|
||||
Follow
|
||||
</button>
|
||||
)
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import { Fold } from '../../common/fold'
|
||||
import { listenForFold, listenForFolds } from '../lib/firebase/folds'
|
||||
import { User } from '../../common/user'
|
||||
import {
|
||||
listenForFold,
|
||||
listenForFolds,
|
||||
listenForFollow,
|
||||
} from '../lib/firebase/folds'
|
||||
|
||||
export const useFold = (foldId: string) => {
|
||||
const [fold, setFold] = useState<Fold | null | undefined>()
|
||||
|
@ -21,3 +26,13 @@ export const useFolds = () => {
|
|||
|
||||
return folds
|
||||
}
|
||||
|
||||
export const useFollowingFold = (fold: Fold, user: User | null | undefined) => {
|
||||
const [following, setFollowing] = useState<boolean | undefined>()
|
||||
|
||||
useEffect(() => {
|
||||
if (user) return listenForFollow(fold, user, setFollowing)
|
||||
}, [fold, user])
|
||||
|
||||
return following
|
||||
}
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
import { collection, doc, query, updateDoc, where } from 'firebase/firestore'
|
||||
import {
|
||||
collection,
|
||||
deleteDoc,
|
||||
doc,
|
||||
query,
|
||||
setDoc,
|
||||
updateDoc,
|
||||
where,
|
||||
} from 'firebase/firestore'
|
||||
import { Fold } from '../../../common/fold'
|
||||
import { Contract, contractCollection } from './contracts'
|
||||
import { db } from './init'
|
||||
import { User } from './users'
|
||||
import { getValues, listenForValue, listenForValues } from './utils'
|
||||
|
||||
const foldCollection = collection(db, 'folds')
|
||||
|
@ -90,3 +99,24 @@ 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 unfollowFold(fold: Fold, user: User) {
|
||||
const followDoc = doc(foldCollection, fold.id, 'followers', user.id)
|
||||
return deleteDoc(followDoc)
|
||||
}
|
||||
|
||||
export function listenForFollow(
|
||||
fold: Fold,
|
||||
user: User,
|
||||
setFollow: (following: boolean) => void
|
||||
) {
|
||||
const followDoc = doc(foldCollection, fold.id, 'followers', user.id)
|
||||
return listenForValue(followDoc, (value) => {
|
||||
setFollow(!!value)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import { Leaderboard } from '../../../components/leaderboard'
|
|||
import { formatMoney } from '../../../lib/util/format'
|
||||
import { EditFoldButton } from '../../../components/edit-fold-button'
|
||||
import Custom404 from '../../404'
|
||||
import { FollowFoldButton } from '../../../components/follow-fold-button'
|
||||
|
||||
export async function getStaticProps(props: { params: { slugs: string[] } }) {
|
||||
const { slugs } = props.params
|
||||
|
@ -155,7 +156,11 @@ export default function FoldPage(props: {
|
|||
<Page wide>
|
||||
<Row className="justify-between mb-6 px-1">
|
||||
<Title className="!m-0" text={fold.name} />
|
||||
{isCurator && <EditFoldButton className="ml-1" fold={fold} />}
|
||||
{isCurator ? (
|
||||
<EditFoldButton className="ml-1" fold={fold} />
|
||||
) : (
|
||||
<FollowFoldButton className="ml-1" fold={fold} />
|
||||
)}
|
||||
</Row>
|
||||
|
||||
<Col className="md:hidden text-gray-500 gap-2 mb-6 px-1">
|
||||
|
|
|
@ -3,6 +3,7 @@ import Link from 'next/link'
|
|||
import { useEffect, useState } from 'react'
|
||||
import { Fold } from '../../common/fold'
|
||||
import { CreateFoldButton } from '../components/create-fold-button'
|
||||
import { FollowFoldButton } from '../components/follow-fold-button'
|
||||
import { Col } from '../components/layout/col'
|
||||
import { Row } from '../components/layout/row'
|
||||
import { Page } from '../components/page'
|
||||
|
@ -82,9 +83,7 @@ export default function Folds(props: {
|
|||
</Link>
|
||||
<Row className="justify-between items-center gap-2">
|
||||
<SiteLink href={foldPath(fold)}>{fold.name}</SiteLink>
|
||||
<button className="btn btn-secondary btn-sm z-10 mb-1">
|
||||
Follow
|
||||
</button>
|
||||
<FollowFoldButton className="z-10 mb-1" fold={fold} />
|
||||
</Row>
|
||||
<Row className="items-center gap-2 text-gray-500 text-sm">
|
||||
<div>12 followers</div>
|
||||
|
|
Loading…
Reference in New Issue
Block a user