import React, { createElement, Dispatch, useCallback, useEffect } from 'react' import { Keyboard, Pressable, StyleSheet, Text, TextInput, View } from 'react-native' import { SafeAreaView } from 'react-native-safe-area-context' import { Feather } from '@expo/vector-icons' import { debounce, differenceWith, isEqual } from 'lodash' import Autolinker from 'src/modules/autolinker' import PostSuggestions from './PostSuggestions' import PostEmojis from './PostEmojis' import { useQuery } from 'react-query' import { emojisFetch } from 'src/stacks/common/emojisFetch' import { PostAction, PostState } from '../PostToot' export interface Props { postState: PostState postDispatch: Dispatch } const PostMain: React.FC = ({ postState, postDispatch }) => { useEffect(() => { Keyboard.addListener('keyboardDidShow', _keyboardDidShow) Keyboard.addListener('keyboardDidHide', _keyboardDidHide) return () => { Keyboard.removeListener('keyboardDidShow', _keyboardDidShow) Keyboard.removeListener('keyboardDidHide', _keyboardDidHide) } }, []) const _keyboardDidShow = (props: any) => { postDispatch({ type: 'height', payload: { keyboard: props.endCoordinates.height } }) } const _keyboardDidHide = () => { postDispatch({ type: 'height', payload: { keyboard: 0 } }) } const { data: emojisData } = useQuery(['Emojis'], emojisFetch) useEffect(() => { if (emojisData && emojisData.length) { postDispatch({ type: 'emojis', payload: emojisData }) } }, [emojisData]) const debouncedSuggestions = useCallback( debounce( tag => (() => { console.log(tag) postDispatch({ type: 'overlay', payload: 'suggestions' }) postDispatch({ type: 'tag', payload: tag }) })(), 300 ), [] ) let prevTags: PostState['tag'][] = [] const onChangeText = useCallback(({ content, disableDebounce }) => { const tags: PostState['tag'][] = [] Autolinker.link(content, { email: false, phone: false, mention: 'mastodon', hashtag: 'twitter', replaceFn: props => { const type = props.getType() let newType: 'url' | 'accounts' | 'hashtags' switch (type) { case 'mention': newType = 'accounts' break case 'hashtag': newType = 'hashtags' break default: newType = 'url' break } // @ts-ignore tags.push({ type: newType, text: props.getMatchedText(), offset: props.getOffset() }) return } }) const changedTag = differenceWith(prevTags, tags, isEqual) // quick delete causes flicking of suggestion box if (changedTag.length && tags.length && !disableDebounce) { if (changedTag[0]!.type !== 'url') { debouncedSuggestions(changedTag[0]) } } else { postDispatch({ type: 'overlay', payload: null }) postDispatch({ type: 'tag', payload: undefined }) } prevTags = tags let _content = content let contentLength: number = 0 const children = [] tags.forEach(tag => { const parts = _content.split(tag!.text) const prevPart = parts.shift() children.push(prevPart) contentLength = contentLength + prevPart.length children.push( {tag!.text} ) switch (tag!.type) { case 'url': contentLength = contentLength + 23 break case 'accounts': contentLength = contentLength + tag!.text.split(new RegExp('(@.*)@?'))[1].length break case 'hashtags': contentLength = contentLength + tag!.text.length break } _content = parts.join() }) children.push(_content) contentLength = contentLength + _content.length postDispatch({ type: 'text', payload: { count: 500 - contentLength, raw: content, formatted: createElement(Text, null, children) } }) }, []) return ( postDispatch({ type: 'height', payload: { view: nativeEvent.layout.height } }) } > onChangeText({ content })} onContentSizeChange={({ nativeEvent }) => { postDispatch({ type: 'height', payload: { editor: nativeEvent.contentSize.height } }) }} onSelectionChange={({ nativeEvent: { selection: { start, end } } }) => { postDispatch({ type: 'selection', payload: { start, end } }) }} scrollEnabled > {postState.text.formatted} {postState.overlay === 'suggestions' ? ( ) : ( <> )} {postState.overlay === 'emojis' ? ( ) : ( <> )} Keyboard.dismiss()}> { if (postState.emojis?.length && postState.overlay === null) { postDispatch({ type: 'overlay', payload: 'emojis' }) } }} /> {postState.text.count} ) } const styles = StyleSheet.create({ main: { flex: 1 }, textInput: { backgroundColor: 'gray' }, suggestions: { flex: 1, backgroundColor: 'lightyellow' }, emojis: { flex: 1, backgroundColor: 'lightblue' }, additions: { height: 44, backgroundColor: 'red', flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center' } }) export default PostMain