diff options
author | João Ferreiro <ferreiro@pinkroom.dev> | 2022-11-29 10:59:32 +0000 |
---|---|---|
committer | João Ferreiro <ferreiro@pinkroom.dev> | 2022-11-29 10:59:32 +0000 |
commit | acfdea2518ad047896079b2ab7b716fa9544baee (patch) | |
tree | 25c42a781ed7a0917e0e3efdb58b935b0a7f55ca /src | |
parent | 77ea3bfd0a28f76651ed425cf733abcf01ecaa24 (diff) | |
download | voidsky-acfdea2518ad047896079b2ab7b716fa9544baee.tar.zst |
reafctor image carousel into new component;
fix photo overlapping text in composer
Diffstat (limited to 'src')
-rw-r--r-- | src/view/com/composer/ComposePost.tsx | 197 | ||||
-rw-r--r-- | src/view/com/composer/PhotoCarouselPicker.tsx | 192 |
2 files changed, 220 insertions, 169 deletions
diff --git a/src/view/com/composer/ComposePost.tsx b/src/view/com/composer/ComposePost.tsx index ce42ee17e..2f8c29e7c 100644 --- a/src/view/com/composer/ComposePost.tsx +++ b/src/view/com/composer/ComposePost.tsx @@ -9,13 +9,10 @@ import { TextInput, TouchableOpacity, View, - ScrollView, - Image, } from 'react-native' import LinearGradient from 'react-native-linear-gradient' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {UserAutocompleteViewModel} from '../../../state/models/user-autocomplete-view' -import {UserLocalPhotosModel} from '../../../state/models/user-local-photos' import {Autocomplete} from './Autocomplete' import * as Toast from '../util/Toast' import ProgressCircle from '../util/ProgressCircle' @@ -26,7 +23,8 @@ import * as apilib from '../../../state/lib/api' import {ComposerOpts} from '../../../state/models/shell-ui' import {s, colors, gradients} from '../../lib/styles' import {detectLinkables} from '../../../lib/strings' -import {openPicker, openCamera} from 'react-native-image-crop-picker' +import {PhotoCarouselPicker} from './PhotoCarouselPicker' +import {UserLocalPhotosModel} from '../../../state/models/user-local-photos' const MAX_TEXT_LENGTH = 256 const DANGER_TEXT_LENGTH = MAX_TEXT_LENGTH @@ -45,7 +43,8 @@ export const ComposePost = observer(function ComposePost({ const [isProcessing, setIsProcessing] = useState(false) const [error, setError] = useState('') const [text, setText] = useState('') - const [photoUris, setPhotoUris] = useState<string[]>([]) + const [selectedPhotos, setSelectedPhotos] = useState<string[]>([]) + const autocompleteView = useMemo<UserAutocompleteViewModel>( () => new UserAutocompleteViewModel(store), [], @@ -58,6 +57,11 @@ export const ComposePost = observer(function ComposePost({ useEffect(() => { autocompleteView.setup() }) + + useEffect(() => { + localPhotos.setup() + }, []) + useEffect(() => { // HACK // wait a moment before focusing the input to resolve some layout bugs with the keyboard-avoiding-view @@ -75,10 +79,6 @@ export const ComposePost = observer(function ComposePost({ } }, [textInput.current]) - useEffect(() => { - localPhotos.setup() - }, []) - const onChangeText = (newText: string) => { setText(newText) @@ -207,7 +207,13 @@ export const ComposePost = observer(function ComposePost({ </View> </View> ) : undefined} - <View style={styles.textInputLayout}> + <View + style={[ + styles.textInputLayout, + selectedPhotos.length !== 0 + ? styles.textInputLayoutWithPhoto + : styles.textInputLayoutWithoutPhoto, + ]}> <UserAvatar handle={store.me.handle || ''} displayName={store.me.displayName} @@ -223,100 +229,12 @@ export const ComposePost = observer(function ComposePost({ {textDecorated} </TextInput> </View> - {photoUris.length !== 0 && ( - <View style={styles.selectedImageContainer}> - {photoUris.length !== 0 && - photoUris.map((item, index) => ( - <View - key={`selected-image-${index}`} - style={[ - styles.selectedImage, - photoUris.length === 1 - ? styles.selectedImage250 - : photoUris.length === 2 - ? styles.selectedImage175 - : styles.selectedImage85, - ]}> - <TouchableOpacity - onPress={() => { - setPhotoUris( - photoUris.filter(filterItem => filterItem !== item), - ) - }} - style={styles.removePhotoButton}> - <FontAwesomeIcon - icon="xmark" - size={16} - style={{color: colors.white}} - /> - </TouchableOpacity> - - <Image - style={[ - styles.selectedImage, - photoUris.length === 1 - ? styles.selectedImage250 - : photoUris.length === 2 - ? styles.selectedImage175 - : styles.selectedImage85, - ]} - source={{uri: item}} - /> - </View> - ))} - </View> - )} - {localPhotos.photos != null && text === '' && photoUris.length === 0 && ( - <ScrollView - horizontal - style={styles.photosContainer} - showsHorizontalScrollIndicator={false}> - <TouchableOpacity - style={[styles.galleryButton, styles.photo]} - onPress={() => { - openCamera({multiple: true, maxFiles: 4}).then() - }}> - <FontAwesomeIcon - icon="camera" - size={24} - style={{color: colors.blue3}} - /> - </TouchableOpacity> - {localPhotos.photos.map((item, index) => ( - <TouchableOpacity - key={`local-image-${index}`} - style={styles.photoButton} - onPress={() => { - setPhotoUris([item.node.image.uri, ...photoUris]) - }}> - <Image - style={styles.photo} - source={{uri: item.node.image.uri}} - /> - </TouchableOpacity> - ))} - <TouchableOpacity - style={[styles.galleryButton, styles.photo]} - onPress={() => { - openPicker({multiple: true, maxFiles: 4}).then(items => { - setPhotoUris([ - ...items.reduce( - (accum, cur) => accum.concat(cur.sourceURL!), - [] as string[], - ), - ...photoUris, - ]) - }) - }}> - <FontAwesomeIcon - icon="image" - style={{color: colors.blue3}} - size={24} - /> - </TouchableOpacity> - </ScrollView> - )} - <View style={styles.separator} /> + <PhotoCarouselPicker + selectedPhotos={selectedPhotos} + setSelectedPhotos={setSelectedPhotos} + localPhotos={localPhotos} + inputText={text} + /> <View style={[s.flexRow, s.pt10, s.pb10, s.pr5, styles.contentCenter]}> <View style={s.flex1} /> <Text style={[s.mr10, {color: progressColor}]}> @@ -390,9 +308,14 @@ const styles = StyleSheet.create({ justifyContent: 'center', marginRight: 5, }, + textInputLayoutWithPhoto: { + flexWrap: 'wrap', + }, + textInputLayoutWithoutPhoto: { + flex: 1, + }, textInputLayout: { flexDirection: 'row', - flex: 1, borderTopWidth: 1, borderTopColor: colors.gray2, paddingTop: 16, @@ -416,68 +339,4 @@ const styles = StyleSheet.create({ paddingRight: 8, }, contentCenter: {alignItems: 'center'}, - selectedImageContainer: { - flex: 10, - flexDirection: 'row', - }, - selectedImage: { - borderRadius: 8, - margin: 2, - }, - selectedImage250: { - width: 250, - height: 250, - }, - selectedImage175: { - width: 175, - height: 175, - }, - selectedImage85: { - width: 85, - height: 85, - }, - photosContainer: { - width: '100%', - maxHeight: 96, - padding: 8, - overflow: 'hidden', - }, - removePhotoButton: { - position: 'absolute', - top: 8, - right: 8, - width: 24, - height: 24, - borderRadius: 12, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: colors.black, - zIndex: 1, - }, - galleryButton: { - borderWidth: 1, - borderColor: colors.gray3, - alignItems: 'center', - justifyContent: 'center', - }, - photoButton: { - width: 75, - height: 75, - marginRight: 8, - borderWidth: 1, - borderRadius: 16, - borderColor: colors.gray3, - }, - photo: { - width: 75, - height: 75, - marginRight: 8, - borderRadius: 16, - }, - separator: { - borderBottomColor: 'black', - borderBottomWidth: StyleSheet.hairlineWidth, - width: '110%', - marginLeft: -16, - }, }) diff --git a/src/view/com/composer/PhotoCarouselPicker.tsx b/src/view/com/composer/PhotoCarouselPicker.tsx new file mode 100644 index 000000000..f3c3b89c0 --- /dev/null +++ b/src/view/com/composer/PhotoCarouselPicker.tsx @@ -0,0 +1,192 @@ +import React from 'react' +import { + Image, + StyleSheet, + TouchableOpacity, + View, + ScrollView, +} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {colors} from '../../lib/styles' +import {openPicker, openCamera} from 'react-native-image-crop-picker' +import {observer} from 'mobx-react-lite' + +export const PhotoCarouselPicker = observer(function PhotoCarouselPicker({ + selectedPhotos, + setSelectedPhotos, + inputText, + localPhotos, +}: { + selectedPhotos: string[] + setSelectedPhotos: React.Dispatch<React.SetStateAction<string[]>> + inputText: string + localPhotos: any +}) { + return ( + <> + {selectedPhotos.length !== 0 && ( + <View style={styles.selectedImageContainer}> + {selectedPhotos.length !== 0 && + selectedPhotos.map((item, index) => ( + <View + key={`selected-image-${index}`} + style={[ + styles.selectedImage, + selectedPhotos.length === 1 + ? styles.selectedImage250 + : selectedPhotos.length === 2 + ? styles.selectedImage175 + : styles.selectedImage85, + ]}> + <TouchableOpacity + onPress={() => { + setSelectedPhotos( + selectedPhotos.filter(filterItem => filterItem !== item), + ) + }} + style={styles.removePhotoButton}> + <FontAwesomeIcon + icon="xmark" + size={16} + style={{color: colors.white}} + /> + </TouchableOpacity> + + <Image + style={[ + styles.selectedImage, + selectedPhotos.length === 1 + ? styles.selectedImage250 + : selectedPhotos.length === 2 + ? styles.selectedImage175 + : styles.selectedImage85, + ]} + source={{uri: item}} + /> + </View> + ))} + </View> + )} + {localPhotos.photos != null && + inputText === '' && + selectedPhotos.length === 0 && ( + <ScrollView + horizontal + style={styles.photosContainer} + showsHorizontalScrollIndicator={false}> + <TouchableOpacity + style={[styles.galleryButton, styles.photo]} + onPress={() => { + openCamera({multiple: true, maxFiles: 4}).then() + }}> + <FontAwesomeIcon + icon="camera" + size={24} + style={{color: colors.blue3}} + /> + </TouchableOpacity> + {localPhotos.photos.map((item: any, index: number) => ( + <TouchableOpacity + key={`local-image-${index}`} + style={styles.photoButton} + onPress={() => { + setSelectedPhotos([item.node.image.uri, ...selectedPhotos]) + }}> + <Image + style={styles.photo} + source={{uri: item.node.image.uri}} + /> + </TouchableOpacity> + ))} + <TouchableOpacity + style={[styles.galleryButton, styles.photo]} + onPress={() => { + openPicker({multiple: true, maxFiles: 4}).then(items => { + setSelectedPhotos([ + ...items.reduce( + (accum, cur) => accum.concat(cur.sourceURL!), + [] as string[], + ), + ...selectedPhotos, + ]) + }) + }}> + <FontAwesomeIcon + icon="image" + style={{color: colors.blue3}} + size={24} + /> + </TouchableOpacity> + </ScrollView> + )} + <View style={styles.separator} /> + </> + ) +}) + +const styles = StyleSheet.create({ + selectedImageContainer: { + flex: 1, + flexDirection: 'row', + marginTop: 16, + }, + selectedImage: { + borderRadius: 8, + margin: 2, + }, + selectedImage250: { + width: 250, + height: 250, + }, + selectedImage175: { + width: 175, + height: 175, + }, + selectedImage85: { + width: 85, + height: 85, + }, + photosContainer: { + width: '100%', + maxHeight: 96, + padding: 8, + overflow: 'hidden', + }, + removePhotoButton: { + position: 'absolute', + top: 8, + right: 8, + width: 24, + height: 24, + borderRadius: 12, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: colors.black, + zIndex: 1, + }, + galleryButton: { + borderWidth: 1, + borderColor: colors.gray3, + alignItems: 'center', + justifyContent: 'center', + }, + photoButton: { + width: 75, + height: 75, + marginRight: 8, + borderWidth: 1, + borderRadius: 16, + borderColor: colors.gray3, + }, + photo: { + width: 75, + height: 75, + marginRight: 8, + borderRadius: 16, + }, + separator: { + borderBottomColor: 'black', + borderBottomWidth: StyleSheet.hairlineWidth, + width: '100%', + }, +}) |