Bring up a list of contracts with @

This commit is contained in:
Austin Chen 2022-09-10 12:47:33 -07:00
parent 33bcc1a65e
commit b0242c6253
4 changed files with 38 additions and 24 deletions

View File

@ -1,24 +1,26 @@
import { SuggestionProps } from '@tiptap/suggestion' import { SuggestionProps } from '@tiptap/suggestion'
import clsx from 'clsx' import clsx from 'clsx'
import { User } from 'common/user' import { Contract } from 'common/contract'
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react' import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
import { contractPath } from 'web/lib/firebase/contracts'
import { Avatar } from '../avatar' import { Avatar } from '../avatar'
// copied from https://tiptap.dev/api/nodes/mention#usage // copied from https://tiptap.dev/api/nodes/mention#usage
export const MentionList = forwardRef((props: SuggestionProps<User>, ref) => { const M = forwardRef((props: SuggestionProps<Contract>, ref) => {
const { items: users, command } = props const { items: contracts, command } = props
const [selectedIndex, setSelectedIndex] = useState(0) const [selectedIndex, setSelectedIndex] = useState(0)
useEffect(() => setSelectedIndex(0), [users]) useEffect(() => setSelectedIndex(0), [contracts])
const submitUser = (index: number) => { const submitUser = (index: number) => {
const user = users[index] const contract = contracts[index]
if (user) command({ id: user.id, label: user.username } as any) if (contract)
command({ id: contract.id, label: contractPath(contract) } as any)
} }
const onUp = () => const onUp = () =>
setSelectedIndex((i) => (i + users.length - 1) % users.length) setSelectedIndex((i) => (i + contracts.length - 1) % contracts.length)
const onDown = () => setSelectedIndex((i) => (i + 1) % users.length) const onDown = () => setSelectedIndex((i) => (i + 1) % contracts.length)
const onEnter = () => submitUser(selectedIndex) const onEnter = () => submitUser(selectedIndex)
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
@ -41,23 +43,26 @@ export const MentionList = forwardRef((props: SuggestionProps<User>, ref) => {
return ( return (
<div className="w-42 absolute z-10 overflow-x-hidden rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"> <div className="w-42 absolute z-10 overflow-x-hidden rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
{!users.length ? ( {!contracts.length ? (
<span className="m-1 whitespace-nowrap">No results...</span> <span className="m-1 whitespace-nowrap">No results...</span>
) : ( ) : (
users.map((user, i) => ( contracts.map((contract, i) => (
<button <button
className={clsx( className={clsx(
'flex h-8 w-full cursor-pointer select-none items-center gap-2 truncate px-4', 'flex h-8 w-full cursor-pointer select-none items-center gap-2 truncate px-4 hover:bg-indigo-200',
selectedIndex === i ? 'bg-indigo-500 text-white' : 'text-gray-900' selectedIndex === i ? 'bg-indigo-500 text-white' : 'text-gray-900'
)} )}
onClick={() => submitUser(i)} onClick={() => submitUser(i)}
key={user.id} key={contract.id}
> >
<Avatar avatarUrl={user.avatarUrl} size="xs" /> <Avatar avatarUrl={contract.creatorAvatarUrl} size="xs" />
{user.username} {contract.question}
</button> </button>
)) ))
)} )}
</div> </div>
) )
}) })
// Just to keep the formatting pretty
export { M as MentionList }

View File

@ -3,7 +3,8 @@ import { ReactRenderer } from '@tiptap/react'
import { searchInAny } from 'common/util/parse' import { searchInAny } from 'common/util/parse'
import { orderBy } from 'lodash' import { orderBy } from 'lodash'
import tippy from 'tippy.js' import tippy from 'tippy.js'
import { getCachedUsers } from 'web/hooks/use-users' import { getCachedContracts } from 'web/hooks/use-contracts'
// import { getCachedUsers } from 'web/hooks/use-users'
import { MentionList } from './mention-list' import { MentionList } from './mention-list'
type Suggestion = MentionOptions['suggestion'] type Suggestion = MentionOptions['suggestion']
@ -15,13 +16,10 @@ const beginsWith = (text: string, query: string) =>
export const mentionSuggestion: Suggestion = { export const mentionSuggestion: Suggestion = {
items: async ({ query }) => items: async ({ query }) =>
orderBy( orderBy(
(await getCachedUsers()).filter((u) => (await getCachedContracts()).filter((c) =>
searchInAny(query, u.username, u.name) searchInAny(query, c.question)
), ),
[ [(c) => [c.question].some((s) => beginsWith(s, query))],
(u) => [u.name, u.username].some((s) => beginsWith(s, query)),
'followerCountCached',
],
['desc', 'desc'] ['desc', 'desc']
).slice(0, 5), ).slice(0, 5),
render: () => { render: () => {

View File

@ -5,14 +5,18 @@ import {
ReactNodeViewRenderer, ReactNodeViewRenderer,
} from '@tiptap/react' } from '@tiptap/react'
import clsx from 'clsx' import clsx from 'clsx'
import { Linkify } from '../linkify' import { useContract } from 'web/hooks/use-contract'
import { ContractCard } from '../contract/contract-card'
const name = 'mention-component' const name = 'mention-component'
const MentionComponent = (props: any) => { const MentionComponent = (props: any) => {
const contract = useContract(props.node.attrs.id)
return ( return (
<NodeViewWrapper className={clsx(name, 'not-prose text-indigo-700')}> <NodeViewWrapper className={clsx(name, 'not-prose text-indigo-700')}>
<Linkify text={'@' + props.node.attrs.label} /> {/* <Linkify text={'@' + props.node.attrs.label} /> */}
{contract && <ContractCard contract={contract} />}
</NodeViewWrapper> </NodeViewWrapper>
) )
} }

View File

@ -11,8 +11,9 @@ import {
listenForNewContracts, listenForNewContracts,
getUserBetContracts, getUserBetContracts,
getUserBetContractsQuery, getUserBetContractsQuery,
listAllContracts,
} from 'web/lib/firebase/contracts' } from 'web/lib/firebase/contracts'
import { useQueryClient } from 'react-query' import { QueryClient, useQueryClient } from 'react-query'
import { MINUTE_MS } from 'common/util/time' import { MINUTE_MS } from 'common/util/time'
export const useContracts = () => { export const useContracts = () => {
@ -25,6 +26,12 @@ export const useContracts = () => {
return contracts return contracts
} }
const q = new QueryClient()
export const getCachedContracts = async () =>
q.fetchQuery(['contracts'], () => listAllContracts(1000), {
staleTime: Infinity,
})
export const useActiveContracts = () => { export const useActiveContracts = () => {
const [activeContracts, setActiveContracts] = useState< const [activeContracts, setActiveContracts] = useState<
Contract[] | undefined Contract[] | undefined