import React, {useEffect, useMemo, useState} from 'react' import {observer} from 'mobx-react-lite' import { ActivityIndicator, KeyboardAvoidingView, SafeAreaView, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native' import LinearGradient from 'react-native-linear-gradient' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {UserAutocompleteViewModel} from '../../../state/models/user-autocomplete-view' import {Autocomplete} from './Autocomplete' import Toast from '../util/Toast' import ProgressCircle from '../util/ProgressCircle' import {useStores} from '../../../state' import * as apilib from '../../../state/lib/api' import {ComposerOpts} from '../../../state/models/shell-ui' import {s, colors, gradients} from '../../lib/styles' const MAX_TEXT_LENGTH = 256 const WARNING_TEXT_LENGTH = 200 const DANGER_TEXT_LENGTH = MAX_TEXT_LENGTH export const ComposePost = observer(function ComposePost({ replyTo, onPost, onClose, }: { replyTo?: ComposerOpts['replyTo'] onPost?: ComposerOpts['onPost'] onClose: () => void }) { const store = useStores() const [isProcessing, setIsProcessing] = useState(false) const [error, setError] = useState('') const [text, setText] = useState('') const autocompleteView = useMemo( () => new UserAutocompleteViewModel(store), [], ) useEffect(() => { autocompleteView.setup() }) const onChangeText = (newText: string) => { setText(newText) const prefix = extractTextAutocompletePrefix(newText) if (typeof prefix === 'string') { autocompleteView.setActive(true) autocompleteView.setPrefix(prefix) } else { autocompleteView.setActive(false) } } const onPressCancel = () => { onClose() } const onPressPublish = async () => { if (isProcessing) { return } if (text.length > MAX_TEXT_LENGTH) { return } setError('') if (text.trim().length === 0) { setError('Did you want to say anything?') return false } setIsProcessing(true) try { await apilib.post(store, text, replyTo, autocompleteView.knownHandles) } catch (e: any) { console.error(`Failed to create post: ${e.toString()}`) setError( 'Post failed to upload. Please check your Internet connection and try again.', ) setIsProcessing(false) return } onPost?.() onClose() Toast.show(`Your ${replyTo ? 'reply' : 'post'} has been published`, { duration: Toast.durations.LONG, position: Toast.positions.TOP, shadow: true, animation: true, hideOnPress: true, }) } const onSelectAutocompleteItem = (item: string) => { setText(replaceTextAutocompletePrefix(text, item)) autocompleteView.setActive(false) } const canPost = text.length <= MAX_TEXT_LENGTH const progressColor = text.length > DANGER_TEXT_LENGTH ? '#e60000' : text.length > WARNING_TEXT_LENGTH ? '#f7c600' : undefined const textDecorated = useMemo(() => { return (text || '').split(/(\s)/g).map((item, i) => { if ( /^@[a-zA-Z0-9\.-]+$/g.test(item) && autocompleteView.knownHandles.has(item.slice(1)) ) { return ( {item} ) } return item }) }, [text]) return ( Cancel {isProcessing ? ( ) : canPost ? ( Post ) : ( Post )} {error !== '' && ( {error} )} onChangeText(text)} placeholder={replyTo ? 'Write your reply' : "What's new?"} style={styles.textInput}> {textDecorated} {text.length} / {MAX_TEXT_LENGTH} ) }) const atPrefixRegex = /@([\S]*)$/i function extractTextAutocompletePrefix(text: string) { const match = atPrefixRegex.exec(text) if (match) { return match[1] } return undefined } function replaceTextAutocompletePrefix(text: string, item: string) { return text.replace(atPrefixRegex, `@${item} `) } const styles = StyleSheet.create({ outer: { flexDirection: 'column', backgroundColor: '#fff', padding: 15, height: '100%', }, topbar: { flexDirection: 'row', alignItems: 'center', paddingTop: 10, paddingBottom: 5, paddingHorizontal: 5, height: 50, }, postBtn: { borderRadius: 20, paddingHorizontal: 20, paddingVertical: 6, }, errorLine: { flexDirection: 'row', backgroundColor: colors.red1, borderRadius: 6, paddingHorizontal: 8, paddingVertical: 6, marginVertical: 6, }, errorIcon: { borderWidth: 1, borderColor: colors.red4, color: colors.red4, borderRadius: 30, width: 16, height: 16, alignItems: 'center', justifyContent: 'center', marginRight: 5, }, textInput: { flex: 1, padding: 5, fontSize: 21, }, })