Bring up a list of contracts with @
This commit is contained in:
parent
33bcc1a65e
commit
b0242c6253
|
@ -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 }
|
||||||
|
|
|
@ -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: () => {
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user