tooot/src/screens/Shared/Compose/Root.tsx

294 lines
8.8 KiB
TypeScript
Raw Normal View History

2020-12-04 01:17:10 +01:00
import { forEach, groupBy, sortBy } from 'lodash'
import React, { Dispatch, useEffect, useMemo, useRef } from 'react'
2020-11-15 20:29:43 +01:00
import {
2020-12-04 01:17:10 +01:00
View,
ActivityIndicator,
FlatList,
2020-11-15 20:29:43 +01:00
Pressable,
2020-12-06 12:52:29 +01:00
ProgressViewIOS,
2020-11-15 20:29:43 +01:00
StyleSheet,
Text,
TextInput,
2020-12-04 01:17:10 +01:00
Image
2020-11-15 20:29:43 +01:00
} from 'react-native'
2020-11-17 23:57:23 +01:00
import { useQuery } from 'react-query'
2020-12-04 01:17:10 +01:00
import Emojis from 'src/components/Timelines/Timeline/Shared/Emojis'
2020-11-21 13:19:05 +01:00
import { emojisFetch } from 'src/utils/fetches/emojisFetch'
2020-12-04 01:17:10 +01:00
import { searchFetch } from 'src/utils/fetches/searchFetch'
2020-12-03 22:03:06 +01:00
import { StyleConstants } from 'src/utils/styles/constants'
2020-12-04 01:17:10 +01:00
import { useTheme } from 'src/utils/styles/ThemeManager'
2020-12-07 12:31:40 +01:00
import { PostAction, ComposeState } from '../Compose'
2020-12-03 22:03:06 +01:00
import ComposeActions from './Actions'
2020-12-04 01:17:10 +01:00
import ComposeAttachments from './Attachments'
import ComposeEmojis from './Emojis'
import ComposePoll from './Poll'
2020-12-06 23:51:13 +01:00
import ComposeSpoilerInput from './SpoilerInput'
2020-12-04 01:17:10 +01:00
import ComposeTextInput from './TextInput'
import updateText from './updateText'
2020-12-06 21:42:19 +01:00
import * as Permissions from 'expo-permissions'
2020-11-15 20:29:43 +01:00
2020-11-15 22:33:09 +01:00
export interface Props {
2020-12-07 12:31:40 +01:00
composeState: ComposeState
composeDispatch: Dispatch<PostAction>
2020-11-15 20:29:43 +01:00
}
2020-12-07 12:31:40 +01:00
const ComposeRoot: React.FC<Props> = ({ composeState, composeDispatch }) => {
2020-12-03 22:03:06 +01:00
const { theme } = useTheme()
2020-12-04 01:17:10 +01:00
const { isFetching, isSuccess, data, refetch } = useQuery(
[
'Search',
2020-12-07 12:31:40 +01:00
{ type: composeState.tag?.type, term: composeState.tag?.text.substring(1) }
2020-12-04 01:17:10 +01:00
],
searchFetch,
{ enabled: false }
)
useEffect(() => {
2020-12-07 12:31:40 +01:00
if (composeState.tag?.text) {
2020-12-04 19:04:23 +01:00
refetch()
}
2020-12-07 12:31:40 +01:00
}, [composeState.tag?.text])
2020-12-04 01:17:10 +01:00
useEffect(() => {
;(async () => {
2020-12-06 21:42:19 +01:00
Permissions.askAsync(Permissions.CAMERA)
// const permissionGaleery = await ImagePicker.requestCameraRollPermissionsAsync()
// if (permissionGaleery.status !== 'granted') {
// alert('Sorry, we need camera roll permissions to make this work!')
// }
// const permissionCamera = await ImagePicker.requestCameraPermissionsAsync()
// if (permissionCamera.status !== 'granted') {
// alert('Sorry, we need camera roll permissions to make this work!')
// }
})()
}, [])
2020-11-15 20:29:43 +01:00
const { data: emojisData } = useQuery(['Emojis'], emojisFetch)
useEffect(() => {
if (emojisData && emojisData.length) {
2020-12-04 01:17:10 +01:00
let sortedEmojis: { title: string; data: Mastodon.Emoji[] }[] = []
forEach(
groupBy(sortBy(emojisData, ['category', 'shortcode']), 'category'),
(value, key) => sortedEmojis.push({ title: key, data: value })
)
2020-12-07 12:31:40 +01:00
composeDispatch({
2020-12-04 01:17:10 +01:00
type: 'emoji',
2020-12-07 12:31:40 +01:00
payload: { ...composeState.emoji, emojis: sortedEmojis }
2020-12-04 01:17:10 +01:00
})
2020-11-15 20:29:43 +01:00
}
}, [emojisData])
2020-12-03 22:03:06 +01:00
const textInputRef = useRef<TextInput>(null)
2020-12-04 01:17:10 +01:00
const listEmpty = useMemo(() => {
if (isFetching) {
return <ActivityIndicator />
}
}, [isFetching])
return (
<View style={styles.base}>
2020-12-06 12:52:29 +01:00
<ProgressViewIOS
2020-12-07 12:31:40 +01:00
progress={composeState.attachmentUploadProgress?.progress || 0}
2020-12-06 12:52:29 +01:00
progressViewStyle='bar'
/>
2020-12-04 01:17:10 +01:00
<FlatList
2020-12-06 22:32:36 +01:00
keyboardShouldPersistTaps='handled'
2020-12-04 01:17:10 +01:00
ListHeaderComponent={
2020-12-06 23:51:13 +01:00
<>
2020-12-07 12:31:40 +01:00
{composeState.spoiler.active ? (
2020-12-06 23:51:13 +01:00
<ComposeSpoilerInput
2020-12-07 12:31:40 +01:00
composeState={composeState}
composeDispatch={composeDispatch}
2020-12-06 23:51:13 +01:00
/>
) : null}
<ComposeTextInput
2020-12-07 12:31:40 +01:00
composeState={composeState}
composeDispatch={composeDispatch}
2020-12-06 23:51:13 +01:00
textInputRef={textInputRef}
/>
</>
2020-12-04 01:17:10 +01:00
}
2020-12-06 21:42:19 +01:00
ListFooterComponent={
<>
2020-12-07 12:31:40 +01:00
{composeState.emoji.active && (
2020-12-06 21:42:19 +01:00
<View style={styles.emojis}>
<ComposeEmojis
textInputRef={textInputRef}
2020-12-07 12:31:40 +01:00
composeState={composeState}
composeDispatch={composeDispatch}
2020-12-06 21:42:19 +01:00
/>
</View>
)}
2020-12-07 12:31:40 +01:00
{(composeState.attachments.uploads.length > 0 ||
composeState.attachmentUploadProgress) && (
2020-12-06 21:42:19 +01:00
<View style={styles.attachments}>
<ComposeAttachments
2020-12-07 12:31:40 +01:00
composeState={composeState}
composeDispatch={composeDispatch}
2020-12-06 21:42:19 +01:00
/>
</View>
)}
2020-12-07 12:31:40 +01:00
{composeState.poll.active && (
2020-12-06 21:42:19 +01:00
<View style={styles.poll}>
<ComposePoll
2020-12-07 12:31:40 +01:00
composeState={composeState}
composeDispatch={composeDispatch}
2020-12-06 21:42:19 +01:00
/>
</View>
)}
</>
}
2020-12-04 01:17:10 +01:00
ListEmptyComponent={listEmpty}
2020-12-07 12:31:40 +01:00
data={composeState.tag && isSuccess ? data[composeState.tag.type] : []}
2020-12-04 01:17:10 +01:00
renderItem={({ item, index }) => (
<Pressable
key={index}
onPress={() => {
2020-12-06 23:51:13 +01:00
const focusedInput = textInputRef.current?.isFocused()
? 'text'
: 'spoiler'
2020-12-04 01:17:10 +01:00
updateText({
2020-12-06 23:51:13 +01:00
origin: focusedInput,
2020-12-07 12:31:40 +01:00
composeState: {
...composeState,
2020-12-06 23:51:13 +01:00
[focusedInput]: {
2020-12-07 12:31:40 +01:00
...composeState[focusedInput],
2020-12-06 23:51:13 +01:00
selection: {
2020-12-07 12:31:40 +01:00
start: composeState.tag!.offset,
2020-12-06 23:51:13 +01:00
end:
2020-12-07 12:31:40 +01:00
composeState.tag!.offset + composeState.tag!.text.length + 1
2020-12-06 23:51:13 +01:00
}
2020-12-04 01:17:10 +01:00
}
},
2020-12-07 12:31:40 +01:00
composeDispatch,
2020-12-04 01:17:10 +01:00
newText: item.acct ? `@${item.acct}` : `#${item.name}`,
type: 'suggestion'
})
}}
style={styles.suggestion}
>
{item.acct ? (
<View
style={[
styles.account,
{ borderBottomColor: theme.border },
index === 0 && {
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: theme.border
}
]}
>
<Image
source={{ uri: item.avatar }}
style={styles.accountAvatar}
/>
<View>
<Text style={[styles.accountName, { color: theme.primary }]}>
{item.emojis.length ? (
<Emojis
content={item.display_name || item.username}
emojis={item.emojis}
size={StyleConstants.Font.Size.S}
/>
) : (
item.display_name || item.username
)}
</Text>
<Text
style={[styles.accountAccount, { color: theme.primary }]}
>
@{item.acct}
</Text>
</View>
</View>
) : (
<View
style={[
styles.hashtag,
{ borderBottomColor: theme.border },
index === 0 && {
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: theme.border
}
]}
>
<Text style={[styles.hashtagText, { color: theme.primary }]}>
#{item.name}
</Text>
</View>
)}
</Pressable>
)}
/>
2020-12-03 22:03:06 +01:00
<ComposeActions
textInputRef={textInputRef}
2020-12-07 12:31:40 +01:00
composeState={composeState}
composeDispatch={composeDispatch}
2020-12-03 22:03:06 +01:00
/>
2020-11-15 23:33:01 +01:00
</View>
2020-11-15 20:29:43 +01:00
)
}
const styles = StyleSheet.create({
2020-11-20 01:41:46 +01:00
base: {
2020-11-15 20:29:43 +01:00
flex: 1
},
2020-12-03 22:03:06 +01:00
contentView: { flex: 1 },
2020-12-04 01:17:10 +01:00
attachments: {
flex: 1
},
2020-11-17 23:57:23 +01:00
poll: {
2020-12-03 22:03:06 +01:00
flex: 1,
padding: StyleConstants.Spacing.Global.PagePadding
2020-11-17 23:57:23 +01:00
},
2020-12-04 01:17:10 +01:00
suggestion: {
flex: 1
},
account: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
paddingTop: StyleConstants.Spacing.S,
paddingBottom: StyleConstants.Spacing.S,
2020-12-06 22:32:36 +01:00
marginLeft: StyleConstants.Spacing.Global.PagePadding,
marginRight: StyleConstants.Spacing.Global.PagePadding,
2020-12-04 01:17:10 +01:00
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: {
2020-11-15 20:29:43 +01:00
flex: 1,
2020-12-04 01:17:10 +01:00
paddingTop: StyleConstants.Spacing.S,
paddingBottom: StyleConstants.Spacing.S,
2020-12-06 22:32:36 +01:00
marginLeft: StyleConstants.Spacing.Global.PagePadding,
marginRight: StyleConstants.Spacing.Global.PagePadding,
2020-12-04 01:17:10 +01:00
borderBottomWidth: StyleSheet.hairlineWidth
},
hashtagText: {
fontSize: StyleConstants.Font.Size.S,
fontWeight: StyleConstants.Font.Weight.Bold,
marginBottom: StyleConstants.Spacing.XS
2020-11-15 20:29:43 +01:00
},
emojis: {
2020-12-03 22:03:06 +01:00
flex: 1
2020-11-15 20:29:43 +01:00
}
})
2020-12-03 22:03:06 +01:00
export default ComposeRoot