fixing merge conflicts
This commit is contained in:
		
							parent
							
								
									a4e2cce4aa
								
							
						
					
					
						commit
						a322249f71
					
				|  | @ -1,15 +1,15 @@ | ||||||
| import { take, sortBy, debounce } from 'lodash' | import { take, sortBy, debounce } from 'lodash' | ||||||
|  | import PlusSmIcon from '@heroicons/react/solid/PlusSmIcon' | ||||||
| 
 | 
 | ||||||
| import { Group } from 'common/group' | import { Group, GROUP_CHAT_SLUG } from 'common/group' | ||||||
| import { Page } from 'web/components/page' | import { Page } from 'web/components/page' | ||||||
| import { listAllBets } from 'web/lib/firebase/bets' | import { listAllBets } from 'web/lib/firebase/bets' | ||||||
| import { Contract } from 'web/lib/firebase/contracts' | import { Contract, listContractsByGroupSlug } from 'web/lib/firebase/contracts' | ||||||
| import { | import { | ||||||
|   groupPath, |   groupPath, | ||||||
|   getGroupBySlug, |   getGroupBySlug, | ||||||
|   getGroupContracts, |  | ||||||
|   updateGroup, |   updateGroup, | ||||||
|   addUserToGroup, |   joinGroup, | ||||||
|   addContractToGroup, |   addContractToGroup, | ||||||
| } from 'web/lib/firebase/groups' | } from 'web/lib/firebase/groups' | ||||||
| import { Row } from 'web/components/layout/row' | import { Row } from 'web/components/layout/row' | ||||||
|  | @ -33,38 +33,37 @@ import { SEO } from 'web/components/SEO' | ||||||
| import { Linkify } from 'web/components/linkify' | import { Linkify } from 'web/components/linkify' | ||||||
| import { fromPropz, usePropz } from 'web/hooks/use-propz' | import { fromPropz, usePropz } from 'web/hooks/use-propz' | ||||||
| import { Tabs } from 'web/components/layout/tabs' | import { Tabs } from 'web/components/layout/tabs' | ||||||
| import { ContractsGrid } from 'web/components/contract/contracts-list' | import { CreateQuestionButton } from 'web/components/create-question-button' | ||||||
| import { |  | ||||||
|   createButtonStyle, |  | ||||||
|   CreateQuestionButton, |  | ||||||
| } from 'web/components/create-question-button' |  | ||||||
| import React, { useEffect, useState } from 'react' | import React, { useEffect, useState } from 'react' | ||||||
| import { GroupChat } from 'web/components/groups/group-chat' | import { GroupChat } from 'web/components/groups/group-chat' | ||||||
| import { LoadingIndicator } from 'web/components/loading-indicator' | import { LoadingIndicator } from 'web/components/loading-indicator' | ||||||
| import { Modal } from 'web/components/layout/modal' | import { Modal } from 'web/components/layout/modal' | ||||||
| import { checkAgainstQuery } from 'web/hooks/use-sort-and-query-params' | import { getSavedSort } from 'web/hooks/use-sort-and-query-params' | ||||||
| import { ChoicesToggleGroup } from 'web/components/choices-toggle-group' | import { ChoicesToggleGroup } from 'web/components/choices-toggle-group' | ||||||
| import { toast } from 'react-hot-toast' | import { toast } from 'react-hot-toast' | ||||||
| import { useCommentsOnGroup } from 'web/hooks/use-comments' | import { useCommentsOnGroup } from 'web/hooks/use-comments' | ||||||
| import { ShareIconButton } from 'web/components/share-icon-button' |  | ||||||
| import { REFERRAL_AMOUNT } from 'common/user' | import { REFERRAL_AMOUNT } from 'common/user' | ||||||
| import { SiteLink } from 'web/components/site-link' |  | ||||||
| import { ContractSearch } from 'web/components/contract-search' | import { ContractSearch } from 'web/components/contract-search' | ||||||
| import clsx from 'clsx' | import clsx from 'clsx' | ||||||
| import { FollowList } from 'web/components/follow-list' | import { FollowList } from 'web/components/follow-list' | ||||||
| import { SearchIcon } from '@heroicons/react/outline' | import { SearchIcon } from '@heroicons/react/outline' | ||||||
| import { useTipTxns } from 'web/hooks/use-tip-txns' | import { useTipTxns } from 'web/hooks/use-tip-txns' | ||||||
| import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button' | import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button' | ||||||
|  | import { searchInAny } from 'common/util/parse' | ||||||
|  | import { useWindowSize } from 'web/hooks/use-window-size' | ||||||
|  | import { CopyLinkButton } from 'web/components/copy-link-button' | ||||||
|  | import { ENV_CONFIG } from 'common/envs/constants' | ||||||
| 
 | 
 | ||||||
| export const getStaticProps = fromPropz(getStaticPropz) | export const getStaticProps = fromPropz(getStaticPropz) | ||||||
| export async function getStaticPropz(props: { params: { slugs: string[] } }) { | export async function getStaticPropz(props: { params: { slugs: string[] } }) { | ||||||
|   const { slugs } = props.params |   const { slugs } = props.params | ||||||
| 
 | 
 | ||||||
|   const group = await getGroupBySlug(slugs[0]) |   const group = await getGroupBySlug(slugs[0]) | ||||||
|   const members = group ? await listMembers(group) : [] |   const members = group && (await listMembers(group)) | ||||||
|   const creatorPromise = group ? getUser(group.creatorId) : null |   const creatorPromise = group ? getUser(group.creatorId) : null | ||||||
| 
 | 
 | ||||||
|   const contracts = group ? await getGroupContracts(group).catch((_) => []) : [] |   const contracts = | ||||||
|  |     (group && (await listContractsByGroupSlug(group.slug))) ?? [] | ||||||
| 
 | 
 | ||||||
|   const bets = await Promise.all( |   const bets = await Promise.all( | ||||||
|     contracts.map((contract: Contract) => listAllBets(contract.id)) |     contracts.map((contract: Contract) => listAllBets(contract.id)) | ||||||
|  | @ -72,10 +71,12 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) { | ||||||
| 
 | 
 | ||||||
|   const creatorScores = scoreCreators(contracts) |   const creatorScores = scoreCreators(contracts) | ||||||
|   const traderScores = scoreTraders(contracts, bets) |   const traderScores = scoreTraders(contracts, bets) | ||||||
|   const [topCreators, topTraders] = await Promise.all([ |   const [topCreators, topTraders] = | ||||||
|     toTopUsers(creatorScores), |     (members && [ | ||||||
|     toTopUsers(traderScores), |       toTopUsers(creatorScores, members), | ||||||
|   ]) |       toTopUsers(traderScores, members), | ||||||
|  |     ]) ?? | ||||||
|  |     [] | ||||||
| 
 | 
 | ||||||
|   const creator = await creatorPromise |   const creator = await creatorPromise | ||||||
| 
 | 
 | ||||||
|  | @ -94,14 +95,14 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) { | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function toTopUsers(userScores: { [userId: string]: number }) { | function toTopUsers(userScores: { [userId: string]: number }, users: User[]) { | ||||||
|   const topUserPairs = take( |   const topUserPairs = take( | ||||||
|     sortBy(Object.entries(userScores), ([_, score]) => -1 * score), |     sortBy(Object.entries(userScores), ([_, score]) => -1 * score), | ||||||
|     10 |     10 | ||||||
|   ).filter(([_, score]) => score >= 0.5) |   ).filter(([_, score]) => score >= 0.5) | ||||||
| 
 | 
 | ||||||
|   const topUsers = await Promise.all( |   const topUsers = topUserPairs.map( | ||||||
|     topUserPairs.map(([userId]) => getUser(userId)) |     ([userId]) => users.filter((user) => user.id === userId)[0] | ||||||
|   ) |   ) | ||||||
|   return topUsers.filter((user) => user) |   return topUsers.filter((user) => user) | ||||||
| } | } | ||||||
|  | @ -111,9 +112,9 @@ export async function getStaticPaths() { | ||||||
| } | } | ||||||
| const groupSubpages = [ | const groupSubpages = [ | ||||||
|   undefined, |   undefined, | ||||||
|   'chat', |   GROUP_CHAT_SLUG, | ||||||
|   'questions', |   'questions', | ||||||
|   'rankings', |   'leaderboards', | ||||||
|   'about', |   'about', | ||||||
| ] as const | ] as const | ||||||
| 
 | 
 | ||||||
|  | @ -149,26 +150,9 @@ export default function GroupPage(props: { | ||||||
|   const page = slugs?.[1] as typeof groupSubpages[number] |   const page = slugs?.[1] as typeof groupSubpages[number] | ||||||
| 
 | 
 | ||||||
|   const group = useGroup(props.group?.id) ?? props.group |   const group = useGroup(props.group?.id) ?? props.group | ||||||
|   const [contracts, setContracts] = useState<Contract[] | undefined>(undefined) |  | ||||||
|   const [query, setQuery] = useState('') |  | ||||||
|   const tips = useTipTxns({ groupId: group?.id }) |   const tips = useTipTxns({ groupId: group?.id }) | ||||||
| 
 | 
 | ||||||
|   const messages = useCommentsOnGroup(group?.id) |   const messages = useCommentsOnGroup(group?.id) | ||||||
|   const debouncedQuery = debounce(setQuery, 50) |  | ||||||
|   const filteredContracts = |  | ||||||
|     query != '' && contracts |  | ||||||
|       ? contracts.filter( |  | ||||||
|           (c) => |  | ||||||
|             checkAgainstQuery(query, c.question) || |  | ||||||
|             checkAgainstQuery(query, c.creatorName) || |  | ||||||
|             checkAgainstQuery(query, c.creatorUsername) |  | ||||||
|         ) |  | ||||||
|       : [] |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     if (group) |  | ||||||
|       getGroupContracts(group).then((contracts) => setContracts(contracts)) |  | ||||||
|   }, [group]) |  | ||||||
| 
 | 
 | ||||||
|   const user = useUser() |   const user = useUser() | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|  | @ -176,9 +160,14 @@ export default function GroupPage(props: { | ||||||
|       referrer?: string |       referrer?: string | ||||||
|     } |     } | ||||||
|     if (!user && router.isReady) |     if (!user && router.isReady) | ||||||
|       writeReferralInfo(creator.username, undefined, referrer, group?.slug) |       writeReferralInfo(creator.username, undefined, referrer, group?.id) | ||||||
|   }, [user, creator, group, router]) |   }, [user, creator, group, router]) | ||||||
| 
 | 
 | ||||||
|  |   const { width } = useWindowSize() | ||||||
|  |   const chatDisabled = !group || group.chatDisabled | ||||||
|  |   const showChatSidebar = !chatDisabled && (width ?? 1280) >= 1280 | ||||||
|  |   const showChatTab = !chatDisabled && !showChatSidebar | ||||||
|  | 
 | ||||||
|   if (group === null || !groupSubpages.includes(page) || slugs[2]) { |   if (group === null || !groupSubpages.includes(page) || slugs[2]) { | ||||||
|     return <Custom404 /> |     return <Custom404 /> | ||||||
|   } |   } | ||||||
|  | @ -186,11 +175,6 @@ export default function GroupPage(props: { | ||||||
|   const isCreator = user && group && user.id === group.creatorId |   const isCreator = user && group && user.id === group.creatorId | ||||||
|   const isMember = user && memberIds.includes(user.id) |   const isMember = user && memberIds.includes(user.id) | ||||||
| 
 | 
 | ||||||
|   const rightSidebar = ( |  | ||||||
|     <Col className="mt-6 hidden xl:block"> |  | ||||||
|       <JoinOrCreateButton group={group} user={user} isMember={!!isMember} /> |  | ||||||
|     </Col> |  | ||||||
|   ) |  | ||||||
|   const leaderboard = ( |   const leaderboard = ( | ||||||
|     <Col> |     <Col> | ||||||
|       <GroupLeaderboards |       <GroupLeaderboards | ||||||
|  | @ -211,70 +195,51 @@ export default function GroupPage(props: { | ||||||
|         creator={creator} |         creator={creator} | ||||||
|         isCreator={!!isCreator} |         isCreator={!!isCreator} | ||||||
|         user={user} |         user={user} | ||||||
|  |         members={members} | ||||||
|       /> |       /> | ||||||
|     </Col> |     </Col> | ||||||
|   ) |   ) | ||||||
| 
 | 
 | ||||||
|  |   const chatTab = ( | ||||||
|  |     <Col className=""> | ||||||
|  |       {messages ? ( | ||||||
|  |         <GroupChat messages={messages} user={user} group={group} tips={tips} /> | ||||||
|  |       ) : ( | ||||||
|  |         <LoadingIndicator /> | ||||||
|  |       )} | ||||||
|  |     </Col> | ||||||
|  |   ) | ||||||
|  | 
 | ||||||
|  |   const questionsTab = ( | ||||||
|  |     <ContractSearch | ||||||
|  |       querySortOptions={{ | ||||||
|  |         shouldLoadFromStorage: true, | ||||||
|  |         defaultSort: getSavedSort() ?? 'newest', | ||||||
|  |         defaultFilter: 'open', | ||||||
|  |       }} | ||||||
|  |       additionalFilter={{ groupSlug: group.slug }} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | 
 | ||||||
|   const tabs = [ |   const tabs = [ | ||||||
|     ...(group.chatDisabled |     ...(!showChatTab | ||||||
|       ? [] |       ? [] | ||||||
|       : [ |       : [ | ||||||
|           { |           { | ||||||
|             title: 'Chat', |             title: 'Chat', | ||||||
|             content: messages ? ( |             content: chatTab, | ||||||
|               <GroupChat |             href: groupPath(group.slug, GROUP_CHAT_SLUG), | ||||||
|                 messages={messages} |  | ||||||
|                 user={user} |  | ||||||
|                 group={group} |  | ||||||
|                 tips={tips} |  | ||||||
|               /> |  | ||||||
|             ) : ( |  | ||||||
|               <LoadingIndicator /> |  | ||||||
|             ), |  | ||||||
|             href: groupPath(group.slug, 'chat'), |  | ||||||
|           }, |           }, | ||||||
|         ]), |         ]), | ||||||
|     { |     { | ||||||
|       title: 'Questions', |       title: 'Questions', | ||||||
|       content: ( |       content: questionsTab, | ||||||
|         <div className={'mt-2 px-1'}> |  | ||||||
|           {contracts ? ( |  | ||||||
|             contracts.length > 0 ? ( |  | ||||||
|               <> |  | ||||||
|                 <input |  | ||||||
|                   type="text" |  | ||||||
|                   onChange={(e) => debouncedQuery(e.target.value)} |  | ||||||
|                   placeholder="Search the group's questions" |  | ||||||
|                   className="input input-bordered mb-4 w-full" |  | ||||||
|                 /> |  | ||||||
|                 <ContractsGrid |  | ||||||
|                   contracts={query != '' ? filteredContracts : contracts} |  | ||||||
|                   hasMore={false} |  | ||||||
|                   loadMore={() => {}} |  | ||||||
|                 /> |  | ||||||
|               </> |  | ||||||
|             ) : ( |  | ||||||
|               <div className="p-2 text-gray-500"> |  | ||||||
|                 No questions yet. Why not{' '} |  | ||||||
|                 <SiteLink |  | ||||||
|                   href={`/create/?groupId=${group.id}`} |  | ||||||
|                   className={'font-bold text-gray-700'} |  | ||||||
|                 > |  | ||||||
|                   add one? |  | ||||||
|                 </SiteLink> |  | ||||||
|               </div> |  | ||||||
|             ) |  | ||||||
|           ) : ( |  | ||||||
|             <LoadingIndicator /> |  | ||||||
|           )} |  | ||||||
|         </div> |  | ||||||
|       ), |  | ||||||
|       href: groupPath(group.slug, 'questions'), |       href: groupPath(group.slug, 'questions'), | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       title: 'Rankings', |       title: 'Leaderboards', | ||||||
|       content: leaderboard, |       content: leaderboard, | ||||||
|       href: groupPath(group.slug, 'rankings'), |       href: groupPath(group.slug, 'leaderboards'), | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       title: 'About', |       title: 'About', | ||||||
|  | @ -282,22 +247,24 @@ export default function GroupPage(props: { | ||||||
|       href: groupPath(group.slug, 'about'), |       href: groupPath(group.slug, 'about'), | ||||||
|     }, |     }, | ||||||
|   ] |   ] | ||||||
|   const tabIndex = tabs.map((t) => t.title).indexOf(page ?? 'chat') |   const tabIndex = tabs.map((t) => t.title).indexOf(page ?? GROUP_CHAT_SLUG) | ||||||
|  | 
 | ||||||
|   return ( |   return ( | ||||||
|     <Page rightSidebar={rightSidebar} className="!pb-0"> |     <Page | ||||||
|  |       rightSidebar={showChatSidebar ? chatTab : undefined} | ||||||
|  |       rightSidebarClassName={showChatSidebar ? '!top-0' : ''} | ||||||
|  |       className={showChatSidebar ? '!max-w-7xl !pb-0' : ''} | ||||||
|  |     > | ||||||
|       <SEO |       <SEO | ||||||
|         title={group.name} |         title={group.name} | ||||||
|         description={`Created by ${creator.name}. ${group.about}`} |         description={`Created by ${creator.name}. ${group.about}`} | ||||||
|         url={groupPath(group.slug)} |         url={groupPath(group.slug)} | ||||||
|       /> |       /> | ||||||
| 
 |  | ||||||
|       <Col className="px-3"> |       <Col className="px-3"> | ||||||
|         <Row className={'items-center justify-between gap-4'}> |         <Row className={'items-center justify-between gap-4'}> | ||||||
|           <div className={'sm:mb-1'}> |           <div className={'sm:mb-1'}> | ||||||
|             <div |             <div | ||||||
|               className={ |               className={'line-clamp-1 my-2 text-2xl text-indigo-700 sm:my-3'} | ||||||
|                 'line-clamp-1 my-1 text-lg text-indigo-700 sm:my-3 sm:text-2xl' |  | ||||||
|               } |  | ||||||
|             > |             > | ||||||
|               {group.name} |               {group.name} | ||||||
|             </div> |             </div> | ||||||
|  | @ -305,19 +272,15 @@ export default function GroupPage(props: { | ||||||
|               <Linkify text={group.about} /> |               <Linkify text={group.about} /> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|           <div className="hidden sm:block xl:hidden"> |           <div className="mt-2"> | ||||||
|             <JoinOrCreateButton |             <JoinOrAddQuestionsButtons | ||||||
|               group={group} |               group={group} | ||||||
|               user={user} |               user={user} | ||||||
|               isMember={!!isMember} |               isMember={!!isMember} | ||||||
|             /> |             /> | ||||||
|           </div> |           </div> | ||||||
|         </Row> |         </Row> | ||||||
|         <div className="block sm:hidden"> |  | ||||||
|           <JoinOrCreateButton group={group} user={user} isMember={!!isMember} /> |  | ||||||
|         </div> |  | ||||||
|       </Col> |       </Col> | ||||||
| 
 |  | ||||||
|       <Tabs |       <Tabs | ||||||
|         currentPageForAnalytics={groupPath(group.slug)} |         currentPageForAnalytics={groupPath(group.slug)} | ||||||
|         className={'mb-0 sm:mb-2'} |         className={'mb-0 sm:mb-2'} | ||||||
|  | @ -328,28 +291,14 @@ export default function GroupPage(props: { | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function JoinOrCreateButton(props: { | function JoinOrAddQuestionsButtons(props: { | ||||||
|   group: Group |   group: Group | ||||||
|   user: User | null | undefined |   user: User | null | undefined | ||||||
|   isMember: boolean |   isMember: boolean | ||||||
| }) { | }) { | ||||||
|   const { group, user, isMember } = props |   const { group, user, isMember } = props | ||||||
|   return user && isMember ? ( |   return user && isMember ? ( | ||||||
|     <Row |     <Row className={'mt-0 justify-end'}> | ||||||
|       className={'-mt-2 justify-between sm:mt-0 sm:flex-col sm:justify-center'} |  | ||||||
|     > |  | ||||||
|       <CreateQuestionButton |  | ||||||
|         user={user} |  | ||||||
|         overrideText={'Add a new question'} |  | ||||||
|         className={'hidden w-48 flex-shrink-0 sm:block'} |  | ||||||
|         query={`?groupId=${group.id}`} |  | ||||||
|       /> |  | ||||||
|       <CreateQuestionButton |  | ||||||
|         user={user} |  | ||||||
|         overrideText={'New question'} |  | ||||||
|         className={'block w-40 flex-shrink-0 sm:hidden'} |  | ||||||
|         query={`?groupId=${group.id}`} |  | ||||||
|       /> |  | ||||||
|       <AddContractButton group={group} user={user} /> |       <AddContractButton group={group} user={user} /> | ||||||
|     </Row> |     </Row> | ||||||
|   ) : group.anyoneCanJoin ? ( |   ) : group.anyoneCanJoin ? ( | ||||||
|  | @ -362,8 +311,9 @@ function GroupOverview(props: { | ||||||
|   creator: User |   creator: User | ||||||
|   user: User | null | undefined |   user: User | null | undefined | ||||||
|   isCreator: boolean |   isCreator: boolean | ||||||
|  |   members: User[] | ||||||
| }) { | }) { | ||||||
|   const { group, creator, isCreator, user } = props |   const { group, creator, isCreator, user, members } = props | ||||||
|   const anyoneCanJoinChoices: { [key: string]: string } = { |   const anyoneCanJoinChoices: { [key: string]: string } = { | ||||||
|     Closed: 'false', |     Closed: 'false', | ||||||
|     Open: 'true', |     Open: 'true', | ||||||
|  | @ -379,6 +329,11 @@ function GroupOverview(props: { | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   const postFix = user ? '?referrer=' + user.username : '' | ||||||
|  |   const shareUrl = `https://${ENV_CONFIG.domain}${groupPath( | ||||||
|  |     group.slug | ||||||
|  |   )}${postFix}` | ||||||
|  | 
 | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <Col className="gap-2 rounded-b bg-white p-2"> |       <Col className="gap-2 rounded-b bg-white p-2"> | ||||||
|  | @ -423,22 +378,27 @@ function GroupOverview(props: { | ||||||
|             </span> |             </span> | ||||||
|           )} |           )} | ||||||
|         </Row> |         </Row> | ||||||
|  | 
 | ||||||
|         {anyoneCanJoin && user && ( |         {anyoneCanJoin && user && ( | ||||||
|           <Row className={'flex-wrap items-center gap-1'}> |           <Col className="my-4 px-2"> | ||||||
|             <span className={'text-gray-500'}>Share</span> |             <div className="text-lg">Invite</div> | ||||||
|             <ShareIconButton |             <div className={'mb-2 text-gray-500'}> | ||||||
|               group={group} |               Invite a friend to this group and get M${REFERRAL_AMOUNT} if they | ||||||
|               username={user.username} |               sign up! | ||||||
|               buttonClassName={'hover:bg-gray-300 mt-1 !text-gray-700'} |             </div> | ||||||
|             > | 
 | ||||||
|               <span className={'mx-2'}> |             <CopyLinkButton | ||||||
|                 Invite a friend and get M${REFERRAL_AMOUNT} if they sign up! |               url={shareUrl} | ||||||
|               </span> |               tracking="copy group share link" | ||||||
|             </ShareIconButton> |               buttonClassName="btn-md rounded-l-none" | ||||||
|           </Row> |               toastClassName={'-left-28 mt-1'} | ||||||
|  |             /> | ||||||
|  |           </Col> | ||||||
|         )} |         )} | ||||||
|  | 
 | ||||||
|         <Col className={'mt-2'}> |         <Col className={'mt-2'}> | ||||||
|           <GroupMemberSearch group={group} /> |           <div className="mb-2 text-lg">Members</div> | ||||||
|  |           <GroupMemberSearch members={members} group={group} /> | ||||||
|         </Col> |         </Col> | ||||||
|       </Col> |       </Col> | ||||||
|     </> |     </> | ||||||
|  | @ -461,14 +421,20 @@ function SearchBar(props: { setQuery: (query: string) => void }) { | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function GroupMemberSearch(props: { group: Group }) { | function GroupMemberSearch(props: { members: User[]; group: Group }) { | ||||||
|   const [query, setQuery] = useState('') |   const [query, setQuery] = useState('') | ||||||
|   const members = useMembers(props.group) |   const { group } = props | ||||||
|  |   let { members } = props | ||||||
|  | 
 | ||||||
|  |   // Use static members on load, but also listen to member changes:
 | ||||||
|  |   const listenToMembers = useMembers(group) | ||||||
|  |   if (listenToMembers) { | ||||||
|  |     members = listenToMembers | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   // TODO use find-active-contracts to sort by?
 |   // TODO use find-active-contracts to sort by?
 | ||||||
|   const matches = sortBy(members, [(member) => member.name]).filter( |   const matches = sortBy(members, [(member) => member.name]).filter((m) => | ||||||
|     (m) => |     searchInAny(query, m.name, m.username) | ||||||
|       checkAgainstQuery(query, m.name) || checkAgainstQuery(query, m.username) |  | ||||||
|   ) |   ) | ||||||
|   const matchLimit = 25 |   const matchLimit = 25 | ||||||
| 
 | 
 | ||||||
|  | @ -489,27 +455,6 @@ function GroupMemberSearch(props: { group: Group }) { | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function GroupMembersList(props: { group: Group }) { |  | ||||||
|   const { group } = props |  | ||||||
|   const members = useMembers(group).filter((m) => m.id !== group.creatorId) |  | ||||||
|   const maxMembersToShow = 3 |  | ||||||
|   if (group.memberIds.length === 1) return <div /> |  | ||||||
|   return ( |  | ||||||
|     <div className="text-neutral flex flex-wrap gap-1"> |  | ||||||
|       <span className={'text-gray-500'}>Other members</span> |  | ||||||
|       {members.slice(0, maxMembersToShow).map((member, i) => ( |  | ||||||
|         <div key={member.id} className={'flex-shrink'}> |  | ||||||
|           <UserLink name={member.name} username={member.username} /> |  | ||||||
|           {members.length > 1 && i !== members.length - 1 && <span>,</span>} |  | ||||||
|         </div> |  | ||||||
|       ))} |  | ||||||
|       {members.length > maxMembersToShow && ( |  | ||||||
|         <span> & {members.length - maxMembersToShow} more</span> |  | ||||||
|       )} |  | ||||||
|     </div> |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function SortedLeaderboard(props: { | function SortedLeaderboard(props: { | ||||||
|   users: User[] |   users: User[] | ||||||
|   scoreFunction: (user: User) => number |   scoreFunction: (user: User) => number | ||||||
|  | @ -553,14 +498,14 @@ function GroupLeaderboards(props: { | ||||||
|             <SortedLeaderboard |             <SortedLeaderboard | ||||||
|               users={members} |               users={members} | ||||||
|               scoreFunction={(user) => traderScores[user.id] ?? 0} |               scoreFunction={(user) => traderScores[user.id] ?? 0} | ||||||
|               title="🏅 Bettor rankings" |               title="🏅 Top bettors" | ||||||
|               header="Profit" |               header="Profit" | ||||||
|               maxToShow={maxToShow} |               maxToShow={maxToShow} | ||||||
|             /> |             /> | ||||||
|             <SortedLeaderboard |             <SortedLeaderboard | ||||||
|               users={members} |               users={members} | ||||||
|               scoreFunction={(user) => creatorScores[user.id] ?? 0} |               scoreFunction={(user) => creatorScores[user.id] ?? 0} | ||||||
|               title="🏅 Creator rankings" |               title="🏅 Top creators" | ||||||
|               header="Market volume" |               header="Market volume" | ||||||
|               maxToShow={maxToShow} |               maxToShow={maxToShow} | ||||||
|             /> |             /> | ||||||
|  | @ -600,7 +545,7 @@ function GroupLeaderboards(props: { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function AddContractButton(props: { group: Group; user: User }) { | function AddContractButton(props: { group: Group; user: User }) { | ||||||
|   const { group } = props |   const { group, user } = props | ||||||
|   const [open, setOpen] = useState(false) |   const [open, setOpen] = useState(false) | ||||||
| 
 | 
 | ||||||
|   async function addContractToCurrentGroup(contract: Contract) { |   async function addContractToCurrentGroup(contract: Contract) { | ||||||
|  | @ -610,16 +555,39 @@ function AddContractButton(props: { group: Group; user: User }) { | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|  |       <div className={'flex justify-center'}> | ||||||
|  |         <button | ||||||
|  |           className={clsx('btn btn-sm btn-outline')} | ||||||
|  |           onClick={() => setOpen(true)} | ||||||
|  |         > | ||||||
|  |           <PlusSmIcon className="h-6 w-6" aria-hidden="true" /> question | ||||||
|  |         </button> | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|       <Modal open={open} setOpen={setOpen} className={'sm:p-0'}> |       <Modal open={open} setOpen={setOpen} className={'sm:p-0'}> | ||||||
|         <Col |         <Col | ||||||
|           className={ |           className={ | ||||||
|             'max-h-[60vh] min-h-[60vh] w-full gap-4 rounded-md bg-white p-8' |             'max-h-[60vh] min-h-[60vh] w-full gap-4 rounded-md bg-white' | ||||||
|           } |           } | ||||||
|         > |         > | ||||||
|           <div className={'text-lg text-indigo-700'}> |           <Col className="p-8 pb-0"> | ||||||
|  |             <div className={'text-xl text-indigo-700'}> | ||||||
|               Add a question to your group |               Add a question to your group | ||||||
|             </div> |             </div> | ||||||
|           <div className={'overflow-y-scroll p-1'}> | 
 | ||||||
|  |             <Col className="items-center"> | ||||||
|  |               <CreateQuestionButton | ||||||
|  |                 user={user} | ||||||
|  |                 overrideText={'New question'} | ||||||
|  |                 className={'w-48 flex-shrink-0 '} | ||||||
|  |                 query={`?groupId=${group.id}`} | ||||||
|  |               /> | ||||||
|  | 
 | ||||||
|  |               <div className={'mt-2 text-lg text-indigo-700'}>or</div> | ||||||
|  |             </Col> | ||||||
|  |           </Col> | ||||||
|  | 
 | ||||||
|  |           <div className={'overflow-y-scroll sm:px-8'}> | ||||||
|             <ContractSearch |             <ContractSearch | ||||||
|               hideOrderSelector={true} |               hideOrderSelector={true} | ||||||
|               onContractClick={addContractToCurrentGroup} |               onContractClick={addContractToCurrentGroup} | ||||||
|  | @ -631,26 +599,6 @@ function AddContractButton(props: { group: Group; user: User }) { | ||||||
|           </div> |           </div> | ||||||
|         </Col> |         </Col> | ||||||
|       </Modal> |       </Modal> | ||||||
|       <div className={'flex justify-center'}> |  | ||||||
|         <button |  | ||||||
|           className={clsx( |  | ||||||
|             createButtonStyle, |  | ||||||
|             'hidden w-48 whitespace-nowrap border border-black text-black hover:bg-black hover:text-white sm:block' |  | ||||||
|           )} |  | ||||||
|           onClick={() => setOpen(true)} |  | ||||||
|         > |  | ||||||
|           Add an old question |  | ||||||
|         </button> |  | ||||||
|         <button |  | ||||||
|           className={clsx( |  | ||||||
|             createButtonStyle, |  | ||||||
|             'block w-40 whitespace-nowrap border border-black text-black hover:bg-black hover:text-white sm:hidden' |  | ||||||
|           )} |  | ||||||
|           onClick={() => setOpen(true)} |  | ||||||
|         > |  | ||||||
|           Old question |  | ||||||
|         </button> |  | ||||||
|       </div> |  | ||||||
|     </> |     </> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  | @ -660,19 +608,19 @@ function JoinGroupButton(props: { | ||||||
|   user: User | null | undefined |   user: User | null | undefined | ||||||
| }) { | }) { | ||||||
|   const { group, user } = props |   const { group, user } = props | ||||||
|   function joinGroup() { |   function addUserToGroup() { | ||||||
|     if (user && !group.memberIds.includes(user.id)) { |     if (user && !group.memberIds.includes(user.id)) { | ||||||
|       toast.promise(addUserToGroup(group, user.id), { |       toast.promise(joinGroup(group, user.id), { | ||||||
|         loading: 'Joining group...', |         loading: 'Joining group...', | ||||||
|         success: 'Joined group!', |         success: 'Joined group!', | ||||||
|         error: "Couldn't join group", |         error: "Couldn't join group, try again?", | ||||||
|       }) |       }) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   return ( |   return ( | ||||||
|     <div> |     <div> | ||||||
|       <button |       <button | ||||||
|         onClick={user ? joinGroup : firebaseLogin} |         onClick={user ? addUserToGroup : firebaseLogin} | ||||||
|         className={'btn-md btn-outline btn whitespace-nowrap normal-case'} |         className={'btn-md btn-outline btn whitespace-nowrap normal-case'} | ||||||
|       > |       > | ||||||
|         {user ? 'Join group' : 'Login to join group'} |         {user ? 'Join group' : 'Login to join group'} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user