import ImagePicker from 'expo-image-picker' import { forEach, groupBy, sortBy } from 'lodash' import React, { Dispatch, useEffect, useMemo, useRef } from 'react' import { View, ActivityIndicator, FlatList, Pressable, StyleSheet, Text, TextInput, Image } from 'react-native' import { useQuery } from 'react-query' import Emojis from 'src/components/Timelines/Timeline/Shared/Emojis' import { emojisFetch } from 'src/utils/fetches/emojisFetch' import { searchFetch } from 'src/utils/fetches/searchFetch' import { StyleConstants } from 'src/utils/styles/constants' import { useTheme } from 'src/utils/styles/ThemeManager' import { PostAction, PostState } from '../Compose' import ComposeActions from './Actions' import ComposeAttachments from './Attachments' import ComposeEmojis from './Emojis' import ComposePoll from './Poll' import ComposeTextInput from './TextInput' import updateText from './updateText' export interface Props { postState: PostState postDispatch: Dispatch } const ComposeRoot: React.FC = ({ postState, postDispatch }) => { const { theme } = useTheme() const { isFetching, isSuccess, data, refetch } = useQuery( [ 'Search', { type: postState.tag?.type, term: postState.tag?.text.substring(1) } ], searchFetch, { enabled: false } ) useEffect(() => { refetch() }, [postState.tag?.text]) useEffect(() => { ;(async () => { const { status } = await ImagePicker.requestCameraRollPermissionsAsync() if (status !== 'granted') { alert('Sorry, we need camera roll permissions to make this work!') } })() }, []) const { data: emojisData } = useQuery(['Emojis'], emojisFetch) useEffect(() => { if (emojisData && emojisData.length) { let sortedEmojis: { title: string; data: Mastodon.Emoji[] }[] = [] forEach( groupBy(sortBy(emojisData, ['category', 'shortcode']), 'category'), (value, key) => sortedEmojis.push({ title: key, data: value }) ) postDispatch({ type: 'emoji', payload: { ...postState.emoji, emojis: sortedEmojis } }) } }, [emojisData]) const textInputRef = useRef(null) const listFooter = useMemo(() => { return ( <> {postState.emoji.active && ( )} {postState.attachments.length > 0 && ( )} {postState.poll.active && ( )} ) }, [ postState.emoji.active, postState.attachments.length, postState.poll.active ]) const listEmpty = useMemo(() => { if (isFetching) { return } }, [isFetching]) return ( } ListFooterComponent={listFooter} ListEmptyComponent={listEmpty} data={postState.tag && isSuccess ? data[postState.tag.type] : []} renderItem={({ item, index }) => ( { updateText({ postState: { ...postState, selection: { start: postState.tag!.offset, end: postState.tag!.offset + postState.tag!.text.length + 1 } }, postDispatch, newText: item.acct ? `@${item.acct}` : `#${item.name}`, type: 'suggestion' }) textInputRef.current?.focus() }} style={styles.suggestion} > {item.acct ? ( {item.emojis.length ? ( ) : ( item.display_name || item.username )} @{item.acct} ) : ( #{item.name} )} )} /> ) } const styles = StyleSheet.create({ base: { flex: 1 }, contentView: { flex: 1 }, attachments: { flex: 1, height: 100 }, poll: { flex: 1, padding: StyleConstants.Spacing.Global.PagePadding }, suggestion: { flex: 1 }, account: { flex: 1, flexDirection: 'row', alignItems: 'center', paddingTop: StyleConstants.Spacing.S, paddingBottom: StyleConstants.Spacing.S, paddingLeft: StyleConstants.Spacing.Global.PagePadding, paddingRight: StyleConstants.Spacing.Global.PagePadding, borderBottomWidth: StyleSheet.hairlineWidth }, accountAvatar: { width: StyleConstants.Font.LineHeight.M * 2, height: StyleConstants.Font.LineHeight.M * 2, marginRight: StyleConstants.Spacing.S, borderRadius: StyleConstants.Avatar.S }, accountName: { fontSize: StyleConstants.Font.Size.S, fontWeight: StyleConstants.Font.Weight.Bold, marginBottom: StyleConstants.Spacing.XS }, accountAccount: { fontSize: StyleConstants.Font.Size.S }, hashtag: { flex: 1, paddingTop: StyleConstants.Spacing.S, paddingBottom: StyleConstants.Spacing.S, paddingLeft: StyleConstants.Spacing.Global.PagePadding, paddingRight: StyleConstants.Spacing.Global.PagePadding, borderBottomWidth: StyleSheet.hairlineWidth }, hashtagText: { fontSize: StyleConstants.Font.Size.S, fontWeight: StyleConstants.Font.Weight.Bold, marginBottom: StyleConstants.Spacing.XS }, emojis: { flex: 1 } }) export default ComposeRoot