The tiptap useEditor() hook creates an awkward challenge for passing event handlers into its plugins and native events. By introducing a memoized editor, we should be able to shuttle events out of tiptap without retriggering the useEditor hook. The emitter can then change its registered handlers with each state update.
This commit is contained in:
parent
5e63d3164b
commit
4a59178cd2
4 changed files with 32 additions and 15 deletions
|
@ -70,6 +70,7 @@
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"bcp-47-match": "^2.0.3",
|
"bcp-47-match": "^2.0.3",
|
||||||
"email-validator": "^2.0.4",
|
"email-validator": "^2.0.4",
|
||||||
|
"eventemitter3": "^5.0.1",
|
||||||
"expo": "~48.0.18",
|
"expo": "~48.0.18",
|
||||||
"expo-application": "~5.1.1",
|
"expo-application": "~5.1.1",
|
||||||
"expo-build-properties": "~0.5.1",
|
"expo-build-properties": "~0.5.1",
|
||||||
|
|
|
@ -151,7 +151,7 @@ export const ComposePost = observer(function ComposePost({
|
||||||
[gallery, track],
|
[gallery, track],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onPressPublish = async (rt: RichText) => {
|
const onPressPublish = async () => {
|
||||||
if (isProcessing || graphemeLength > MAX_GRAPHEME_LENGTH) {
|
if (isProcessing || graphemeLength > MAX_GRAPHEME_LENGTH) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ export const ComposePost = observer(function ComposePost({
|
||||||
|
|
||||||
setError('')
|
setError('')
|
||||||
|
|
||||||
if (rt.text.trim().length === 0 && gallery.isEmpty) {
|
if (richtext.text.trim().length === 0 && gallery.isEmpty) {
|
||||||
setError('Did you want to say anything?')
|
setError('Did you want to say anything?')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ export const ComposePost = observer(function ComposePost({
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await apilib.post(store, {
|
await apilib.post(store, {
|
||||||
rawText: rt.text,
|
rawText: richtext.text,
|
||||||
replyTo: replyTo?.uri,
|
replyTo: replyTo?.uri,
|
||||||
images: gallery.images,
|
images: gallery.images,
|
||||||
quote,
|
quote,
|
||||||
|
@ -245,9 +245,7 @@ export const ComposePost = observer(function ComposePost({
|
||||||
) : canPost ? (
|
) : canPost ? (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
testID="composerPublishBtn"
|
testID="composerPublishBtn"
|
||||||
onPress={() => {
|
onPress={onPressPublish}
|
||||||
onPressPublish(richtext)
|
|
||||||
}}
|
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={replyTo ? 'Publish reply' : 'Publish post'}
|
accessibilityLabel={replyTo ? 'Publish reply' : 'Publish post'}
|
||||||
accessibilityHint={
|
accessibilityHint={
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {RichText} from '@atproto/api'
|
import {RichText} from '@atproto/api'
|
||||||
|
import EventEmitter from 'eventemitter3'
|
||||||
import {useEditor, EditorContent, JSONContent} from '@tiptap/react'
|
import {useEditor, EditorContent, JSONContent} from '@tiptap/react'
|
||||||
import {Document} from '@tiptap/extension-document'
|
import {Document} from '@tiptap/extension-document'
|
||||||
import History from '@tiptap/extension-history'
|
import History from '@tiptap/extension-history'
|
||||||
|
@ -53,6 +54,22 @@ export const TextInput = React.forwardRef(
|
||||||
'ProseMirror-dark',
|
'ProseMirror-dark',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// we use a memoized emitter to propagate events out of tiptap
|
||||||
|
// without triggering re-runs of the useEditor hook
|
||||||
|
const emitter = React.useMemo(() => new EventEmitter(), [])
|
||||||
|
React.useEffect(() => {
|
||||||
|
emitter.addListener('publish', onPressPublish)
|
||||||
|
return () => {
|
||||||
|
emitter.removeListener('publish', onPressPublish)
|
||||||
|
}
|
||||||
|
}, [emitter, onPressPublish])
|
||||||
|
React.useEffect(() => {
|
||||||
|
emitter.addListener('photo-pasted', onPhotoPasted)
|
||||||
|
return () => {
|
||||||
|
emitter.removeListener('photo-pasted', onPhotoPasted)
|
||||||
|
}
|
||||||
|
}, [emitter, onPhotoPasted])
|
||||||
|
|
||||||
const editor = useEditor(
|
const editor = useEditor(
|
||||||
{
|
{
|
||||||
extensions: [
|
extensions: [
|
||||||
|
@ -87,17 +104,13 @@ export const TextInput = React.forwardRef(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
getImageFromUri(items, onPhotoPasted)
|
getImageFromUri(items, (uri: string) => {
|
||||||
|
emitter.emit('photo-pasted', uri)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
handleKeyDown: (_, event) => {
|
handleKeyDown: (_, event) => {
|
||||||
if ((event.metaKey || event.ctrlKey) && event.code === 'Enter') {
|
if ((event.metaKey || event.ctrlKey) && event.code === 'Enter') {
|
||||||
// Workaround relying on previous state from `setRichText` to
|
emitter.emit('publish')
|
||||||
// get the updated text content during editor initialization
|
|
||||||
setRichText((state: RichText) => {
|
|
||||||
onPressPublish(state)
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -118,7 +131,7 @@ export const TextInput = React.forwardRef(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[modeClass],
|
[modeClass, emitter],
|
||||||
)
|
)
|
||||||
|
|
||||||
React.useImperativeHandle(ref, () => ({
|
React.useImperativeHandle(ref, () => ({
|
||||||
|
|
|
@ -10150,6 +10150,11 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||||
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
|
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
|
||||||
|
|
||||||
|
eventemitter3@^5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
|
||||||
|
integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
|
||||||
|
|
||||||
events@3.3.0, events@^3.2.0, events@^3.3.0:
|
events@3.3.0, events@^3.2.0, events@^3.3.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue