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