tooot/src/screens/Compose/Root/Footer/Attachments.tsx

320 lines
9.1 KiB
TypeScript
Raw Normal View History

2021-01-24 02:25:43 +01:00
import analytics from '@components/analytics'
2020-12-30 00:56:25 +01:00
import Button from '@components/Button'
2021-01-01 17:52:14 +01:00
import haptics from '@components/haptics'
import Icon from '@components/Icon'
2021-01-13 01:03:46 +01:00
import { useActionSheet } from '@expo/react-native-action-sheet'
2020-12-30 00:56:25 +01:00
import { useNavigation } from '@react-navigation/native'
import { StyleConstants } from '@utils/styles/constants'
2021-01-01 17:52:14 +01:00
import layoutAnimation from '@utils/styles/layoutAnimation'
2020-12-30 00:56:25 +01:00
import { useTheme } from '@utils/styles/ThemeManager'
2021-01-01 16:48:16 +01:00
import React, {
useCallback,
useContext,
useEffect,
useMemo,
useRef
} from 'react'
2021-01-19 01:13:45 +01:00
import { useTranslation } from 'react-i18next'
2021-03-19 21:44:52 +01:00
import {
FlatList,
Image,
Pressable,
StyleSheet,
Text,
View
} from 'react-native'
2021-02-08 23:47:20 +01:00
import { Circle } from 'react-native-animated-spinkit'
2021-01-19 01:13:45 +01:00
import ComposeContext from '../../utils/createContext'
import { ExtendedAttachment } from '../../utils/types'
2021-01-01 17:52:14 +01:00
import addAttachment from './addAttachment'
2020-12-06 12:52:29 +01:00
const DEFAULT_HEIGHT = 200
const ComposeAttachments: React.FC = () => {
2021-01-13 01:03:46 +01:00
const { showActionSheetWithOptions } = useActionSheet()
const { composeState, composeDispatch } = useContext(ComposeContext)
2021-01-19 01:13:45 +01:00
const { t } = useTranslation('sharedCompose')
2020-12-06 12:52:29 +01:00
const { theme } = useTheme()
const navigation = useNavigation()
2021-01-01 16:48:16 +01:00
const flatListRef = useRef<FlatList>(null)
2021-01-24 02:25:43 +01:00
const sensitiveOnPress = useCallback(() => {
analytics('compose_attachment_sensitive_press', {
current: composeState.attachments.sensitive
})
composeDispatch({
type: 'attachments/sensitive',
payload: { sensitive: !composeState.attachments.sensitive }
})
}, [composeState.attachments.sensitive])
2020-12-30 00:56:25 +01:00
2021-01-01 16:48:16 +01:00
const calculateWidth = useCallback(item => {
if (item.local) {
return (item.local.width / item.local.height) * DEFAULT_HEIGHT
} else {
if (item.remote) {
if (item.remote.meta.original.aspect) {
return item.remote.meta.original.aspect * DEFAULT_HEIGHT
} else if (
item.remote.meta.original.width &&
item.remote.meta.original.height
) {
return (
(item.remote.meta.original.width /
item.remote.meta.original.height) *
DEFAULT_HEIGHT
)
2020-12-30 00:56:25 +01:00
} else {
2021-01-01 16:48:16 +01:00
return DEFAULT_HEIGHT
2020-12-30 00:56:25 +01:00
}
2021-01-01 16:48:16 +01:00
} else {
return DEFAULT_HEIGHT
2020-12-30 00:56:25 +01:00
}
2021-01-01 16:48:16 +01:00
}
}, [])
const snapToOffsets = useMemo(() => {
const attachmentsOffsets = composeState.attachments.uploads.map(
(_, index) => {
let currentOffset = 0
Array.from(Array(index).keys()).map(
i =>
(currentOffset =
currentOffset +
calculateWidth(composeState.attachments.uploads[i]) +
StyleConstants.Spacing.Global.PagePadding)
)
return currentOffset
}
)
return attachmentsOffsets.length < 4
? [
...attachmentsOffsets,
attachmentsOffsets.reduce((a, b) => a + b, 0) +
DEFAULT_HEIGHT +
StyleConstants.Spacing.Global.PagePadding
]
: attachmentsOffsets
}, [composeState.attachments.uploads.length])
2021-02-11 01:33:31 +01:00
let prevOffsets = useRef<number[]>()
2021-01-01 16:48:16 +01:00
useEffect(() => {
if (
snapToOffsets.length >
2021-03-02 01:17:06 +01:00
(prevOffsets.current ? prevOffsets.current.length : 0)
2021-01-01 16:48:16 +01:00
) {
flatListRef.current?.scrollToOffset({
offset:
snapToOffsets[snapToOffsets.length - 2] +
snapToOffsets[snapToOffsets.length - 1]
})
}
prevOffsets.current = snapToOffsets
2021-02-11 01:33:31 +01:00
}, [snapToOffsets, prevOffsets.current])
2021-01-01 16:48:16 +01:00
const renderAttachment = useCallback(
({ item, index }: { item: ExtendedAttachment; index: number }) => {
2020-12-06 12:52:29 +01:00
return (
2020-12-30 00:56:25 +01:00
<View
key={index}
2021-01-01 16:48:16 +01:00
style={[styles.container, { width: calculateWidth(item) }]}
2020-12-30 00:56:25 +01:00
>
2021-03-19 21:44:52 +01:00
<Image
2020-12-30 00:56:25 +01:00
style={styles.image}
2020-12-06 12:52:29 +01:00
source={{
2020-12-30 00:56:25 +01:00
uri: item.local?.local_thumbnail || item.remote?.preview_url
2020-12-06 12:52:29 +01:00
}}
/>
2020-12-30 00:56:25 +01:00
{item.remote?.meta?.original?.duration && (
2020-12-06 21:42:19 +01:00
<Text
style={[
styles.duration,
{
color: theme.backgroundDefault,
backgroundColor: theme.backgroundOverlayInvert
2020-12-06 21:42:19 +01:00
}
]}
>
2020-12-30 00:56:25 +01:00
{item.remote.meta.original.duration}
2020-12-06 21:42:19 +01:00
</Text>
)}
2020-12-30 00:56:25 +01:00
{item.uploading ? (
<View
style={[
styles.uploading,
{ backgroundColor: theme.backgroundOverlayInvert }
2020-12-30 00:56:25 +01:00
]}
2020-12-06 21:42:19 +01:00
>
2021-02-08 23:47:20 +01:00
<Circle
size={StyleConstants.Font.Size.L}
2020-12-30 00:56:25 +01:00
color={theme.primaryOverlay}
/>
</View>
) : (
2021-01-04 14:55:34 +01:00
<View style={styles.actions}>
2020-12-26 23:05:17 +01:00
<Button
type='icon'
content='X'
2020-12-26 23:05:17 +01:00
spacing='M'
round
overlay
2020-12-30 00:56:25 +01:00
onPress={() => {
2021-01-24 02:25:43 +01:00
analytics('compose_attachment_delete')
2020-12-30 00:56:25 +01:00
layoutAnimation()
composeDispatch({
type: 'attachment/delete',
payload: item.remote!.id
})
2020-12-30 14:33:33 +01:00
haptics('Success')
2020-12-06 21:42:19 +01:00
}}
/>
2020-12-30 00:56:25 +01:00
<Button
type='icon'
content='Edit'
2020-12-30 00:56:25 +01:00
spacing='M'
round
overlay
2021-01-24 02:25:43 +01:00
onPress={() => {
analytics('compose_attachment_edit')
2021-01-30 01:29:15 +01:00
navigation.navigate('Screen-Compose-EditAttachment', {
2020-12-30 00:56:25 +01:00
index
})
2021-01-24 02:25:43 +01:00
}}
2020-12-30 00:56:25 +01:00
/>
2021-01-04 14:55:34 +01:00
</View>
2020-12-06 21:42:19 +01:00
)}
2020-12-30 00:56:25 +01:00
</View>
)
},
[]
)
2020-12-06 21:42:19 +01:00
2020-12-30 00:56:25 +01:00
const listFooter = useMemo(
() => (
2020-12-07 00:23:26 +01:00
<Pressable
2020-12-30 00:56:25 +01:00
style={[
styles.container,
{
width: DEFAULT_HEIGHT,
backgroundColor: theme.backgroundOverlayInvert
2020-12-30 00:56:25 +01:00
}
]}
2021-01-24 02:25:43 +01:00
onPress={async () => {
analytics('compose_attachment_add_container_press')
2021-01-13 01:03:46 +01:00
await addAttachment({ composeDispatch, showActionSheetWithOptions })
2021-01-24 02:25:43 +01:00
}}
2020-12-07 00:23:26 +01:00
>
2020-12-30 00:56:25 +01:00
<Button
type='icon'
content='UploadCloud'
2020-12-30 00:56:25 +01:00
spacing='M'
round
overlay
2021-01-24 02:25:43 +01:00
onPress={async () => {
analytics('compose_attachment_add_button_press')
2021-01-13 01:03:46 +01:00
await addAttachment({ composeDispatch, showActionSheetWithOptions })
2021-01-24 02:25:43 +01:00
}}
2020-12-30 00:56:25 +01:00
style={{
position: 'absolute',
top:
(DEFAULT_HEIGHT -
StyleConstants.Spacing.M * 2 -
StyleConstants.Font.Size.M) /
2,
left:
(DEFAULT_HEIGHT -
StyleConstants.Spacing.M * 2 -
StyleConstants.Font.Size.M) /
2
}}
/>
</Pressable>
),
[]
)
return (
<View style={styles.base}>
<Pressable style={styles.sensitive} onPress={sensitiveOnPress}>
<Icon
name={composeState.attachments.sensitive ? 'CheckCircle' : 'Circle'}
2020-12-07 00:23:26 +01:00
size={StyleConstants.Font.Size.L}
color={theme.primaryDefault}
2020-12-07 00:23:26 +01:00
/>
<Text style={[styles.sensitiveText, { color: theme.primaryDefault }]}>
2021-01-19 01:13:45 +01:00
{t('content.root.footer.attachments.sensitive')}
2020-12-07 00:23:26 +01:00
</Text>
</Pressable>
2020-12-30 00:56:25 +01:00
<FlatList
horizontal
2021-01-01 16:48:16 +01:00
ref={flatListRef}
decelerationRate={0}
pagingEnabled={false}
snapToAlignment='center'
2020-12-30 00:56:25 +01:00
renderItem={renderAttachment}
2021-01-01 16:48:16 +01:00
snapToOffsets={snapToOffsets}
2021-03-18 23:32:31 +01:00
keyboardShouldPersistTaps='always'
2021-01-01 16:48:16 +01:00
showsHorizontalScrollIndicator={false}
data={composeState.attachments.uploads}
2021-02-07 00:39:11 +01:00
keyExtractor={item =>
2021-02-11 01:33:31 +01:00
item.local?.uri || item.remote?.url || Math.random().toString()
2021-02-07 00:39:11 +01:00
}
2020-12-30 00:56:25 +01:00
ListFooterComponent={
composeState.attachments.uploads.length < 4 ? listFooter : null
}
/>
</View>
)
}
const styles = StyleSheet.create({
base: {
flex: 1,
marginRight: StyleConstants.Spacing.Global.PagePadding,
2020-12-07 00:23:26 +01:00
marginTop: StyleConstants.Spacing.M
},
sensitive: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
marginLeft: StyleConstants.Spacing.Global.PagePadding
},
sensitiveText: {
...StyleConstants.FontStyle.M,
2020-12-07 00:23:26 +01:00
marginLeft: StyleConstants.Spacing.S
},
2020-12-30 00:56:25 +01:00
container: {
height: DEFAULT_HEIGHT,
marginLeft: StyleConstants.Spacing.Global.PagePadding,
marginTop: StyleConstants.Spacing.Global.PagePadding,
marginBottom: StyleConstants.Spacing.Global.PagePadding
},
2020-12-30 00:56:25 +01:00
image: {
width: '100%',
height: '100%'
},
2020-12-06 21:42:19 +01:00
duration: {
position: 'absolute',
2020-12-30 00:56:25 +01:00
bottom: StyleConstants.Spacing.S,
left: StyleConstants.Spacing.S,
...StyleConstants.FontStyle.S,
2020-12-06 21:42:19 +01:00
paddingLeft: StyleConstants.Spacing.S,
paddingRight: StyleConstants.Spacing.S,
paddingTop: StyleConstants.Spacing.XS,
paddingBottom: StyleConstants.Spacing.XS
},
2020-12-30 00:56:25 +01:00
uploading: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center'
},
2021-01-04 14:55:34 +01:00
actions: {
...StyleSheet.absoluteFillObject,
justifyContent: 'space-between',
alignContent: 'flex-end',
alignItems: 'flex-end',
padding: StyleConstants.Spacing.S
}
})
2020-12-30 00:56:25 +01:00
export default React.memo(ComposeAttachments, () => true)