From 55500e2f66d5f30f1aedf63d4355a6b7568d4552 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Fri, 30 Dec 2022 11:55:25 -0600 Subject: [PATCH] Improve image layout --- src/view/com/util/PostEmbeds.tsx | 87 +++-------- src/view/com/util/images/AutoSizedImage.tsx | 36 +++-- src/view/com/util/images/ImageLayoutGrid.tsx | 148 +++++++++++++++++++ 3 files changed, 192 insertions(+), 79 deletions(-) create mode 100644 src/view/com/util/images/ImageLayoutGrid.tsx diff --git a/src/view/com/util/PostEmbeds.tsx b/src/view/com/util/PostEmbeds.tsx index 870df50a..1d5c690f 100644 --- a/src/view/com/util/PostEmbeds.tsx +++ b/src/view/com/util/PostEmbeds.tsx @@ -3,11 +3,10 @@ import {ImageStyle, StyleSheet, StyleProp, View, ViewStyle} from 'react-native' import {AppBskyEmbedImages, AppBskyEmbedExternal} from '@atproto/api' import {Link} from '../util/Link' import {Text} from './text/Text' -import {colors} from '../../lib/styles' import {AutoSizedImage} from './images/AutoSizedImage' +import {ImageLayoutGrid} from './images/ImageLayoutGrid' import {ImagesLightbox} from '../../../state/models/shell-ui' import {useStores} from '../../../state' -import {useTheme} from '../../lib/ThemeContext' import {usePalette} from '../../lib/hooks/usePalette' type Embed = @@ -22,7 +21,6 @@ export function PostEmbeds({ embed?: Embed style?: StyleProp }) { - const theme = useTheme() const pal = usePalette('default') const store = useStores() if (embed?.$type === 'app.bsky.embed.images#presented') { @@ -32,59 +30,44 @@ export function PostEmbeds({ const openLightbox = (index: number) => { store.shell.openLightbox(new ImagesLightbox(uris, index)) } - const Thumb = ({i, style}: {i: number; style: StyleProp}) => ( - openLightbox(i)} - /> - ) if (imgEmbed.images.length === 4) { return ( - - - - - - - - - - - + img.thumb)} + onPress={openLightbox} + /> ) } else if (imgEmbed.images.length === 3) { return ( - - - - - - - - - + img.thumb)} + onPress={openLightbox} + /> ) } else if (imgEmbed.images.length === 2) { return ( - - - - - + img.thumb)} + onPress={openLightbox} + /> ) } else { return ( - - - + openLightbox(0)} + containerStyle={{borderRadius: 4}} + /> ) } @@ -99,7 +82,7 @@ export function PostEmbeds({ href={link.uri} noFeedback> {link.thumb ? ( - + ) : undefined} {link.title || link.uri} @@ -123,34 +106,10 @@ export function PostEmbeds({ const styles = StyleSheet.create({ imagesContainer: { + marginTop: 4, marginBottom: 6, }, - imagesWidthSpacer: { - width: 5, - }, - imagesHeightSpacer: { - height: 5, - }, - imagePair: { - flexDirection: 'row', - }, - imagePairItem: { - resizeMode: 'contain', - flex: 1, - borderRadius: 4, - }, - imageWide: {}, - imageWideItem: { - resizeMode: 'contain', - borderRadius: 4, - }, - imageBig: {}, - imageBigItem: { - borderRadius: 4, - }, - extOuter: { - borderRadius: 8, padding: 10, }, extDescription: { diff --git a/src/view/com/util/images/AutoSizedImage.tsx b/src/view/com/util/images/AutoSizedImage.tsx index 9de443b7..a711323a 100644 --- a/src/view/com/util/images/AutoSizedImage.tsx +++ b/src/view/com/util/images/AutoSizedImage.tsx @@ -10,7 +10,8 @@ import { ViewStyle, } from 'react-native' import {Text} from '../text/Text' -import {colors} from '../../../lib/styles' +import {useTheme} from '../../../lib/ThemeContext' +import {usePalette} from '../../../lib/hooks/usePalette' const MAX_HEIGHT = 300 @@ -23,12 +24,16 @@ export function AutoSizedImage({ uri, onPress, style, + containerStyle, }: { uri: string onPress?: () => void - style: StyleProp + style?: StyleProp + containerStyle?: StyleProp }) { - const [error, setError] = useState() + const theme = useTheme() + const errPal = usePalette('error') + const [error, setError] = useState('') const [imgInfo, setImgInfo] = useState() const [containerInfo, setContainerInfo] = useState() @@ -77,15 +82,22 @@ export function AutoSizedImage({ {error ? ( - - {error} + + {error} ) : calculatedStyle ? ( - + ) : ( - + )} @@ -96,18 +108,12 @@ const styles = StyleSheet.create({ placeholder: { width: '100%', aspectRatio: 1, - backgroundColor: colors.gray1, }, errorContainer: { - backgroundColor: colors.red1, - paddingHorizontal: 8, - paddingVertical: 4, + paddingHorizontal: 12, + paddingVertical: 8, }, container: { - borderRadius: 8, overflow: 'hidden', }, - error: { - color: colors.red5, - }, }) diff --git a/src/view/com/util/images/ImageLayoutGrid.tsx b/src/view/com/util/images/ImageLayoutGrid.tsx new file mode 100644 index 00000000..cb560dd3 --- /dev/null +++ b/src/view/com/util/images/ImageLayoutGrid.tsx @@ -0,0 +1,148 @@ +import React from 'react' +import { + Image, + ImageStyle, + LayoutChangeEvent, + StyleProp, + StyleSheet, + TouchableWithoutFeedback, + View, + ViewStyle, +} from 'react-native' + +interface Dim { + width: number + height: number +} + +export type ImageLayoutGridType = 'two' | 'three' | 'four' + +export function ImageLayoutGrid({ + type, + uris, + onPress, + style, +}: { + type: ImageLayoutGridType + uris: string + onPress?: (index: number) => void + style?: StyleProp +}) { + const [containerInfo, setContainerInfo] = React.useState() + + const onLayout = (evt: LayoutChangeEvent) => { + setContainerInfo({ + width: evt.nativeEvent.layout.width, + height: evt.nativeEvent.layout.height, + }) + } + + return ( + + {containerInfo ? ( + + ) : undefined} + + ) +} + +function ImageLayoutGridInner({ + type, + uris, + onPress, + containerInfo, +}: { + type: ImageLayoutGridType + uris: string + onPress?: (index: number) => void + containerInfo: Dim +}) { + const size1 = React.useMemo(() => { + if (type === 'three') { + const size = (containerInfo.width - 10) / 3 + return {width: size, height: size, resizeMode: 'cover', borderRadius: 4} + } else { + const size = (containerInfo.width - 5) / 2 + return {width: size, height: size, resizeMode: 'cover', borderRadius: 4} + } + }, [type, containerInfo]) + const size2 = React.useMemo(() => { + if (type === 'three') { + const size = ((containerInfo.width - 10) / 3) * 2 + 5 + return {width: size, height: size, resizeMode: 'cover', borderRadius: 4} + } else { + const size = (containerInfo.width - 5) / 2 + return {width: size, height: size, resizeMode: 'cover', borderRadius: 4} + } + }, [type, containerInfo]) + + if (type === 'two') { + return ( + + onPress?.(0)}> + + + + onPress?.(1)}> + + + + ) + } + if (type === 'three') { + return ( + + onPress?.(0)}> + + + + + onPress?.(1)}> + + + + onPress?.(2)}> + + + + + ) + } + if (type === 'four') { + return ( + + + onPress?.(0)}> + + + + onPress?.(1)}> + + + + + + onPress?.(2)}> + + + + onPress?.(3)}> + + + + + ) + } + return +} + +const styles = StyleSheet.create({ + flexRow: {flexDirection: 'row'}, + wSpace: {width: 5}, + hSpace: {height: 5}, +})