Cache likes and liked by user ids on contract
This commit is contained in:
parent
2e502a774c
commit
73b75c3d9b
|
@ -59,6 +59,8 @@ export type Contract<T extends AnyContractType = AnyContractType> = {
|
|||
popularityScore?: number
|
||||
followerCount?: number
|
||||
featuredOnHomeRank?: number
|
||||
likedByUserIds?: string[]
|
||||
likedByUserCount?: number
|
||||
} & T
|
||||
|
||||
export type BinaryContract = Contract & Binary
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export type Like = {
|
||||
id: string
|
||||
id: string // will be id of the object liked, i.e. contract.id
|
||||
userId: string
|
||||
contractId: string
|
||||
type: 'contract'
|
||||
createdTime: number
|
||||
tipTxnId?: string
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ export * from './reset-betting-streaks'
|
|||
export * from './reset-weekly-emails-flag'
|
||||
export * from './on-update-contract-follow'
|
||||
export * from './on-create-like'
|
||||
export * from './on-delete-like'
|
||||
|
||||
// v2
|
||||
export * from './health'
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Like } from '../../common/like'
|
|||
import { getContract, getUser, log } from './utils'
|
||||
import { createLikeNotification } from './create-notification'
|
||||
import { TipTxn } from '../../common/txn'
|
||||
import { uniq } from 'lodash'
|
||||
|
||||
const firestore = admin.firestore()
|
||||
|
||||
|
@ -12,11 +13,28 @@ export const onCreateLike = functions.firestore
|
|||
.onCreate(async (change, context) => {
|
||||
const like = change.data() as Like
|
||||
const { eventId } = context
|
||||
await handleCreateLike(like, eventId)
|
||||
if (like.type === 'contract') {
|
||||
await handleCreateLikeNotification(like, eventId)
|
||||
await updateContractLikes(like)
|
||||
}
|
||||
})
|
||||
|
||||
const handleCreateLike = async (like: Like, eventId: string) => {
|
||||
const contract = await getContract(like.contractId)
|
||||
const updateContractLikes = async (like: Like) => {
|
||||
const contract = await getContract(like.id)
|
||||
if (!contract) {
|
||||
log('Could not find contract')
|
||||
return
|
||||
}
|
||||
const likedByUserIds = uniq(contract.likedByUserIds ?? [])
|
||||
likedByUserIds.push(like.userId)
|
||||
await firestore
|
||||
.collection('contracts')
|
||||
.doc(like.id)
|
||||
.update({ likedByUserIds, likedByUserCount: likedByUserIds.length })
|
||||
}
|
||||
|
||||
const handleCreateLikeNotification = async (like: Like, eventId: string) => {
|
||||
const contract = await getContract(like.id)
|
||||
if (!contract) {
|
||||
log('Could not find contract')
|
||||
return
|
||||
|
|
32
functions/src/on-delete-like.ts
Normal file
32
functions/src/on-delete-like.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import * as functions from 'firebase-functions'
|
||||
import * as admin from 'firebase-admin'
|
||||
import { Like } from '../../common/like'
|
||||
import { getContract, log } from './utils'
|
||||
import { uniq } from 'lodash'
|
||||
|
||||
const firestore = admin.firestore()
|
||||
|
||||
export const onDeleteLike = functions.firestore
|
||||
.document('users/{userId}/likes/{likeId}')
|
||||
.onDelete(async (change) => {
|
||||
const like = change.data() as Like
|
||||
if (like.type === 'contract') {
|
||||
await removeContractLike(like)
|
||||
}
|
||||
})
|
||||
|
||||
const removeContractLike = async (like: Like) => {
|
||||
const contract = await getContract(like.id)
|
||||
if (!contract) {
|
||||
log('Could not find contract')
|
||||
return
|
||||
}
|
||||
const likedByUserIds = uniq(contract.likedByUserIds ?? [])
|
||||
const newLikedByUserIds = likedByUserIds.filter(
|
||||
(userId) => userId !== like.userId
|
||||
)
|
||||
await firestore.collection('contracts').doc(like.id).update({
|
||||
likedByUserIds: newLikedByUserIds,
|
||||
likedByUserCount: newLikedByUserIds.length,
|
||||
})
|
||||
}
|
|
@ -2,6 +2,7 @@ import * as functions from 'firebase-functions'
|
|||
import * as admin from 'firebase-admin'
|
||||
import { FieldValue } from 'firebase-admin/firestore'
|
||||
|
||||
// TODO: should cache the follower user ids in the contract as these triggers aren't idempotent
|
||||
export const onDeleteContractFollow = functions.firestore
|
||||
.document('contracts/{contractId}/follows/{userId}')
|
||||
.onDelete(async (change, context) => {
|
||||
|
|
|
@ -19,7 +19,9 @@ export function LikeMarketButton(props: {
|
|||
const { contract, user } = props
|
||||
|
||||
const likes = useUserLikes(user?.id)
|
||||
const likedContractIds = likes?.map((l) => l.contractId)
|
||||
const likedContractIds = likes
|
||||
?.filter((l) => l.type === 'contract')
|
||||
.map((l) => l.id)
|
||||
if (!user) return <div />
|
||||
|
||||
const onLike = async () => {
|
||||
|
|
|
@ -35,10 +35,8 @@ export function UserLikesButton(props: { user: User }) {
|
|||
{likedContract.question}
|
||||
</SiteLink>
|
||||
<XIcon
|
||||
className="ml-2 h-5 w-5 cursor-pointer"
|
||||
onClick={() => async () => {
|
||||
await unLikeContract(user.id, likedContract.id)
|
||||
}}
|
||||
className="ml-2 h-5 w-5 shrink-0 cursor-pointer"
|
||||
onClick={() => unLikeContract(user.id, likedContract.id)}
|
||||
/>
|
||||
</Row>
|
||||
))}
|
||||
|
|
|
@ -15,21 +15,24 @@ export const useUserLikes = (userId: string | undefined) => {
|
|||
return contractIds
|
||||
}
|
||||
export const useUserLikedContracts = (userId: string | undefined) => {
|
||||
const [contractIds, setContractIds] = useState<Like[] | undefined>()
|
||||
const [likes, setLikes] = useState<Like[] | undefined>()
|
||||
const [contracts, setContracts] = useState<Contract[] | undefined>()
|
||||
|
||||
useEffect(() => {
|
||||
if (userId) return listenForLikes(userId, setContractIds)
|
||||
if (userId)
|
||||
return listenForLikes(userId, (likes) => {
|
||||
setLikes(likes.filter((l) => l.type === 'contract'))
|
||||
})
|
||||
}, [userId])
|
||||
|
||||
useEffect(() => {
|
||||
if (contractIds)
|
||||
if (likes)
|
||||
Promise.all(
|
||||
contractIds.map(async (like) => {
|
||||
return await getContractFromId(like.contractId)
|
||||
likes.map(async (like) => {
|
||||
return await getContractFromId(like.id)
|
||||
})
|
||||
).then((contracts) => setContracts(filterDefined(contracts)))
|
||||
}, [contractIds])
|
||||
}, [likes])
|
||||
|
||||
return contracts
|
||||
}
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
import {
|
||||
collection,
|
||||
deleteDoc,
|
||||
doc,
|
||||
query,
|
||||
setDoc,
|
||||
where,
|
||||
} from 'firebase/firestore'
|
||||
import { collection, deleteDoc, doc, setDoc } from 'firebase/firestore'
|
||||
import { db } from 'web/lib/firebase/init'
|
||||
import toast from 'react-hot-toast'
|
||||
import { transact } from 'web/lib/firebase/api'
|
||||
import { removeUndefinedProps } from 'common/lib/util/object'
|
||||
import { Like } from 'common/lib/like'
|
||||
import { removeUndefinedProps } from 'common/util/object'
|
||||
import { Like } from 'common/like'
|
||||
import { track } from '@amplitude/analytics-browser'
|
||||
import { User } from 'common/user'
|
||||
import { Contract } from 'common/lib/contract'
|
||||
import { Contract } from 'common/contract'
|
||||
|
||||
export const LIKE_TIP_AMOUNT = 5
|
||||
|
||||
|
@ -22,11 +15,7 @@ function getLikesCollection(userId: string) {
|
|||
}
|
||||
|
||||
export const unLikeContract = async (userId: string, contractId: string) => {
|
||||
const ref = await query(
|
||||
getLikesCollection(userId),
|
||||
where('contractId', '==', contractId)
|
||||
)
|
||||
const snapshot = await ref.get()
|
||||
const ref = await doc(getLikesCollection(userId), contractId)
|
||||
return await deleteDoc(ref)
|
||||
}
|
||||
|
||||
|
@ -51,13 +40,13 @@ export const likeContract = async (user: User, contract: Contract) => {
|
|||
console.log('result', result)
|
||||
}
|
||||
// create new like in db under users collection
|
||||
const ref = doc(getLikesCollection(user.id))
|
||||
const ref = doc(getLikesCollection(user.id), contract.id)
|
||||
// contract slug and question are set via trigger
|
||||
const like = removeUndefinedProps({
|
||||
id: ref.id,
|
||||
userId: user.id,
|
||||
createdTime: Date.now(),
|
||||
contractId: contract.id,
|
||||
type: 'contract',
|
||||
tipTxnId: result.txn.id,
|
||||
} as Like)
|
||||
track('like', {
|
||||
|
|
Loading…
Reference in New Issue
Block a user