Add post embeds (images and external links)

This commit is contained in:
Paul Frazee 2022-12-14 15:35:15 -06:00
parent 345ec83f26
commit 4966b2152e
30 changed files with 936 additions and 242 deletions

View file

@ -29,7 +29,6 @@ import {detectLinkables} from '../../../lib/strings'
import {UserLocalPhotosModel} from '../../../state/models/user-local-photos'
import {PhotoCarouselPicker} from './PhotoCarouselPicker'
import {SelectedPhoto} from './SelectedPhoto'
import {IMAGES_ENABLED} from '../../../build-flags'
const MAX_TEXT_LENGTH = 256
const DANGER_TEXT_LENGTH = MAX_TEXT_LENGTH
@ -46,6 +45,7 @@ export const ComposePost = observer(function ComposePost({
const store = useStores()
const textInput = useRef<TextInput>(null)
const [isProcessing, setIsProcessing] = useState(false)
const [processingState, setProcessingState] = useState('')
const [error, setError] = useState('')
const [text, setText] = useState('')
const [selectedPhotos, setSelectedPhotos] = useState<string[]>([])
@ -81,6 +81,10 @@ export const ComposePost = observer(function ComposePost({
}
}, [])
const onSelectPhotos = (photos: string[]) => {
setSelectedPhotos(photos)
}
const onChangeText = (newText: string) => {
setText(newText)
@ -109,15 +113,16 @@ export const ComposePost = observer(function ComposePost({
}
setIsProcessing(true)
try {
const replyRef = replyTo
? {uri: replyTo.uri, cid: replyTo.cid}
: undefined
await apilib.post(store, text, replyRef, autocompleteView.knownHandles)
} catch (e: any) {
console.error(`Failed to create post: ${e.toString()}`)
setError(
'Post failed to upload. Please check your Internet connection and try again.',
await apilib.post(
store,
text,
replyTo?.uri,
selectedPhotos,
autocompleteView.knownHandles,
setProcessingState,
)
} catch (e: any) {
setError(e.message)
setIsProcessing(false)
return
}
@ -189,6 +194,11 @@ export const ComposePost = observer(function ComposePost({
</View>
)}
</View>
{isProcessing ? (
<View style={styles.processingLine}>
<Text>{processingState}</Text>
</View>
) : undefined}
{error !== '' && (
<View style={styles.errorLine}>
<View style={styles.errorIcon}>
@ -198,7 +208,7 @@ export const ComposePost = observer(function ComposePost({
size={10}
/>
</View>
<Text style={s.red4}>{error}</Text>
<Text style={[s.red4, s.flex1]}>{error}</Text>
</View>
)}
{replyTo ? (
@ -240,18 +250,15 @@ export const ComposePost = observer(function ComposePost({
</View>
<SelectedPhoto
selectedPhotos={selectedPhotos}
setSelectedPhotos={setSelectedPhotos}
onSelectPhotos={onSelectPhotos}
/>
{IMAGES_ENABLED &&
localPhotos.photos != null &&
text === '' &&
selectedPhotos.length === 0 && (
<PhotoCarouselPicker
selectedPhotos={selectedPhotos}
setSelectedPhotos={setSelectedPhotos}
localPhotos={localPhotos}
/>
)}
{localPhotos.photos != null && selectedPhotos.length < 4 && (
<PhotoCarouselPicker
selectedPhotos={selectedPhotos}
onSelectPhotos={onSelectPhotos}
localPhotos={localPhotos}
/>
)}
<View style={styles.bottomBar}>
<View style={s.flex1} />
<Text style={[s.mr10, {color: progressColor}]}>
@ -322,6 +329,13 @@ const styles = StyleSheet.create({
paddingHorizontal: 20,
paddingVertical: 6,
},
processingLine: {
backgroundColor: colors.gray1,
borderRadius: 6,
paddingHorizontal: 8,
paddingVertical: 6,
marginBottom: 6,
},
errorLine: {
flexDirection: 'row',
backgroundColor: colors.red1,

View file

@ -8,48 +8,54 @@ import {
openCropper,
} from 'react-native-image-crop-picker'
const IMAGE_PARAMS = {
width: 500,
height: 500,
freeStyleCropEnabled: true,
forceJpg: true, // ios only
compressImageQuality: 0.7,
}
export const PhotoCarouselPicker = ({
selectedPhotos,
setSelectedPhotos,
onSelectPhotos,
localPhotos,
}: {
selectedPhotos: string[]
setSelectedPhotos: React.Dispatch<React.SetStateAction<string[]>>
onSelectPhotos: (v: string[]) => void
localPhotos: any
}) => {
const handleOpenCamera = useCallback(() => {
openCamera({
mediaType: 'photo',
cropping: true,
width: 1000,
height: 1000,
...IMAGE_PARAMS,
}).then(
item => {
setSelectedPhotos([item.path, ...selectedPhotos])
onSelectPhotos([item.path, ...selectedPhotos])
},
_err => {
// ignore
},
)
}, [selectedPhotos, setSelectedPhotos])
}, [selectedPhotos, onSelectPhotos])
const handleSelectPhoto = useCallback(
async (uri: string) => {
const img = await openCropper({
mediaType: 'photo',
path: uri,
width: 1000,
height: 1000,
...IMAGE_PARAMS,
})
setSelectedPhotos([img.path, ...selectedPhotos])
onSelectPhotos([img.path, ...selectedPhotos])
},
[selectedPhotos, setSelectedPhotos],
[selectedPhotos, onSelectPhotos],
)
const handleOpenGallery = useCallback(() => {
openPicker({
multiple: true,
maxFiles: 4,
maxFiles: 4 - selectedPhotos.length,
mediaType: 'photo',
}).then(async items => {
const result = []
@ -58,14 +64,13 @@ export const PhotoCarouselPicker = ({
const img = await openCropper({
mediaType: 'photo',
path: image.path,
width: 1000,
height: 1000,
...IMAGE_PARAMS,
})
result.push(img.path)
}
setSelectedPhotos([...result, ...selectedPhotos])
onSelectPhotos([...result, ...selectedPhotos])
})
}, [selectedPhotos, setSelectedPhotos])
}, [selectedPhotos, onSelectPhotos])
return (
<ScrollView

View file

@ -5,10 +5,10 @@ import {colors} from '../../lib/styles'
export const SelectedPhoto = ({
selectedPhotos,
setSelectedPhotos,
onSelectPhotos,
}: {
selectedPhotos: string[]
setSelectedPhotos: React.Dispatch<React.SetStateAction<string[]>>
onSelectPhotos: (v: string[]) => void
}) => {
const imageStyle =
selectedPhotos.length === 1
@ -19,11 +19,9 @@ export const SelectedPhoto = ({
const handleRemovePhoto = useCallback(
item => {
setSelectedPhotos(
selectedPhotos.filter(filterItem => filterItem !== item),
)
onSelectPhotos(selectedPhotos.filter(filterItem => filterItem !== item))
},
[selectedPhotos, setSelectedPhotos],
[selectedPhotos, onSelectPhotos],
)
return selectedPhotos.length !== 0 ? (
@ -57,8 +55,10 @@ const styles = StyleSheet.create({
marginTop: 16,
},
image: {
resizeMode: 'contain',
borderRadius: 8,
margin: 2,
backgroundColor: colors.gray1,
},
image250: {
width: 250,