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.zio/stable
parent
5e63d3164b
commit
4a59178cd2
|
@ -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…
Reference in New Issue