Add user's collated comments onto profile
This commit is contained in:
		
							parent
							
								
									a97ca900c4
								
							
						
					
					
						commit
						ce62edbc8c
					
				
							
								
								
									
										69
									
								
								web/components/comments-list.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								web/components/comments-list.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,69 @@ | |||
| import { Comment } from '../../common/comment' | ||||
| import { Contract } from '../../common/contract' | ||||
| import { contractPath } from '../lib/firebase/contracts' | ||||
| import { SiteLink } from './site-link' | ||||
| import { Row } from './layout/row' | ||||
| import { Avatar } from './avatar' | ||||
| import { RelativeTimestamp } from './relative-timestamp' | ||||
| import { UserLink } from './user-page' | ||||
| import { User } from '../../common/user' | ||||
| import _, { Dictionary } from 'lodash' | ||||
| import { Col } from './layout/col' | ||||
| 
 | ||||
| export function UserCommentsList(props: { | ||||
|   user: User | ||||
|   commentsByContractId: Dictionary<Comment[]> | ||||
|   uniqueContracts: (Contract | undefined)[] | ||||
| }) { | ||||
|   const { commentsByContractId, uniqueContracts } = props | ||||
| 
 | ||||
|   return ( | ||||
|     <Col className={'bg-white'}> | ||||
|       {uniqueContracts.map( | ||||
|         (contract) => | ||||
|           contract && ( | ||||
|             <div key={contract.id} className={'border-width-1 border-b p-5'}> | ||||
|               <div className={'mb-2 text-sm text-indigo-700'}> | ||||
|                 <SiteLink href={contract ? contractPath(contract) : ''}> | ||||
|                   {contract ? contract.question : '...'} | ||||
|                 </SiteLink> | ||||
|               </div> | ||||
|               {commentsByContractId[contract.id].map((comment) => ( | ||||
|                 <div key={comment.id} className={'relative pb-6'}> | ||||
|                   <div className="relative flex items-start space-x-3"> | ||||
|                     <ProfileComment comment={comment} /> | ||||
|                   </div> | ||||
|                 </div> | ||||
|               ))} | ||||
|             </div> | ||||
|           ) | ||||
|       )} | ||||
|     </Col> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| function ProfileComment(props: { comment: Comment }) { | ||||
|   const { comment } = props | ||||
|   const { text, userUsername, userName, userAvatarUrl, createdTime } = comment | ||||
|   // TODO: find and attach relevant bets by comment betId at some point
 | ||||
|   return ( | ||||
|     <div> | ||||
|       <Row className={'gap-4'}> | ||||
|         <Avatar username={userUsername} avatarUrl={userAvatarUrl} /> | ||||
|         <div className="min-w-0 flex-1"> | ||||
|           <div> | ||||
|             <p className="mt-0.5 text-sm text-gray-500"> | ||||
|               <UserLink | ||||
|                 className="text-gray-500" | ||||
|                 username={userUsername} | ||||
|                 name={userName} | ||||
|               />{' '} | ||||
|               <RelativeTimestamp time={createdTime} /> | ||||
|             </p> | ||||
|           </div> | ||||
|           {text} | ||||
|         </div> | ||||
|       </Row> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
|  | @ -13,12 +13,14 @@ import { LinkIcon } from '@heroicons/react/solid' | |||
| import { genHash } from '../../common/util/random' | ||||
| import { PencilIcon } from '@heroicons/react/outline' | ||||
| import { Tabs } from './layout/tabs' | ||||
| import React, { useEffect, useState } from 'react' | ||||
| import { Comment } from '../../common/comment' | ||||
| import { getUsersComments } from '../lib/firebase/comments' | ||||
| import { Bet } from '../../common/bet' | ||||
| import { formatMoney } from '../../common/util/format' | ||||
| import { RelativeTimestamp } from './relative-timestamp' | ||||
| import { UserCommentsList } from './comments-list' | ||||
| import { useEffect, useState } from 'react' | ||||
| import { Comment, getUsersComments } from '../lib/firebase/comments' | ||||
| import { Contract } from '../../common/contract' | ||||
| import { getContractFromId, listContracts } from '../lib/firebase/contracts' | ||||
| import { LoadingIndicator } from './loading-indicator' | ||||
| import { useRouter } from 'next/router' | ||||
| import _ from 'lodash' | ||||
| 
 | ||||
| export function UserLink(props: { | ||||
|   name: string | ||||
|  | @ -36,24 +38,43 @@ export function UserLink(props: { | |||
|   ) | ||||
| } | ||||
| 
 | ||||
| export function UserPage(props: { user: User; currentUser?: User }) { | ||||
|   const { user, currentUser } = props | ||||
| export function UserPage(props: { | ||||
|   user: User | ||||
|   currentUser?: User | ||||
|   defaultTabIndex?: number | ||||
| }) { | ||||
|   const router = useRouter() | ||||
|   const { user, currentUser, defaultTabIndex } = props | ||||
|   const isCurrentUser = user.id === currentUser?.id | ||||
|   const bannerUrl = user.bannerUrl ?? defaultBannerUrl(user.id) | ||||
|   const [comments, setComments] = useState<Comment[]>([] as Comment[]) | ||||
|   const [usersComments, setUsersComments] = useState<Comment[]>([] as Comment[]) | ||||
|   const [usersContracts, setUsersContracts] = useState<Contract[] | 'loading'>( | ||||
|     'loading' | ||||
|   ) | ||||
|   const [uniqueContracts, setUniqueContracts] = useState< | ||||
|     (Contract | undefined)[] | 'loading' | ||||
|   >('loading') | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (user) { | ||||
|       getUsersComments(user.id).then(setComments) | ||||
|       getUsersComments(user.id).then(setUsersComments) | ||||
|       listContracts(user.id).then(setUsersContracts) | ||||
|     } | ||||
|   }, [user]) | ||||
| 
 | ||||
|   const items = comments | ||||
|     .sort((a, b) => b.createdTime - a.createdTime) | ||||
|     .map((comment) => ({ | ||||
|       comment, | ||||
|       bet: undefined, | ||||
|     })) | ||||
|   useEffect(() => { | ||||
|     // get all unique contracts for the comments and group each comments array to a contract
 | ||||
|     if (usersComments) { | ||||
|       const uniqueContractIds = _.uniq( | ||||
|         usersComments.map((comment) => comment.contractId) | ||||
|       ) | ||||
|       const uniqueContracts = Array.from(uniqueContractIds).map((id) => | ||||
|         getContractFromId(id) | ||||
|       ) | ||||
|       Promise.all(uniqueContracts).then(setUniqueContracts) | ||||
|     } | ||||
|   }, [usersComments]) | ||||
| 
 | ||||
|   return ( | ||||
|     <Page> | ||||
|       <SEO | ||||
|  | @ -158,28 +179,63 @@ export function UserPage(props: { user: User; currentUser?: User }) { | |||
|         </Col> | ||||
| 
 | ||||
|         <Spacer h={10} /> | ||||
|         {usersContracts !== 'loading' && uniqueContracts != 'loading' ? ( | ||||
|           <Tabs | ||||
|             className={'pb-2 pt-1 '} | ||||
|             defaultIndex={defaultTabIndex} | ||||
|             onClick={(tabName) => | ||||
|               router.push( | ||||
|                 { | ||||
|                   pathname: `/${user.username}`, | ||||
|                   query: { tab: tabName }, | ||||
|                 }, | ||||
|                 undefined, | ||||
|                 { shallow: true } | ||||
|               ) | ||||
|             } | ||||
|             tabs={[ | ||||
|               { | ||||
|                 title: 'Markets', | ||||
|               content: <CreatorContractsList creator={user} />, | ||||
|                 content: <CreatorContractsList contracts={usersContracts} />, | ||||
|                 tabIcon: ( | ||||
|                   <div | ||||
|                     className={clsx( | ||||
|                       usersContracts.length > 9 ? 'px-1' : 'px-1.5', | ||||
|                       'items-center rounded-full border-2 border-current py-0.5 text-xs' | ||||
|                     )} | ||||
|                   > | ||||
|                     {usersContracts.length} | ||||
|                   </div> | ||||
|                 ), | ||||
|               }, | ||||
|               { | ||||
|                 title: 'Comments', | ||||
|                 content: ( | ||||
|                 <> | ||||
|                   {items.map((item, activityItemIdx) => ( | ||||
|                     <div key={item.comment.id} className={'relative pb-6'}> | ||||
|                       <div className="relative flex items-start space-x-3"> | ||||
|                         <ProfileComment comment={item.comment} bet={item.bet} /> | ||||
|                   <UserCommentsList | ||||
|                     user={user} | ||||
|                     commentsByContractId={_.groupBy( | ||||
|                       usersComments, | ||||
|                       (comment) => comment.contractId | ||||
|                     )} | ||||
|                     uniqueContracts={uniqueContracts} | ||||
|                   /> | ||||
|                 ), | ||||
|                 tabIcon: ( | ||||
|                   <div | ||||
|                     className={clsx( | ||||
|                       usersComments.length > 9 ? 'px-1' : 'px-1.5', | ||||
|                       'items-center rounded-full border-2 border-current py-0.5 text-xs' | ||||
|                     )} | ||||
|                   > | ||||
|                     {usersComments.length} | ||||
|                   </div> | ||||
|                     </div> | ||||
|                   ))} | ||||
|                 </> | ||||
|                 ), | ||||
|               }, | ||||
|             ]} | ||||
|           /> | ||||
|         ) : ( | ||||
|           <LoadingIndicator /> | ||||
|         )} | ||||
|       </Col> | ||||
|     </Page> | ||||
|   ) | ||||
|  | @ -197,35 +253,3 @@ export function defaultBannerUrl(userId: string) { | |||
|   ] | ||||
|   return defaultBanner[genHash(userId)() % defaultBanner.length] | ||||
| } | ||||
| 
 | ||||
| function ProfileComment(props: { comment: Comment; bet: Bet | undefined }) { | ||||
|   const { comment, bet } = props | ||||
|   let money: string | undefined | ||||
|   let outcome: string | undefined | ||||
|   let bought: string | undefined | ||||
|   if (bet) { | ||||
|     outcome = bet.outcome | ||||
|     bought = bet.amount >= 0 ? 'bought' : 'sold' | ||||
|     money = formatMoney(Math.abs(bet.amount)) | ||||
|   } | ||||
|   const { text, userUsername, userName, userAvatarUrl, createdTime } = comment | ||||
| 
 | ||||
|   return ( | ||||
|     <> | ||||
|       <Avatar username={userUsername} avatarUrl={userAvatarUrl} /> | ||||
|       <div className="min-w-0 flex-1"> | ||||
|         <div> | ||||
|           <p className="mt-0.5 text-sm text-gray-500"> | ||||
|             <UserLink | ||||
|               className="text-gray-500" | ||||
|               username={userUsername} | ||||
|               name={userName} | ||||
|             />{' '} | ||||
|             <RelativeTimestamp time={createdTime} /> | ||||
|           </p> | ||||
|         </div> | ||||
|         {text} | ||||
|       </div> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
|  |  | |||
|  | @ -126,10 +126,13 @@ export async function getDailyComments( | |||
|   return commentsByDay | ||||
| } | ||||
| 
 | ||||
| // TODO: add firebase index for comments - userid
 | ||||
| export async function getUsersComments(userId: string) { | ||||
|   const getUsersCommentsQuery = (userId: string) => | ||||
|     query(collectionGroup(db, 'comments'), where('userId', '==', userId)) | ||||
|     query( | ||||
|       collectionGroup(db, 'comments'), | ||||
|       where('userId', '==', userId), | ||||
|       orderBy('createdTime', 'desc') | ||||
|     ) | ||||
|   const comments = await getValues<Comment>(getUsersCommentsQuery(userId)) | ||||
|   return comments | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user