Composer update (react-query refactor) (#1899)

* Move composer state to a context

* Rework composer to use RQ

---------

Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
Paul Frazee 2023-11-14 10:41:55 -08:00 committed by GitHub
parent c687172de9
commit 0a26e78dcb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 269 additions and 239 deletions

View file

@ -3,6 +3,7 @@ import React, {
useCallback,
useRef,
useMemo,
useState,
ComponentProps,
} from 'react'
import {
@ -18,7 +19,6 @@ import PasteInput, {
} from '@mattermost/react-native-paste-input'
import {AppBskyRichtextFacet, RichText} from '@atproto/api'
import isEqual from 'lodash.isequal'
import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
import {Autocomplete} from './mobile/Autocomplete'
import {Text} from 'view/com/util/text/Text'
import {cleanError} from 'lib/strings/errors'
@ -38,7 +38,6 @@ interface TextInputProps extends ComponentProps<typeof RNTextInput> {
richtext: RichText
placeholder: string
suggestedLinks: Set<string>
autocompleteView: UserAutocompleteModel
setRichText: (v: RichText | ((v: RichText) => RichText)) => void
onPhotoPasted: (uri: string) => void
onPressPublish: (richtext: RichText) => Promise<void>
@ -56,7 +55,6 @@ export const TextInput = forwardRef(function TextInputImpl(
richtext,
placeholder,
suggestedLinks,
autocompleteView,
setRichText,
onPhotoPasted,
onSuggestedLinksChanged,
@ -69,6 +67,7 @@ export const TextInput = forwardRef(function TextInputImpl(
const textInput = useRef<PasteInputRef>(null)
const textInputSelection = useRef<Selection>({start: 0, end: 0})
const theme = useTheme()
const [autocompletePrefix, setAutocompletePrefix] = useState('')
React.useImperativeHandle(ref, () => ({
focus: () => textInput.current?.focus(),
@ -99,10 +98,9 @@ export const TextInput = forwardRef(function TextInputImpl(
textInputSelection.current?.start || 0,
)
if (prefix) {
autocompleteView.setActive(true)
autocompleteView.setPrefix(prefix.value)
} else {
autocompleteView.setActive(false)
setAutocompletePrefix(prefix.value)
} else if (autocompletePrefix) {
setAutocompletePrefix('')
}
const set: Set<string> = new Set()
@ -139,7 +137,8 @@ export const TextInput = forwardRef(function TextInputImpl(
},
[
setRichText,
autocompleteView,
autocompletePrefix,
setAutocompletePrefix,
suggestedLinks,
onSuggestedLinksChanged,
onPhotoPasted,
@ -179,9 +178,9 @@ export const TextInput = forwardRef(function TextInputImpl(
item,
),
)
autocompleteView.setActive(false)
setAutocompletePrefix('')
},
[onChangeText, richtext, autocompleteView],
[onChangeText, richtext, setAutocompletePrefix],
)
const textDecorated = useMemo(() => {
@ -221,7 +220,7 @@ export const TextInput = forwardRef(function TextInputImpl(
{textDecorated}
</PasteInput>
<Autocomplete
view={autocompleteView}
prefix={autocompletePrefix}
onSelect={onSelectAutocompleteItem}
/>
</View>

View file

@ -11,13 +11,15 @@ import {Paragraph} from '@tiptap/extension-paragraph'
import {Placeholder} from '@tiptap/extension-placeholder'
import {Text} from '@tiptap/extension-text'
import isEqual from 'lodash.isequal'
import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
import {createSuggestion} from './web/Autocomplete'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
import {isUriImage, blobToDataUri} from 'lib/media/util'
import {Emoji} from './web/EmojiPicker.web'
import {LinkDecorator} from './web/LinkDecorator'
import {generateJSON} from '@tiptap/html'
import {ActorAutocomplete} from '#/state/queries/actor-autocomplete'
import {useSession} from '#/state/session'
import {useMyFollowsQuery} from '#/state/queries/my-follows'
export interface TextInputRef {
focus: () => void
@ -28,7 +30,6 @@ interface TextInputProps {
richtext: RichText
placeholder: string
suggestedLinks: Set<string>
autocompleteView: UserAutocompleteModel
setRichText: (v: RichText | ((v: RichText) => RichText)) => void
onPhotoPasted: (uri: string) => void
onPressPublish: (richtext: RichText) => Promise<void>
@ -43,7 +44,6 @@ export const TextInput = React.forwardRef(function TextInputImpl(
richtext,
placeholder,
suggestedLinks,
autocompleteView,
setRichText,
onPhotoPasted,
onPressPublish,
@ -52,6 +52,16 @@ export const TextInput = React.forwardRef(function TextInputImpl(
TextInputProps,
ref,
) {
const {agent} = useSession()
const autocomplete = React.useMemo(
() => new ActorAutocomplete(agent),
[agent],
)
const {data: follows} = useMyFollowsQuery()
if (follows) {
autocomplete.setFollows(follows)
}
const modeClass = useColorSchemeStyle('ProseMirror-light', 'ProseMirror-dark')
const extensions = React.useMemo(
() => [
@ -61,7 +71,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
HTMLAttributes: {
class: 'mention',
},
suggestion: createSuggestion({autocompleteView}),
suggestion: createSuggestion({autocomplete}),
}),
Paragraph,
Placeholder.configure({
@ -71,7 +81,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
History,
Hardbreak,
],
[autocompleteView, placeholder],
[autocomplete, placeholder],
)
React.useEffect(() => {

View file

@ -1,31 +1,33 @@
import React, {useEffect} from 'react'
import {Animated, TouchableOpacity, StyleSheet, View} from 'react-native'
import {observer} from 'mobx-react-lite'
import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
import {usePalette} from 'lib/hooks/usePalette'
import {Text} from 'view/com/util/text/Text'
import {UserAvatar} from 'view/com/util/UserAvatar'
import {useGrapheme} from '../hooks/useGrapheme'
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
export const Autocomplete = observer(function AutocompleteImpl({
view,
prefix,
onSelect,
}: {
view: UserAutocompleteModel
prefix: string
onSelect: (item: string) => void
}) {
const pal = usePalette('default')
const positionInterp = useAnimatedValue(0)
const {getGraphemeString} = useGrapheme()
const isActive = !!prefix
const {data: suggestions} = useActorAutocompleteQuery(prefix)
useEffect(() => {
Animated.timing(positionInterp, {
toValue: view.isActive ? 1 : 0,
toValue: isActive ? 1 : 0,
duration: 200,
useNativeDriver: true,
}).start()
}, [positionInterp, view.isActive])
}, [positionInterp, isActive])
const topAnimStyle = {
transform: [
@ -40,10 +42,10 @@ export const Autocomplete = observer(function AutocompleteImpl({
return (
<Animated.View style={topAnimStyle}>
{view.isActive ? (
{isActive ? (
<View style={[pal.view, styles.container, pal.border]}>
{view.suggestions.length > 0 ? (
view.suggestions.slice(0, 5).map(item => {
{suggestions?.length ? (
suggestions.slice(0, 5).map(item => {
// Eventually use an average length
const MAX_CHARS = 40
const MAX_HANDLE_CHARS = 20

View file

@ -12,7 +12,7 @@ import {
SuggestionProps,
SuggestionKeyDownProps,
} from '@tiptap/suggestion'
import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
import {ActorAutocomplete} from '#/state/queries/actor-autocomplete'
import {usePalette} from 'lib/hooks/usePalette'
import {Text} from 'view/com/util/text/Text'
import {UserAvatar} from 'view/com/util/UserAvatar'
@ -23,15 +23,14 @@ interface MentionListRef {
}
export function createSuggestion({
autocompleteView,
autocomplete,
}: {
autocompleteView: UserAutocompleteModel
autocomplete: ActorAutocomplete
}): Omit<SuggestionOptions, 'editor'> {
return {
async items({query}) {
autocompleteView.setActive(true)
await autocompleteView.setPrefix(query)
return autocompleteView.suggestions.slice(0, 8)
await autocomplete.query(query)
return autocomplete.suggestions.slice(0, 8)
},
render: () => {