Reduce lightbox button size on native (#10500)

This commit is contained in:
Samuel Newman
2026-05-25 14:34:35 +03:00
committed by GitHub
parent ebed23657f
commit 709589fa36
6 changed files with 51 additions and 65 deletions
+2 -2
View File
@@ -19,8 +19,8 @@ export function Lightbox() {
<ImageView
lightbox={activeLightbox}
onRequestClose={onClose}
onPressSave={saveImageToAlbum}
onPressShare={uri => shareImageModal({uri})}
onPressSave={uri => void saveImageToAlbum(uri)}
onPressShare={uri => void shareImageModal({uri})}
/>
)
}
+19 -23
View File
@@ -1,9 +1,7 @@
import {useCallback, useEffect, useRef, useState} from 'react'
import {Pressable, StyleSheet, View} from 'react-native'
import {Image} from 'expo-image'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {FocusGuards, FocusScope} from 'radix-ui/internal'
import {RemoveScrollBar} from 'react-remove-scroll-bar'
@@ -67,12 +65,12 @@ function LightboxContainer({
children: React.ReactNode
handleBackgroundPress: () => void
}) {
const {_} = useLingui()
const {t: l} = useLingui()
FocusGuards.useFocusGuards()
return (
<Pressable
accessibilityHint={undefined}
accessibilityLabel={_(msg`Close image viewer`)}
accessibilityLabel={l`Close image viewer`}
onPress={handleBackgroundPress}
style={[a.fixed, a.inset_0, a.z_10]}>
<Backdrop />
@@ -81,7 +79,7 @@ function LightboxContainer({
<div
role="dialog"
aria-modal="true"
aria-label={_(msg`Image viewer`)}
aria-label={l`Image viewer`}
style={{position: 'absolute', inset: 0}}>
{children}
</div>
@@ -100,7 +98,7 @@ function LightboxGallery({
onClose: () => void
}) {
const t = useTheme()
const {_} = useLingui()
const {t: l} = useLingui()
const {reduceMotionEnabled} = useA11y()
const [index, setIndex] = useState(initialIndex)
const [hasAnyLoaded, setAnyHasLoaded] = useState(false)
@@ -196,7 +194,7 @@ function LightboxGallery({
]}
hoverStyle={styles.blurredBackdropHover}
color="secondary"
label={_(msg`Previous image`)}
label={l`Previous image`}
shape="round"
size={gtPhone ? 'large' : 'small'}>
<ChevronLeftIcon
@@ -217,7 +215,7 @@ function LightboxGallery({
]}
hoverStyle={styles.blurredBackdropHover}
color="secondary"
label={_(msg`Next image`)}
label={l`Next image`}
shape="round"
size={gtPhone ? 'large' : 'small'}>
<ChevronRightIcon
@@ -236,10 +234,8 @@ function LightboxGallery({
delayedFadeInAnim,
]}>
<Pressable
accessibilityLabel={_(msg`Expand alt text`)}
accessibilityHint={_(
msg`If alt text is long, toggles alt text expanded state`,
)}
accessibilityLabel={l`Expand alt text`}
accessibilityHint={l`If alt text is long, toggles alt text expanded state`}
onPress={() => {
setAltExpanded(!isAltExpanded)
}}>
@@ -254,11 +250,11 @@ function LightboxGallery({
) : null}
{imgs.length > 1 && (
<div aria-live="polite" aria-atomic="true" style={a.sr_only}>
<Text>{_(msg`Image ${index + 1} of ${imgs.length}`)}</Text>
<Text>{l`Image ${index + 1} of ${imgs.length}`}</Text>
</div>
)}
<Menu.Root>
<Menu.Trigger label={_(msg`Image options`)}>
<Menu.Trigger label={l`Image options`}>
{({props}) => (
<View style={[a.absolute, styles.menuBtn, delayedFadeInAnim]}>
<CircleChromeButton
@@ -266,7 +262,7 @@ function LightboxGallery({
accessible={false}
icon={EllipsisIcon}
iconStyle={{transform: [{rotate: '90deg'}]}}
label={_(msg`Image options`)}
label={l`Image options`}
/>
</View>
)}
@@ -274,7 +270,7 @@ function LightboxGallery({
<Menu.Outer>
<Menu.Group>
<Menu.Item
label={_(msg`Share image`)}
label={l`Share image`}
onPress={async () => {
const url = img.uri
if (
@@ -293,9 +289,9 @@ function LightboxGallery({
) {
try {
await navigator.clipboard.writeText(url)
Toast.show(_(msg`Link copied to clipboard`))
Toast.show(l`Link copied to clipboard`)
} catch {
Toast.show(_(msg`Failed to copy link`), {type: 'error'})
Toast.show(l`Failed to copy link`, {type: 'error'})
}
}
}}>
@@ -305,14 +301,14 @@ function LightboxGallery({
<Menu.ItemIcon icon={ShareIcon} position="right" />
</Menu.Item>
<Menu.Item
label={_(msg`Download image`)}
label={l`Download image`}
onPress={() => {
saveImageToMediaLibrary({uri: img.uri}).then(
() => {
Toast.show(_(msg`Image saved`))
Toast.show(l`Image saved`)
},
() => {
Toast.show(_(msg`Failed to save image`), {type: 'error'})
Toast.show(l`Failed to save image`, {type: 'error'})
},
)
}}>
@@ -327,7 +323,7 @@ function LightboxGallery({
<View style={[a.absolute, styles.closeBtn, delayedFadeInAnim]}>
<CircleChromeButton
icon={XIcon}
label={_(msg`Close image viewer`)}
label={l`Close image viewer`}
onPress={onClose}
/>
</View>
@@ -8,7 +8,7 @@ import {
} from 'react-native'
import {BlurView} from 'expo-blur'
import {HITSLOP_10} from '#/lib/constants'
import {HITSLOP_20} from '#/lib/constants'
import {type Props as IconProps} from '#/components/icons/common'
type Props = {
@@ -27,9 +27,9 @@ type Props = {
| 'accessibilityHint'
>
const SIZE = 44
const RADIUS = 24
const ICON = 24
const SIZE = 32
const RADIUS = SIZE / 2
const ICON = 18
export function CircleChromeButton({
icon: Icon,
@@ -45,7 +45,7 @@ export function CircleChromeButton({
accessibilityRole="button"
accessibilityLabel={label}
accessibilityHint=""
hitSlop={HITSLOP_10}
hitSlop={HITSLOP_20}
onPress={onPress}
testID={testID}
style={({pressed}) => [styles.root, pressed && styles.pressed]}>
+14 -17
View File
@@ -7,10 +7,9 @@ import {
View,
} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {useLingui} from '@lingui/react/macro'
import {atoms as a} from '#/alf'
import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography'
type Props = {
@@ -20,7 +19,8 @@ type Props = {
}
export function Footer({altText, isAltExpanded, onToggleAltExpanded}: Props) {
const {_} = useLingui()
const {t: l} = useLingui()
const t = useTheme()
const insets = useSafeAreaInsets()
const isMomentumScrolling = useRef(false)
@@ -28,8 +28,14 @@ export function Footer({altText, isAltExpanded, onToggleAltExpanded}: Props) {
return (
<View
style={[styles.root, {paddingBottom: insets.bottom + 8}]}
pointerEvents="box-none">
style={[
a.absolute,
a.left_0,
a.right_0,
a.bottom_0,
a.pointer_events_box_none,
{paddingBottom: insets.bottom + 8},
]}>
<View style={[a.mx_md, styles.altWrap]}>
<ScrollView
scrollEnabled={isAltExpanded}
@@ -42,7 +48,7 @@ export function Footer({altText, isAltExpanded, onToggleAltExpanded}: Props) {
contentContainerStyle={[a.px_md, a.py_sm]}>
<Pressable
accessibilityRole="button"
accessibilityLabel={_(msg`Expand alt text`)}
accessibilityLabel={l`Expand alt text`}
accessibilityHint=""
onPress={() => {
if (isMomentumScrolling.current) return
@@ -55,7 +61,7 @@ export function Footer({altText, isAltExpanded, onToggleAltExpanded}: Props) {
<Text
emoji
selectable
style={[a.text_sm, styles.altText]}
style={[a.text_sm, {color: t.palette.white}]}
numberOfLines={isAltExpanded ? undefined : 3}>
{altText}
</Text>
@@ -67,18 +73,9 @@ export function Footer({altText, isAltExpanded, onToggleAltExpanded}: Props) {
}
const styles = StyleSheet.create({
root: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
},
altWrap: {
backgroundColor: 'rgba(0, 0, 0, 0.45)',
borderRadius: 12,
overflow: 'hidden',
},
altText: {
color: '#fff',
},
})
+10 -17
View File
@@ -1,7 +1,6 @@
import {StyleSheet, View} from 'react-native'
import {View} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {useLingui} from '@lingui/react/macro'
import {atoms as a} from '#/alf'
import {TimesLarge_Stroke2_Corner0_Rounded as CloseIcon} from '#/components/icons/Times'
@@ -24,36 +23,30 @@ export function Header({
imageCount,
activeIndex,
}: Props) {
const {_} = useLingui()
const {t: l} = useLingui()
const insets = useSafeAreaInsets()
return (
<View
style={[
styles.root,
a.absolute,
a.top_0,
a.left_0,
a.right_0,
a.flex_row,
a.justify_between,
a.align_center,
a.px_md,
a.pointer_events_box_none,
{paddingTop: insets.top + 8},
]}
pointerEvents="box-none">
]}>
<ImageMenu onPressShare={onPressShare} onPressSave={onPressSave} />
<PagerDots count={imageCount} activeIndex={activeIndex} />
<CircleChromeButton
icon={CloseIcon}
label={_(msg`Close image`)}
label={l`Close image`}
onPress={onRequestClose}
/>
</View>
)
}
const styles = StyleSheet.create({
root: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
},
})
+1 -1
View File
@@ -84,7 +84,7 @@ export function ImageMenu({onPressShare, onPressSave}: Props) {
accessibilityRole="button"
accessibilityLabel={l`Close menu`}
accessibilityHint=""
style={StyleSheet.absoluteFill}
style={[a.absolute, a.inset_0]}
onPress={close}
/>
{anchor && (