wip liked contracts list

This commit is contained in:
Ian Philips 2022-08-29 13:37:49 -06:00
parent 559793d23b
commit 2e502a774c
6 changed files with 170 additions and 66 deletions

View File

@ -76,28 +76,34 @@ export const ContractOverview = (props: {
<Row className="items-center justify-between gap-4 xl:hidden"> <Row className="items-center justify-between gap-4 xl:hidden">
<BinaryResolutionOrChance contract={contract} /> <BinaryResolutionOrChance contract={contract} />
{tradingAllowed(contract) && ( {tradingAllowed(contract) && (
<Col> <Row>
<BetButton contract={contract as CPMMBinaryContract} /> <LikeMarketButton contract={contract} user={user} />
{!user && ( <Col>
<div className="mt-1 text-center text-sm text-gray-500"> <BetButton contract={contract as CPMMBinaryContract} />
(with play money!) {!user && (
</div> <div className="mt-1 text-center text-sm text-gray-500">
)} (with play money!)
</Col> </div>
)}
</Col>
</Row>
)} )}
</Row> </Row>
) : isPseudoNumeric ? ( ) : isPseudoNumeric ? (
<Row className="items-center justify-between gap-4 xl:hidden"> <Row className="items-center justify-between gap-4 xl:hidden">
<PseudoNumericResolutionOrExpectation contract={contract} /> <PseudoNumericResolutionOrExpectation contract={contract} />
{tradingAllowed(contract) && ( {tradingAllowed(contract) && (
<Col> <Row>
<BetButton contract={contract} /> <LikeMarketButton contract={contract} user={user} />
{!user && ( <Col>
<div className="mt-1 text-center text-sm text-gray-500"> <BetButton contract={contract} />
(with play money!) {!user && (
</div> <div className="mt-1 text-center text-sm text-gray-500">
)} (with play money!)
</Col> </div>
)}
</Col>
</Row>
)} )}
</Row> </Row>
) : ( ) : (

View File

@ -3,20 +3,14 @@ import { Button } from 'web/components/button'
import React from 'react' import React from 'react'
import { Contract } from 'common/contract' import { Contract } from 'common/contract'
import { User } from 'common/user' import { User } from 'common/user'
import { collection, deleteDoc, doc, setDoc } from 'firebase/firestore'
import { removeUndefinedProps } from 'common/util/object'
import { track } from '@amplitude/analytics-browser'
import { db } from 'web/lib/firebase/init'
import { Like } from 'common/like'
import { useUserLikes } from 'web/hooks/use-likes' import { useUserLikes } from 'web/hooks/use-likes'
import { transact } from 'web/lib/firebase/api'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import { formatMoney } from 'common/util/format' import { formatMoney } from 'common/util/format'
import {
function getLikesCollection(userId: string) { LIKE_TIP_AMOUNT,
return collection(db, 'users', userId, 'likes') likeContract,
} unLikeContract,
const LIKE_TIP_AMOUNT = 5 } from 'web/lib/firebase/likes'
export function LikeMarketButton(props: { export function LikeMarketButton(props: {
contract: Contract contract: Contract
@ -30,48 +24,11 @@ export function LikeMarketButton(props: {
const onLike = async () => { const onLike = async () => {
if (likedContractIds?.includes(contract.id)) { if (likedContractIds?.includes(contract.id)) {
const ref = doc( await unLikeContract(user.id, contract.id)
getLikesCollection(user.id),
likes?.find((l) => l.contractId === contract.id)?.id
)
await deleteDoc(ref)
toast(`You removed this market from your likes`) toast(`You removed this market from your likes`)
return return
} }
if (user.balance < LIKE_TIP_AMOUNT) { await likeContract(user, contract)
toast('You do not have enough M$ to like this market!')
return
}
let result: any = {}
if (LIKE_TIP_AMOUNT > 0) {
result = await transact({
amount: LIKE_TIP_AMOUNT,
fromId: user.id,
fromType: 'USER',
toId: contract.creatorId,
toType: 'USER',
token: 'M$',
category: 'TIP',
data: { contractId: contract.id },
description: `${user.name} liked contract ${contract.id} for M$ ${LIKE_TIP_AMOUNT} to ${contract.creatorId} `,
})
console.log('result', result)
}
// create new like in db under users collection
const ref = doc(getLikesCollection(user.id))
// contract slug and question are set via trigger
const like = removeUndefinedProps({
id: ref.id,
userId: user.id,
createdTime: Date.now(),
contractId: contract.id,
tipTxnId: result.txn.id,
} as Like)
track('like', {
contractId: contract.id,
})
await setDoc(ref, like)
toast(`You tipped ${contract.creatorName} ${formatMoney(LIKE_TIP_AMOUNT)}!`) toast(`You tipped ${contract.creatorName} ${formatMoney(LIKE_TIP_AMOUNT)}!`)
} }

View File

@ -0,0 +1,50 @@
import { User } from 'common/lib/user'
import { useState } from 'react'
import { TextButton } from 'web/components/text-button'
import { Modal } from 'web/components/layout/modal'
import { Col } from 'web/components/layout/col'
import { useUserLikedContracts } from 'web/hooks/use-likes'
import { SiteLink } from 'web/components/site-link'
import { Row } from 'web/components/layout/row'
import { XIcon } from '@heroicons/react/outline'
import { unLikeContract } from 'web/lib/firebase/likes'
import { contractPath } from 'web/lib/firebase/contracts'
export function UserLikesButton(props: { user: User }) {
const { user } = props
const [isOpen, setIsOpen] = useState(false)
const likedContracts = useUserLikedContracts(user.id)
return (
<>
<TextButton onClick={() => setIsOpen(true)}>
<span className="font-semibold">{likedContracts?.length ?? ''}</span>{' '}
Likes
</TextButton>
<Modal open={isOpen} setOpen={setIsOpen}>
<Col className="rounded bg-white p-6">
<span className={'mb-4 text-xl'}>Liked Markets</span>
<Col className={'gap-4'}>
{likedContracts?.map((likedContract) => (
<Row key={likedContract.id} className={'justify-between gap-2'}>
<SiteLink
href={contractPath(likedContract)}
className={'truncate text-indigo-700'}
>
{likedContract.question}
</SiteLink>
<XIcon
className="ml-2 h-5 w-5 cursor-pointer"
onClick={() => async () => {
await unLikeContract(user.id, likedContract.id)
}}
/>
</Row>
))}
</Col>
</Col>
</Modal>
</>
)
}

View File

@ -31,6 +31,7 @@ import { ENV_CONFIG } from 'common/envs/constants'
import { BettingStreakModal } from 'web/components/profile/betting-streak-modal' import { BettingStreakModal } from 'web/components/profile/betting-streak-modal'
import { REFERRAL_AMOUNT } from 'common/economy' import { REFERRAL_AMOUNT } from 'common/economy'
import { LoansModal } from './profile/loans-modal' import { LoansModal } from './profile/loans-modal'
import { UserLikesButton } from 'web/components/profile/user-likes-button'
export function UserPage(props: { user: User }) { export function UserPage(props: { user: User }) {
const { user } = props const { user } = props
@ -273,6 +274,7 @@ export function UserPage(props: { user: User }) {
<FollowersButton user={user} /> <FollowersButton user={user} />
<ReferralsButton user={user} /> <ReferralsButton user={user} />
<GroupsButton user={user} /> <GroupsButton user={user} />
<UserLikesButton user={user} />
</Row> </Row>
), ),
}, },

View File

@ -1,6 +1,9 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { listenForLikes } from 'web/lib/firebase/users' import { listenForLikes } from 'web/lib/firebase/users'
import { Like } from 'common/like' import { Like } from 'common/like'
import { Contract } from 'common/lib/contract'
import { getContractFromId } from 'web/lib/firebase/contracts'
import { filterDefined } from 'common/util/array'
export const useUserLikes = (userId: string | undefined) => { export const useUserLikes = (userId: string | undefined) => {
const [contractIds, setContractIds] = useState<Like[] | undefined>() const [contractIds, setContractIds] = useState<Like[] | undefined>()
@ -11,3 +14,22 @@ export const useUserLikes = (userId: string | undefined) => {
return contractIds return contractIds
} }
export const useUserLikedContracts = (userId: string | undefined) => {
const [contractIds, setContractIds] = useState<Like[] | undefined>()
const [contracts, setContracts] = useState<Contract[] | undefined>()
useEffect(() => {
if (userId) return listenForLikes(userId, setContractIds)
}, [userId])
useEffect(() => {
if (contractIds)
Promise.all(
contractIds.map(async (like) => {
return await getContractFromId(like.contractId)
})
).then((contracts) => setContracts(filterDefined(contracts)))
}, [contractIds])
return contracts
}

67
web/lib/firebase/likes.ts Normal file
View File

@ -0,0 +1,67 @@
import {
collection,
deleteDoc,
doc,
query,
setDoc,
where,
} 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 { track } from '@amplitude/analytics-browser'
import { User } from 'common/user'
import { Contract } from 'common/lib/contract'
export const LIKE_TIP_AMOUNT = 5
function getLikesCollection(userId: string) {
return collection(db, 'users', userId, 'likes')
}
export const unLikeContract = async (userId: string, contractId: string) => {
const ref = await query(
getLikesCollection(userId),
where('contractId', '==', contractId)
)
const snapshot = await ref.get()
return await deleteDoc(ref)
}
export const likeContract = async (user: User, contract: Contract) => {
if (user.balance < LIKE_TIP_AMOUNT) {
toast('You do not have enough M$ to like this market!')
return
}
let result: any = {}
if (LIKE_TIP_AMOUNT > 0) {
result = await transact({
amount: LIKE_TIP_AMOUNT,
fromId: user.id,
fromType: 'USER',
toId: contract.creatorId,
toType: 'USER',
token: 'M$',
category: 'TIP',
data: { contractId: contract.id },
description: `${user.name} liked contract ${contract.id} for M$ ${LIKE_TIP_AMOUNT} to ${contract.creatorId} `,
})
console.log('result', result)
}
// create new like in db under users collection
const ref = doc(getLikesCollection(user.id))
// contract slug and question are set via trigger
const like = removeUndefinedProps({
id: ref.id,
userId: user.id,
createdTime: Date.now(),
contractId: contract.id,
tipTxnId: result.txn.id,
} as Like)
track('like', {
contractId: contract.id,
})
await setDoc(ref, like)
}