Update the web composer textinput to an emitter (close #1193) (#1205)

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:
Paul Frazee 2023-08-17 13:39:59 -07:00 committed by GitHub
parent 5e63d3164b
commit 4a59178cd2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 32 additions and 15 deletions

View file

@ -70,6 +70,7 @@
"base64-js": "^1.5.1",
"bcp-47-match": "^2.0.3",
"email-validator": "^2.0.4",
"eventemitter3": "^5.0.1",
"expo": "~48.0.18",
"expo-application": "~5.1.1",
"expo-build-properties": "~0.5.1",

View file

@ -151,7 +151,7 @@ export const ComposePost = observer(function ComposePost({
[gallery, track],
)
const onPressPublish = async (rt: RichText) => {
const onPressPublish = async () => {
if (isProcessing || graphemeLength > MAX_GRAPHEME_LENGTH) {
return
}
@ -161,7 +161,7 @@ export const ComposePost = observer(function ComposePost({
setError('')
if (rt.text.trim().length === 0 && gallery.isEmpty) {
if (richtext.text.trim().length === 0 && gallery.isEmpty) {
setError('Did you want to say anything?')
return
}
@ -170,7 +170,7 @@ export const ComposePost = observer(function ComposePost({
try {
await apilib.post(store, {
rawText: rt.text,
rawText: richtext.text,
replyTo: replyTo?.uri,
images: gallery.images,
quote,
@ -245,9 +245,7 @@ export const ComposePost = observer(function ComposePost({
) : canPost ? (
<TouchableOpacity
testID="composerPublishBtn"
onPress={() => {
onPressPublish(richtext)
}}
onPress={onPressPublish}
accessibilityRole="button"
accessibilityLabel={replyTo ? 'Publish reply' : 'Publish post'}
accessibilityHint={

View file

@ -1,6 +1,7 @@
import React from 'react'
import {StyleSheet, View} from 'react-native'
import {RichText} from '@atproto/api'
import EventEmitter from 'eventemitter3'
import {useEditor, EditorContent, JSONContent} from '@tiptap/react'
import {Document} from '@tiptap/extension-document'
import History from '@tiptap/extension-history'
@ -53,6 +54,22 @@ export const TextInput = React.forwardRef(
'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(
{
extensions: [
@ -87,17 +104,13 @@ export const TextInput = React.forwardRef(
return
}
getImageFromUri(items, onPhotoPasted)
getImageFromUri(items, (uri: string) => {
emitter.emit('photo-pasted', uri)
})
},
handleKeyDown: (_, event) => {
if ((event.metaKey || event.ctrlKey) && event.code === 'Enter') {
// Workaround relying on previous state from `setRichText` to
// get the updated text content during editor initialization
setRichText((state: RichText) => {
onPressPublish(state)
return state
})
return true
emitter.emit('publish')
}
},
},
@ -118,7 +131,7 @@ export const TextInput = React.forwardRef(
}
},
},
[modeClass],
[modeClass, emitter],
)
React.useImperativeHandle(ref, () => ({

View file

@ -10150,6 +10150,11 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.4:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
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:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"