reafctor image carousel into new component;
fix photo overlapping text in composer
This commit is contained in:
parent
77ea3bfd0a
commit
acfdea2518
3 changed files with 285 additions and 234 deletions
|
@ -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,
|
||||
},
|
||||
})
|
||||
|
|
192
src/view/com/composer/PhotoCarouselPicker.tsx
Normal file
192
src/view/com/composer/PhotoCarouselPicker.tsx
Normal file
|
@ -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%',
|
||||
},
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue