add simple fallback implmentation

This commit is contained in:
Samuel Newman
2025-12-30 16:43:38 +02:00
parent 08eede98de
commit d37da80efa
2 changed files with 113 additions and 7 deletions
+3
View File
@@ -74,6 +74,9 @@ a[role='link'][data-no-underline='1']:hover {
height: 0; height: 0;
pointer-events: none; pointer-events: none;
} }
.composer-fallback-input::placeholder {
color: #8d8e96;
}
.ProseMirror .mention { .ProseMirror .mention {
color: #0085ff; color: #0085ff;
} }
@@ -1,13 +1,24 @@
import {lazy, Suspense, useEffect, useState} from 'react' import {
lazy,
Suspense,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useRef,
useState,
} from 'react'
import {StyleSheet, View} from 'react-native' import {StyleSheet, View} from 'react-native'
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated' import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
import {RichText} from '@atproto/api'
import {Trans} from '@lingui/macro' import {Trans} from '@lingui/macro'
import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter' import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useAlf, useTheme} from '#/alf'
import {normalizeTextStyles} from '#/alf/typography'
import {Portal} from '#/components/Portal' import {Portal} from '#/components/Portal'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {type TextInputProps} from './TextInput.types' import {type TextInputProps, type TextInputRef} from './TextInput.types'
import {getImageOrVideoFromUri} from './web/uri-to-media' import {getImageOrVideoFromUri} from './web/uri-to-media'
const RichTextInput = lazy(async () => { const RichTextInput = lazy(async () => {
@@ -16,6 +27,101 @@ const RichTextInput = lazy(async () => {
return await import('./web/RichTextInputWeb') return await import('./web/RichTextInputWeb')
}) })
function FallbackTextInput({
ref,
richtext,
placeholder,
webForceMinHeight,
setRichText,
onFocus,
}: Pick<
TextInputProps,
| 'ref'
| 'richtext'
| 'placeholder'
| 'webForceMinHeight'
| 'setRichText'
| 'onFocus'
>) {
const {theme: t, fonts} = useAlf()
const textareaRef = useRef<HTMLTextAreaElement>(null)
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
const newText = e.target.value
const newRt = new RichText({text: newText})
newRt.detectFacetsWithoutResolution()
setRichText(newRt)
},
[setRichText],
)
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
textInputWebEmitter.emit('publish')
e.preventDefault()
}
}, [])
useImperativeHandle(
ref,
(): TextInputRef => ({
focus: () => textareaRef.current?.focus(),
blur: () => textareaRef.current?.blur(),
getCursorPosition: () => undefined,
maybeClosePopup: () => false,
}),
)
useEffect(() => {
// Move cursor to end after autofocus
const textarea = textareaRef.current
if (textarea) {
const len = textarea.value.length
textarea.setSelectionRange(len, len)
}
}, [])
const inputStyle = useMemo((): React.CSSProperties => {
const style = normalizeTextStyles(
[a.text_lg, a.leading_snug, t.atoms.text],
{
fontScale: fonts.scaleMultiplier,
fontFamily: fonts.family,
flags: {},
},
)
style.lineHeight = style.lineHeight
? ((style.lineHeight + 'px') as unknown as number)
: undefined
style.minHeight = webForceMinHeight ? 140 : undefined
return {
...style,
width: '100%',
border: 'none',
outline: 'none',
resize: 'none',
background: 'transparent',
padding: 0,
margin: 0,
} as React.CSSProperties
}, [t, fonts, webForceMinHeight])
return (
<textarea
ref={textareaRef}
className="composer-fallback-input"
autoFocus
value={richtext.text}
onChange={handleChange}
onKeyDown={handleKeyDown}
onFocus={onFocus}
placeholder={placeholder}
style={inputStyle}
/>
)
}
export function TextInput(props: TextInputProps) { export function TextInput(props: TextInputProps) {
const {hasRightPadding, isActive, onPhotoPasted, onPressPublish} = props const {hasRightPadding, isActive, onPhotoPasted, onPressPublish} = props
@@ -90,7 +196,7 @@ export function TextInput(props: TextInputProps) {
return ( return (
<> <>
<View style={[styles.container, hasRightPadding && styles.rightPadding]}> <View style={[styles.container, hasRightPadding && styles.rightPadding]}>
<Suspense fallback={<View style={styles.fallback} />}> <Suspense fallback={<FallbackTextInput {...props} />}>
<RichTextInput {...props} /> <RichTextInput {...props} />
</Suspense> </Suspense>
</View> </View>
@@ -163,7 +269,4 @@ const styles = StyleSheet.create({
borderRadius: 8, borderRadius: 8,
borderWidth: 2, borderWidth: 2,
}, },
fallback: {
minHeight: 140,
},
}) })