[APP-539] Rework lightbox and alt-image behaviors (#573)

* Replace the long press on the lightbox with footer controls

* Remove long-press from images in the feed

* Tune the lightbox footer control ui

* Replace the AltImageRead modal with the ability to view all alt text in the lightbox footer

* Tune lightbox footer for iOS

* Add alt text to the web lightbox

* Fix lint

* a11y slight changes

---------

Co-authored-by: renahlee <renahlee@outlook.com>
This commit is contained in:
Paul Frazee 2023-05-04 00:54:35 -05:00 committed by GitHub
parent 4ef853ef6c
commit d97e75c62f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 127 additions and 179 deletions

View file

@ -1,31 +1,75 @@
import React from 'react'
import {Pressable, StyleSheet, View} from 'react-native'
import {observer} from 'mobx-react-lite'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import ImageView from './ImageViewing'
import {useStores} from 'state/index'
import * as models from 'state/models/ui/shell'
import {saveImageModal} from 'lib/media/manip'
import {ImageSource} from './ImageViewing/@types'
import {Text} from '../util/text/Text'
import {s, colors} from 'lib/styles'
import {Button} from '../util/forms/Button'
import {isIOS} from 'platform/detection'
export const Lightbox = observer(function Lightbox() {
const store = useStores()
if (!store.shell.isLightboxActive) {
return null
}
const [isAltExpanded, setAltExpanded] = React.useState(false)
const onClose = () => {
const onClose = React.useCallback(() => {
store.shell.closeLightbox()
}
const onLongPress = (image: ImageSource) => {
if (
typeof image === 'object' &&
'uri' in image &&
typeof image.uri === 'string'
) {
saveImageModal({uri: image.uri})
}
}
}, [store])
if (store.shell.activeLightbox?.name === 'profile-image') {
const LightboxFooter = React.useCallback(
({imageIndex}: {imageIndex: number}) => {
const lightbox = store.shell.activeLightbox
if (!lightbox) {
return null
}
let altText = ''
let uri
if (lightbox.name === 'images') {
const opts = store.shell.activeLightbox as models.ImagesLightbox
uri = opts.images[imageIndex].uri
altText = opts.images[imageIndex].alt
} else if (store.shell.activeLightbox.name === 'profile-image') {
const opts = store.shell.activeLightbox as models.ProfileImageLightbox
uri = opts.profileView.avatar
}
return (
<View style={[styles.footer]}>
{altText ? (
<Pressable
onPress={() => setAltExpanded(!isAltExpanded)}
accessibilityRole="button">
<Text
style={[s.gray3, styles.footerText]}
numberOfLines={isAltExpanded ? undefined : 3}>
{altText}
</Text>
</Pressable>
) : null}
<View style={styles.footerBtns}>
<Button
type="primary-outline"
style={styles.footerBtn}
onPress={() => saveImageModal({uri})}>
<FontAwesomeIcon icon="arrow-up-from-bracket" style={s.white} />
<Text type="xl" style={s.white}>
Share
</Text>
</Button>
</View>
</View>
)
},
[store.shell.activeLightbox, isAltExpanded, setAltExpanded],
)
if (!store.shell.activeLightbox) {
return null
} else if (store.shell.activeLightbox.name === 'profile-image') {
const opts = store.shell.activeLightbox as models.ProfileImageLightbox
return (
<ImageView
@ -33,20 +77,44 @@ export const Lightbox = observer(function Lightbox() {
imageIndex={0}
visible
onRequestClose={onClose}
FooterComponent={LightboxFooter}
/>
)
} else if (store.shell.activeLightbox?.name === 'images') {
} else if (store.shell.activeLightbox.name === 'images') {
const opts = store.shell.activeLightbox as models.ImagesLightbox
return (
<ImageView
images={opts.uris.map(uri => ({uri}))}
images={opts.images.map(({uri}) => ({uri}))}
imageIndex={opts.index}
visible
onRequestClose={onClose}
onLongPress={onLongPress}
FooterComponent={LightboxFooter}
/>
)
} else {
return null
}
})
const styles = StyleSheet.create({
footer: {
paddingTop: 16,
paddingBottom: isIOS ? 40 : 24,
paddingHorizontal: 24,
backgroundColor: '#000d',
},
footerText: {
paddingBottom: isIOS ? 20 : 16,
},
footerBtns: {
flexDirection: 'row',
justifyContent: 'center',
},
footerBtn: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
backgroundColor: 'transparent',
borderColor: colors.white,
},
})

View file

@ -10,11 +10,13 @@ import {observer} from 'mobx-react-lite'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {useStores} from 'state/index'
import * as models from 'state/models/ui/shell'
import {colors} from 'lib/styles'
import {colors, s} from 'lib/styles'
import ImageDefaultHeader from './ImageViewing/components/ImageDefaultHeader'
import {Text} from '../util/text/Text'
interface Img {
uri: string
alt?: string
}
export const Lightbox = observer(function Lightbox() {
@ -37,7 +39,7 @@ export const Lightbox = observer(function Lightbox() {
}
} else if (activeLightbox instanceof models.ImagesLightbox) {
const opts = activeLightbox
imgs = opts.uris.map(uri => ({uri}))
imgs = opts.images
}
if (!imgs) {
@ -131,6 +133,11 @@ function LightboxInner({
)}
</View>
</TouchableWithoutFeedback>
{imgs[index].alt ? (
<View style={styles.footer}>
<Text style={s.white}>{imgs[index].alt}</Text>
</View>
) : null}
<View style={styles.closeBtn}>
<ImageDefaultHeader onRequestClose={onClose} />
</View>
@ -183,4 +190,9 @@ const styles = StyleSheet.create({
right: 30,
top: '50%',
},
footer: {
paddingHorizontal: 32,
paddingVertical: 24,
backgroundColor: colors.black,
},
})