fix link highlighting with mention present (#1544)
parent
6d4ad59416
commit
b030d94a64
|
@ -63,6 +63,7 @@
|
||||||
"@tiptap/extension-paragraph": "^2.0.0-beta.220",
|
"@tiptap/extension-paragraph": "^2.0.0-beta.220",
|
||||||
"@tiptap/extension-placeholder": "^2.0.0-beta.220",
|
"@tiptap/extension-placeholder": "^2.0.0-beta.220",
|
||||||
"@tiptap/extension-text": "^2.0.0-beta.220",
|
"@tiptap/extension-text": "^2.0.0-beta.220",
|
||||||
|
"@tiptap/html": "^2.1.11",
|
||||||
"@tiptap/pm": "^2.0.0-beta.220",
|
"@tiptap/pm": "^2.0.0-beta.220",
|
||||||
"@tiptap/react": "^2.0.0-beta.220",
|
"@tiptap/react": "^2.0.0-beta.220",
|
||||||
"@tiptap/suggestion": "^2.0.0-beta.220",
|
"@tiptap/suggestion": "^2.0.0-beta.220",
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
|
||||||
import {isUriImage, blobToDataUri} from 'lib/media/util'
|
import {isUriImage, blobToDataUri} from 'lib/media/util'
|
||||||
import {Emoji} from './web/EmojiPicker.web'
|
import {Emoji} from './web/EmojiPicker.web'
|
||||||
import {LinkDecorator} from './web/LinkDecorator'
|
import {LinkDecorator} from './web/LinkDecorator'
|
||||||
|
import {generateJSON} from '@tiptap/html'
|
||||||
|
|
||||||
export interface TextInputRef {
|
export interface TextInputRef {
|
||||||
focus: () => void
|
focus: () => void
|
||||||
|
@ -52,6 +53,26 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const modeClass = useColorSchemeStyle('ProseMirror-light', 'ProseMirror-dark')
|
const modeClass = useColorSchemeStyle('ProseMirror-light', 'ProseMirror-dark')
|
||||||
|
const extensions = React.useMemo(
|
||||||
|
() => [
|
||||||
|
Document,
|
||||||
|
LinkDecorator,
|
||||||
|
Mention.configure({
|
||||||
|
HTMLAttributes: {
|
||||||
|
class: 'mention',
|
||||||
|
},
|
||||||
|
suggestion: createSuggestion({autocompleteView}),
|
||||||
|
}),
|
||||||
|
Paragraph,
|
||||||
|
Placeholder.configure({
|
||||||
|
placeholder,
|
||||||
|
}),
|
||||||
|
Text,
|
||||||
|
History,
|
||||||
|
Hardbreak,
|
||||||
|
],
|
||||||
|
[autocompleteView, placeholder],
|
||||||
|
)
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
textInputWebEmitter.addListener('publish', onPressPublish)
|
textInputWebEmitter.addListener('publish', onPressPublish)
|
||||||
|
@ -68,23 +89,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||||
|
|
||||||
const editor = useEditor(
|
const editor = useEditor(
|
||||||
{
|
{
|
||||||
extensions: [
|
extensions,
|
||||||
Document,
|
|
||||||
LinkDecorator,
|
|
||||||
Mention.configure({
|
|
||||||
HTMLAttributes: {
|
|
||||||
class: 'mention',
|
|
||||||
},
|
|
||||||
suggestion: createSuggestion({autocompleteView}),
|
|
||||||
}),
|
|
||||||
Paragraph,
|
|
||||||
Placeholder.configure({
|
|
||||||
placeholder,
|
|
||||||
}),
|
|
||||||
Text,
|
|
||||||
History,
|
|
||||||
Hardbreak,
|
|
||||||
],
|
|
||||||
editorProps: {
|
editorProps: {
|
||||||
attributes: {
|
attributes: {
|
||||||
class: modeClass,
|
class: modeClass,
|
||||||
|
@ -107,7 +112,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
content: textToEditorJson(richtext.text.toString()),
|
content: generateJSON(richtext.text.toString(), extensions),
|
||||||
autofocus: 'end',
|
autofocus: 'end',
|
||||||
editable: true,
|
editable: true,
|
||||||
injectCSS: true,
|
injectCSS: true,
|
||||||
|
@ -182,61 +187,6 @@ function editorJsonToText(json: JSONContent): string {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
function textToEditorJson(text: string): JSONContent {
|
|
||||||
if (text === '' || text.length === 0) {
|
|
||||||
return {
|
|
||||||
text: '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const lines = text.split('\n')
|
|
||||||
const docContent: JSONContent[] = []
|
|
||||||
|
|
||||||
for (const line of lines) {
|
|
||||||
if (line.trim() === '') {
|
|
||||||
continue // skip empty lines
|
|
||||||
}
|
|
||||||
|
|
||||||
const paragraphContent: JSONContent[] = []
|
|
||||||
let position = 0
|
|
||||||
|
|
||||||
while (position < line.length) {
|
|
||||||
if (line[position] === '@') {
|
|
||||||
// Handle mentions
|
|
||||||
let endPosition = position + 1
|
|
||||||
while (endPosition < line.length && /\S/.test(line[endPosition])) {
|
|
||||||
endPosition++
|
|
||||||
}
|
|
||||||
const mentionId = line.substring(position + 1, endPosition)
|
|
||||||
paragraphContent.push({
|
|
||||||
type: 'mention',
|
|
||||||
attrs: {id: mentionId},
|
|
||||||
})
|
|
||||||
position = endPosition
|
|
||||||
} else {
|
|
||||||
// Handle regular text
|
|
||||||
let endPosition = line.indexOf('@', position)
|
|
||||||
if (endPosition === -1) endPosition = line.length
|
|
||||||
paragraphContent.push({
|
|
||||||
type: 'text',
|
|
||||||
text: line.substring(position, endPosition),
|
|
||||||
})
|
|
||||||
position = endPosition
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
docContent.push({
|
|
||||||
type: 'paragraph',
|
|
||||||
content: paragraphContent,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'doc',
|
|
||||||
content: docContent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
import {Mark} from '@tiptap/core'
|
import {Mark} from '@tiptap/core'
|
||||||
import {Plugin, PluginKey} from '@tiptap/pm/state'
|
import {Plugin, PluginKey} from '@tiptap/pm/state'
|
||||||
import {findChildren} from '@tiptap/core'
|
|
||||||
import {Node as ProsemirrorNode} from '@tiptap/pm/model'
|
import {Node as ProsemirrorNode} from '@tiptap/pm/model'
|
||||||
import {Decoration, DecorationSet} from '@tiptap/pm/view'
|
import {Decoration, DecorationSet} from '@tiptap/pm/view'
|
||||||
import {isValidDomain} from 'lib/strings/url-helpers'
|
import {isValidDomain} from 'lib/strings/url-helpers'
|
||||||
|
@ -36,20 +35,20 @@ export const LinkDecorator = Mark.create({
|
||||||
function getDecorations(doc: ProsemirrorNode) {
|
function getDecorations(doc: ProsemirrorNode) {
|
||||||
const decorations: Decoration[] = []
|
const decorations: Decoration[] = []
|
||||||
|
|
||||||
findChildren(doc, node => node.type.name === 'paragraph').forEach(
|
doc.descendants((node, pos) => {
|
||||||
paragraphNode => {
|
if (node.isText && node.text) {
|
||||||
const textContent = paragraphNode.node.textContent
|
const textContent = node.textContent
|
||||||
|
|
||||||
// links
|
// links
|
||||||
iterateUris(textContent, (from, to) => {
|
iterateUris(textContent, (from, to) => {
|
||||||
decorations.push(
|
decorations.push(
|
||||||
Decoration.inline(paragraphNode.pos + from, paragraphNode.pos + to, {
|
Decoration.inline(pos + from, pos + to, {
|
||||||
class: 'autolink',
|
class: 'autolink',
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
)
|
})
|
||||||
|
|
||||||
return DecorationSet.create(doc, decorations)
|
return DecorationSet.create(doc, decorations)
|
||||||
}
|
}
|
||||||
|
|
14
yarn.lock
14
yarn.lock
|
@ -4972,6 +4972,13 @@
|
||||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.1.6.tgz#23f36114ee164e3da2fd326145ac7b7f8bd34c56"
|
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.1.6.tgz#23f36114ee164e3da2fd326145ac7b7f8bd34c56"
|
||||||
integrity sha512-CqV0N6ngoXZFeJGlQ86FSZJ/0k7+BN3S6aSUcb5DRAKsSEv/Ga1LvSG24sHy+dwjTuj3EtRPJSVZTFcSB17ZSA==
|
integrity sha512-CqV0N6ngoXZFeJGlQ86FSZJ/0k7+BN3S6aSUcb5DRAKsSEv/Ga1LvSG24sHy+dwjTuj3EtRPJSVZTFcSB17ZSA==
|
||||||
|
|
||||||
|
"@tiptap/html@^2.1.11":
|
||||||
|
version "2.1.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tiptap/html/-/html-2.1.11.tgz#998421b526f200d01c549f37eb8fae2a0d1f0ed6"
|
||||||
|
integrity sha512-VKmBb1c3YN9hZfBzkV+QERf3ZWBUHHxjv2/BOr/Dw6mbb6+0iA1nxO9vQYPUb+xAmlm0n8vWwc7YQ8rxBwTKWQ==
|
||||||
|
dependencies:
|
||||||
|
zeed-dom "^0.9.19"
|
||||||
|
|
||||||
"@tiptap/pm@^2.0.0-beta.220":
|
"@tiptap/pm@^2.0.0-beta.220":
|
||||||
version "2.1.6"
|
version "2.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.1.6.tgz#4c196a7147fedd71316ef3413bb0e98d5c97726d"
|
resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.1.6.tgz#4c196a7147fedd71316ef3413bb0e98d5c97726d"
|
||||||
|
@ -19227,6 +19234,13 @@ yocto-queue@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
|
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
|
||||||
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
|
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
|
||||||
|
|
||||||
|
zeed-dom@^0.9.19:
|
||||||
|
version "0.9.26"
|
||||||
|
resolved "https://registry.yarnpkg.com/zeed-dom/-/zeed-dom-0.9.26.tgz#f0127d1024b34a1233a321bd6d0275b3ba998b30"
|
||||||
|
integrity sha512-HWjX8rA3Y/RI32zby3KIN1D+mgskce+She4K7kRyyx62OiVxJ5FnYm8vWq0YVAja3Tf2S1M0XAc6O2lRFcMgcQ==
|
||||||
|
dependencies:
|
||||||
|
css-what "^6.1.0"
|
||||||
|
|
||||||
zeego@^1.6.2:
|
zeego@^1.6.2:
|
||||||
version "1.7.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/zeego/-/zeego-1.7.0.tgz#8034adb842199c4ccf21bcb19877800bff18606b"
|
resolved "https://registry.yarnpkg.com/zeego/-/zeego-1.7.0.tgz#8034adb842199c4ccf21bcb19877800bff18606b"
|
||||||
|
|
Loading…
Reference in New Issue