Revert "Use %mention to embed a contract card in rich text editor (#869)"
This reverts commit 140628692f
.
This commit is contained in:
parent
ebbb8905e2
commit
e0634cea6d
|
@ -21,8 +21,6 @@ import { FileUploadButton } from './file-upload-button'
|
||||||
import { linkClass } from './site-link'
|
import { linkClass } from './site-link'
|
||||||
import { mentionSuggestion } from './editor/mention-suggestion'
|
import { mentionSuggestion } from './editor/mention-suggestion'
|
||||||
import { DisplayMention } from './editor/mention'
|
import { DisplayMention } from './editor/mention'
|
||||||
import { contractMentionSuggestion } from './editor/contract-mention-suggestion'
|
|
||||||
import { DisplayContractMention } from './editor/contract-mention'
|
|
||||||
import Iframe from 'common/util/tiptap-iframe'
|
import Iframe from 'common/util/tiptap-iframe'
|
||||||
import TiptapTweet from './editor/tiptap-tweet'
|
import TiptapTweet from './editor/tiptap-tweet'
|
||||||
import { EmbedModal } from './editor/embed-modal'
|
import { EmbedModal } from './editor/embed-modal'
|
||||||
|
@ -99,12 +97,7 @@ export function useTextEditor(props: {
|
||||||
CharacterCount.configure({ limit: max }),
|
CharacterCount.configure({ limit: max }),
|
||||||
simple ? DisplayImage : Image,
|
simple ? DisplayImage : Image,
|
||||||
DisplayLink,
|
DisplayLink,
|
||||||
DisplayMention.configure({
|
DisplayMention.configure({ suggestion: mentionSuggestion }),
|
||||||
suggestion: mentionSuggestion,
|
|
||||||
}),
|
|
||||||
DisplayContractMention.configure({
|
|
||||||
suggestion: contractMentionSuggestion,
|
|
||||||
}),
|
|
||||||
Iframe,
|
Iframe,
|
||||||
TiptapTweet,
|
TiptapTweet,
|
||||||
],
|
],
|
||||||
|
@ -323,21 +316,13 @@ export function RichContent(props: {
|
||||||
smallImage ? DisplayImage : Image,
|
smallImage ? DisplayImage : Image,
|
||||||
DisplayLink.configure({ openOnClick: false }), // stop link opening twice (browser still opens)
|
DisplayLink.configure({ openOnClick: false }), // stop link opening twice (browser still opens)
|
||||||
DisplayMention,
|
DisplayMention,
|
||||||
DisplayContractMention.configure({
|
|
||||||
// Needed to set a different PluginKey for Prosemirror
|
|
||||||
suggestion: contractMentionSuggestion,
|
|
||||||
}),
|
|
||||||
Iframe,
|
Iframe,
|
||||||
TiptapTweet,
|
TiptapTweet,
|
||||||
],
|
],
|
||||||
content,
|
content,
|
||||||
editable: false,
|
editable: false,
|
||||||
})
|
})
|
||||||
useEffect(
|
useEffect(() => void editor?.commands?.setContent(content), [editor, content])
|
||||||
// Check isDestroyed here so hot reload works, see https://github.com/ueberdosis/tiptap/issues/1451#issuecomment-941988769
|
|
||||||
() => void !editor?.isDestroyed && editor?.commands?.setContent(content),
|
|
||||||
[editor, content]
|
|
||||||
)
|
|
||||||
|
|
||||||
return <EditorContent className={className} editor={editor} />
|
return <EditorContent className={className} editor={editor} />
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
import { SuggestionProps } from '@tiptap/suggestion'
|
|
||||||
import clsx from 'clsx'
|
|
||||||
import { Contract } from 'common/contract'
|
|
||||||
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
|
|
||||||
import { contractPath } from 'web/lib/firebase/contracts'
|
|
||||||
import { Avatar } from '../avatar'
|
|
||||||
|
|
||||||
// copied from https://tiptap.dev/api/nodes/mention#usage
|
|
||||||
const M = forwardRef((props: SuggestionProps<Contract>, ref) => {
|
|
||||||
const { items: contracts, command } = props
|
|
||||||
|
|
||||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
|
||||||
useEffect(() => setSelectedIndex(0), [contracts])
|
|
||||||
|
|
||||||
const submitUser = (index: number) => {
|
|
||||||
const contract = contracts[index]
|
|
||||||
if (contract)
|
|
||||||
command({ id: contract.id, label: contractPath(contract) } as any)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onUp = () =>
|
|
||||||
setSelectedIndex((i) => (i + contracts.length - 1) % contracts.length)
|
|
||||||
const onDown = () => setSelectedIndex((i) => (i + 1) % contracts.length)
|
|
||||||
const onEnter = () => submitUser(selectedIndex)
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
|
||||||
onKeyDown: ({ event }: any) => {
|
|
||||||
if (event.key === 'ArrowUp') {
|
|
||||||
onUp()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (event.key === 'ArrowDown') {
|
|
||||||
onDown()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
onEnter()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
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">
|
|
||||||
{!contracts.length ? (
|
|
||||||
<span className="m-1 whitespace-nowrap">No results...</span>
|
|
||||||
) : (
|
|
||||||
contracts.map((contract, i) => (
|
|
||||||
<button
|
|
||||||
className={clsx(
|
|
||||||
'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'
|
|
||||||
)}
|
|
||||||
onClick={() => submitUser(i)}
|
|
||||||
key={contract.id}
|
|
||||||
>
|
|
||||||
<Avatar avatarUrl={contract.creatorAvatarUrl} size="xs" />
|
|
||||||
{contract.question}
|
|
||||||
</button>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Just to keep the formatting pretty
|
|
||||||
export { M as MentionList }
|
|
|
@ -1,76 +0,0 @@
|
||||||
import type { MentionOptions } from '@tiptap/extension-mention'
|
|
||||||
import { ReactRenderer } from '@tiptap/react'
|
|
||||||
import { searchInAny } from 'common/util/parse'
|
|
||||||
import { orderBy } from 'lodash'
|
|
||||||
import tippy from 'tippy.js'
|
|
||||||
import { getCachedContracts } from 'web/hooks/use-contracts'
|
|
||||||
import { MentionList } from './contract-mention-list'
|
|
||||||
import { PluginKey } from 'prosemirror-state'
|
|
||||||
|
|
||||||
type Suggestion = MentionOptions['suggestion']
|
|
||||||
|
|
||||||
const beginsWith = (text: string, query: string) =>
|
|
||||||
text.toLocaleLowerCase().startsWith(query.toLocaleLowerCase())
|
|
||||||
|
|
||||||
// copied from https://tiptap.dev/api/nodes/mention#usage
|
|
||||||
// TODO: merge with mention-suggestion.ts?
|
|
||||||
export const contractMentionSuggestion: Suggestion = {
|
|
||||||
char: '%',
|
|
||||||
allowSpaces: true,
|
|
||||||
pluginKey: new PluginKey('contract-mention'),
|
|
||||||
items: async ({ query }) =>
|
|
||||||
orderBy(
|
|
||||||
(await getCachedContracts()).filter((c) =>
|
|
||||||
searchInAny(query, c.question)
|
|
||||||
),
|
|
||||||
[(c) => [c.question].some((s) => beginsWith(s, query))],
|
|
||||||
['desc', 'desc']
|
|
||||||
).slice(0, 5),
|
|
||||||
render: () => {
|
|
||||||
let component: ReactRenderer
|
|
||||||
let popup: ReturnType<typeof tippy>
|
|
||||||
return {
|
|
||||||
onStart: (props) => {
|
|
||||||
component = new ReactRenderer(MentionList, {
|
|
||||||
props,
|
|
||||||
editor: props.editor,
|
|
||||||
})
|
|
||||||
if (!props.clientRect) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
popup = tippy('body', {
|
|
||||||
getReferenceClientRect: props.clientRect as any,
|
|
||||||
appendTo: () => document.body,
|
|
||||||
content: component?.element,
|
|
||||||
showOnCreate: true,
|
|
||||||
interactive: true,
|
|
||||||
trigger: 'manual',
|
|
||||||
placement: 'bottom-start',
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onUpdate(props) {
|
|
||||||
component?.updateProps(props)
|
|
||||||
|
|
||||||
if (!props.clientRect) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
popup?.[0].setProps({
|
|
||||||
getReferenceClientRect: props.clientRect as any,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onKeyDown(props) {
|
|
||||||
if (props.event.key === 'Escape') {
|
|
||||||
popup?.[0].hide()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return (component?.ref as any)?.onKeyDown(props)
|
|
||||||
},
|
|
||||||
onExit() {
|
|
||||||
popup?.[0].destroy()
|
|
||||||
component?.destroy()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
import Mention from '@tiptap/extension-mention'
|
|
||||||
import {
|
|
||||||
mergeAttributes,
|
|
||||||
NodeViewWrapper,
|
|
||||||
ReactNodeViewRenderer,
|
|
||||||
} from '@tiptap/react'
|
|
||||||
import clsx from 'clsx'
|
|
||||||
import { useContract } from 'web/hooks/use-contract'
|
|
||||||
import { ContractCard } from '../contract/contract-card'
|
|
||||||
|
|
||||||
const name = 'contract-mention-component'
|
|
||||||
|
|
||||||
const ContractMentionComponent = (props: any) => {
|
|
||||||
const contract = useContract(props.node.attrs.id)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NodeViewWrapper className={clsx(name, 'not-prose')}>
|
|
||||||
{contract && (
|
|
||||||
<ContractCard
|
|
||||||
contract={contract}
|
|
||||||
className="my-2 w-full border border-gray-100"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</NodeViewWrapper>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mention extension that renders React. See:
|
|
||||||
* https://tiptap.dev/guide/custom-extensions#extend-existing-extensions
|
|
||||||
* https://tiptap.dev/guide/node-views/react#render-a-react-component
|
|
||||||
*/
|
|
||||||
export const DisplayContractMention = Mention.extend({
|
|
||||||
parseHTML: () => [{ tag: name }],
|
|
||||||
renderHTML: ({ HTMLAttributes }) => [name, mergeAttributes(HTMLAttributes)],
|
|
||||||
addNodeView: () =>
|
|
||||||
ReactNodeViewRenderer(ContractMentionComponent, {
|
|
||||||
// On desktop, render cards below half-width so you can stack two
|
|
||||||
className: 'inline-block sm:w-[calc(50%-1rem)] sm:mr-1',
|
|
||||||
}),
|
|
||||||
})
|
|
|
@ -9,9 +9,8 @@ import {
|
||||||
listenForNewContracts,
|
listenForNewContracts,
|
||||||
getUserBetContracts,
|
getUserBetContracts,
|
||||||
getUserBetContractsQuery,
|
getUserBetContractsQuery,
|
||||||
listAllContracts,
|
|
||||||
} from 'web/lib/firebase/contracts'
|
} from 'web/lib/firebase/contracts'
|
||||||
import { QueryClient, useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
import { MINUTE_MS } from 'common/util/time'
|
import { MINUTE_MS } from 'common/util/time'
|
||||||
|
|
||||||
export const useContracts = () => {
|
export const useContracts = () => {
|
||||||
|
@ -24,12 +23,6 @@ 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
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
"nanoid": "^3.3.4",
|
"nanoid": "^3.3.4",
|
||||||
"next": "12.2.5",
|
"next": "12.2.5",
|
||||||
"node-fetch": "3.2.4",
|
"node-fetch": "3.2.4",
|
||||||
"prosemirror-state": "1.4.1",
|
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-beautiful-dnd": "13.1.1",
|
"react-beautiful-dnd": "13.1.1",
|
||||||
"react-confetti": "6.0.1",
|
"react-confetti": "6.0.1",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user