import React, {useMemo, useState} from 'react' import {StyleSheet, Text, TouchableOpacity, View} from 'react-native' import {BottomSheetTextInput} from '@gorhom/bottom-sheet' import LinearGradient from 'react-native-linear-gradient' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Autocomplete} from './composer/Autocomplete' import Toast from '../util/Toast' import ProgressCircle from '../util/ProgressCircle' import {useStores} from '../../../state' import * as apilib from '../../../state/lib/api' import {s, colors, gradients} from '../../lib/styles' const MAX_TEXT_LENGTH = 256 const WARNING_TEXT_LENGTH = 200 const DANGER_TEXT_LENGTH = 255 export const snapPoints = ['100%'] const DEBUG_USERNAMES = ['alice.com', 'bob.com', 'carla.com'] export function Component({replyTo}: {replyTo?: string}) { const store = useStores() const [error, setError] = useState('') const [text, setText] = useState('') const [autocompleteOptions, setAutocompleteOptions] = useState([]) const onChangeText = (newText: string) => { if (newText.length > MAX_TEXT_LENGTH) { newText = newText.slice(0, MAX_TEXT_LENGTH) } setText(newText) const prefix = extractTextAutocompletePrefix(newText) if (typeof prefix === 'string') { setAutocompleteOptions( DEBUG_USERNAMES.filter(name => name.includes(prefix)), ) } else if (autocompleteOptions) { setAutocompleteOptions([]) } } const onPressCancel = () => { store.shell.closeModal() } const onPressPublish = async () => { setError('') if (text.trim().length === 0) { setError('Did you want to say anything?') return false } try { await apilib.post(store.api, 'alice.com', text, replyTo) } catch (e: any) { console.error(`Failed to create post: ${e.toString()}`) setError( 'Post failed to upload. Please check your Internet connection and try again.', ) return } store.shell.closeModal() 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)) setAutocompleteOptions([]) } 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)) { return ( {item} ) } return item }) }, [text]) return ( Cancel Post {error !== '' && ( {error} )} onChangeText(text)} placeholder={replyTo ? 'Write your reply' : "What's new?"} style={styles.textInput}> {textDecorated} 0} items={autocompleteOptions} onSelect={onSelectAutocompleteItem} /> ) } 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, }, 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: 18, }, })