diff --git a/src/screens/Profile/Header/Shell.tsx b/src/screens/Profile/Header/Shell.tsx
index 26e9406883..925066d72e 100644
--- a/src/screens/Profile/Header/Shell.tsx
+++ b/src/screens/Profile/Header/Shell.tsx
@@ -55,8 +55,18 @@ let ProfileHeaderShell = ({
const modui = moderation.ui('avatar')
if (profile.avatar && !(modui.blur && modui.noOverride)) {
openLightbox({
- type: 'profile-image',
- profile: profile,
+ images: [
+ {
+ uri: profile.avatar,
+ thumbUri: profile.avatar,
+ dimensions: {
+ // It's fine if it's actually smaller but we know it's 1:1.
+ height: 1000,
+ width: 1000,
+ },
+ },
+ ],
+ index: 0,
thumbDims: null,
})
}
diff --git a/src/state/lightbox.tsx b/src/state/lightbox.tsx
index 53409e3ecf..1dae67932c 100644
--- a/src/state/lightbox.tsx
+++ b/src/state/lightbox.tsx
@@ -1,32 +1,15 @@
import React from 'react'
import type {MeasuredDimensions} from 'react-native-reanimated'
-import {AppBskyActorDefs} from '@atproto/api'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
-import {Dimensions} from '#/lib/media/types'
+import {ImageSource} from '#/view/com/lightbox/ImageViewing/@types'
-type ProfileImageLightbox = {
- type: 'profile-image'
- profile: AppBskyActorDefs.ProfileViewDetailed
- thumbDims: null
-}
-
-type ImagesLightboxItem = {
- uri: string
- thumbUri: string
- alt?: string
- dimensions: Dimensions | null
-}
-
-type ImagesLightbox = {
- type: 'images'
- images: ImagesLightboxItem[]
+type Lightbox = {
+ images: ImageSource[]
thumbDims: MeasuredDimensions | null
index: number
}
-type Lightbox = ProfileImageLightbox | ImagesLightbox
-
const LightboxContext = React.createContext<{
activeLightbox: Lightbox | null
}>({
diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx
index 0d0ac4df1f..40df4c8199 100644
--- a/src/view/com/lightbox/ImageViewing/index.tsx
+++ b/src/view/com/lightbox/ImageViewing/index.tsx
@@ -8,13 +8,27 @@
// Original code copied and simplified from the link below as the codebase is currently not maintained:
// https://github.com/jobtoday/react-native-image-viewing
-import React, {ComponentType, useCallback, useMemo, useState} from 'react'
-import {Platform, StyleSheet, View} from 'react-native'
+import React, {useCallback, useMemo, useState} from 'react'
+import {
+ Dimensions,
+ LayoutAnimation,
+ Platform,
+ StyleSheet,
+ View,
+} from 'react-native'
import PagerView from 'react-native-pager-view'
import {MeasuredDimensions} from 'react-native-reanimated'
import Animated, {useAnimatedStyle, withSpring} from 'react-native-reanimated'
+import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {Edge, SafeAreaView} from 'react-native-safe-area-context'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {Trans} from '@lingui/macro'
+import {colors, s} from '#/lib/styles'
+import {isIOS} from '#/platform/detection'
+import {Button} from '#/view/com/util/forms/Button'
+import {Text} from '#/view/com/util/text/Text'
+import {ScrollView} from '#/view/com/util/Views'
import {ImageSource} from './@types'
import ImageDefaultHeader from './components/ImageDefaultHeader'
import ImageItem from './components/ImageItem/ImageItem'
@@ -26,10 +40,11 @@ type Props = {
visible: boolean
onRequestClose: () => void
backgroundColor?: string
- HeaderComponent?: ComponentType<{imageIndex: number}>
- FooterComponent?: ComponentType<{imageIndex: number}>
+ onPressSave: (uri: string) => void
+ onPressShare: (uri: string) => void
}
+const SCREEN_HEIGHT = Dimensions.get('window').height
const DEFAULT_BG_COLOR = '#000'
function ImageViewing({
@@ -39,8 +54,8 @@ function ImageViewing({
visible,
onRequestClose,
backgroundColor = DEFAULT_BG_COLOR,
- HeaderComponent,
- FooterComponent,
+ onPressSave,
+ onPressShare,
}: Props) {
const [isScaled, setIsScaled] = useState(false)
const [isDragging, setIsDragging] = useState(false)
@@ -96,13 +111,7 @@ function ImageViewing({
accessibilityViewIsModal>
- {typeof HeaderComponent !== 'undefined' ? (
- React.createElement(HeaderComponent, {
- imageIndex,
- })
- ) : (
-
- )}
+
))}
- {typeof FooterComponent !== 'undefined' && (
-
- {React.createElement(FooterComponent, {
- imageIndex,
- })}
-
- )}
+
+
+
)
}
+function LightboxFooter({
+ images,
+ index,
+ onPressSave,
+ onPressShare,
+}: {
+ images: ImageSource[]
+ index: number
+ onPressSave: (uri: string) => void
+ onPressShare: (uri: string) => void
+}) {
+ const {alt: altText, uri} = images[index]
+ const [isAltExpanded, setAltExpanded] = React.useState(false)
+ const insets = useSafeAreaInsets()
+ const svMaxHeight = SCREEN_HEIGHT - insets.top - 50
+ const isMomentumScrolling = React.useRef(false)
+ return (
+ {
+ isMomentumScrolling.current = true
+ }}
+ onMomentumScrollEnd={() => {
+ isMomentumScrolling.current = false
+ }}
+ contentContainerStyle={{
+ paddingTop: 16,
+ paddingBottom: insets.bottom + 10,
+ paddingHorizontal: 24,
+ }}>
+ {altText ? (
+
+ {
+ if (isMomentumScrolling.current) {
+ return
+ }
+ LayoutAnimation.configureNext({
+ duration: 450,
+ update: {type: 'spring', springDamping: 1},
+ })
+ setAltExpanded(prev => !prev)
+ }}
+ onLongPress={() => {}}>
+ {altText}
+
+
+ ) : null}
+
+
+
+
+
+ )
+}
+
const styles = StyleSheet.create({
screen: {
position: 'absolute',
@@ -169,6 +260,21 @@ const styles = StyleSheet.create({
zIndex: 1,
bottom: 0,
},
+ footerText: {
+ paddingBottom: isIOS ? 20 : 16,
+ },
+ footerBtns: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ gap: 8,
+ },
+ footerBtn: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 8,
+ backgroundColor: 'transparent',
+ borderColor: colors.white,
+ },
})
const EnhancedImageViewing = (props: Props) => (
diff --git a/src/view/com/lightbox/Lightbox.tsx b/src/view/com/lightbox/Lightbox.tsx
index 83ea2e941b..ed570d5a79 100644
--- a/src/view/com/lightbox/Lightbox.tsx
+++ b/src/view/com/lightbox/Lightbox.tsx
@@ -1,82 +1,25 @@
import React from 'react'
-import {Dimensions, LayoutAnimation, StyleSheet, View} from 'react-native'
-import {useSafeAreaInsets} from 'react-native-safe-area-context'
import * as MediaLibrary from 'expo-media-library'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {msg, Trans} from '@lingui/macro'
+import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {saveImageToMediaLibrary, shareImageModal} from '#/lib/media/manip'
-import {colors, s} from '#/lib/styles'
-import {isIOS} from '#/platform/detection'
import {useLightbox, useLightboxControls} from '#/state/lightbox'
-import {ScrollView} from '#/view/com/util/Views'
-import {Button} from '../util/forms/Button'
-import {Text} from '../util/text/Text'
import * as Toast from '../util/Toast'
import ImageView from './ImageViewing'
-const SCREEN_HEIGHT = Dimensions.get('window').height
-
export function Lightbox() {
const {activeLightbox} = useLightbox()
const {closeLightbox} = useLightboxControls()
+
const onClose = React.useCallback(() => {
closeLightbox()
}, [closeLightbox])
- if (!activeLightbox) {
- return null
- } else if (activeLightbox.type === 'profile-image') {
- const opts = activeLightbox
- return (
-
- )
- } else if (activeLightbox.type === 'images') {
- const opts = activeLightbox
- return (
- ({...img}))}
- initialImageIndex={opts.index}
- thumbDims={opts.thumbDims}
- visible
- onRequestClose={onClose}
- FooterComponent={LightboxFooter}
- />
- )
- } else {
- return null
- }
-}
-
-function LightboxFooter({imageIndex}: {imageIndex: number}) {
const {_} = useLingui()
- const {activeLightbox} = useLightbox()
- const [isAltExpanded, setAltExpanded] = React.useState(false)
const [permissionResponse, requestPermission] = MediaLibrary.usePermissions({
granularPermissions: ['photo'],
})
- const insets = useSafeAreaInsets()
- const svMaxHeight = SCREEN_HEIGHT - insets.top - 50
- const isMomentumScrolling = React.useRef(false)
-
const saveImageToAlbumWithToasts = React.useCallback(
async (uri: string) => {
if (!permissionResponse || permissionResponse.granted === false) {
@@ -96,7 +39,6 @@ function LightboxFooter({imageIndex}: {imageIndex: number}) {
}
return
}
-
try {
await saveImageToMediaLibrary({uri})
Toast.show(_(msg`Saved to your camera roll`))
@@ -107,101 +49,19 @@ function LightboxFooter({imageIndex}: {imageIndex: number}) {
[permissionResponse, requestPermission, _],
)
- const lightbox = activeLightbox
- if (!lightbox) {
+ if (!activeLightbox) {
return null
}
- let altText = ''
- let uri = ''
- if (lightbox.type === 'images') {
- const opts = lightbox
- uri = opts.images[imageIndex].uri
- altText = opts.images[imageIndex].alt || ''
- } else if (lightbox.type === 'profile-image') {
- const opts = lightbox
- uri = opts.profile.avatar || ''
- }
-
return (
- {
- isMomentumScrolling.current = true
- }}
- onMomentumScrollEnd={() => {
- isMomentumScrolling.current = false
- }}
- contentContainerStyle={{
- paddingTop: 16,
- paddingBottom: insets.bottom + 10,
- paddingHorizontal: 24,
- }}>
- {altText ? (
-
- {
- if (isMomentumScrolling.current) {
- return
- }
- LayoutAnimation.configureNext({
- duration: 450,
- update: {type: 'spring', springDamping: 1},
- })
- setAltExpanded(prev => !prev)
- }}
- onLongPress={() => {}}>
- {altText}
-
-
- ) : null}
-
-
-
-
-
+ shareImageModal({uri})}
+ />
)
}
-
-const styles = StyleSheet.create({
- footerText: {
- paddingBottom: isIOS ? 20 : 16,
- },
- footerBtns: {
- flexDirection: 'row',
- justifyContent: 'center',
- gap: 8,
- },
- footerBtn: {
- flexDirection: 'row',
- alignItems: 'center',
- gap: 8,
- backgroundColor: 'transparent',
- borderColor: colors.white,
- },
-})
diff --git a/src/view/com/lightbox/Lightbox.web.tsx b/src/view/com/lightbox/Lightbox.web.tsx
index 9a0fa5d331..2ba7d06cc5 100644
--- a/src/view/com/lightbox/Lightbox.web.tsx
+++ b/src/view/com/lightbox/Lightbox.web.tsx
@@ -38,24 +38,8 @@ export function Lightbox() {
return null
}
- const initialIndex =
- activeLightbox.type === 'images' ? activeLightbox.index : 0
-
- let imgs: Img[] | undefined
- if (activeLightbox.type === 'profile-image') {
- const opts = activeLightbox
- if (opts.profile.avatar) {
- imgs = [{uri: opts.profile.avatar}]
- }
- } else if (activeLightbox.type === 'images') {
- const opts = activeLightbox
- imgs = opts.images
- }
-
- if (!imgs) {
- return null
- }
-
+ const initialIndex = activeLightbox.index
+ const imgs = activeLightbox.images
return (
{
openLightbox({
- type: 'images',
images: items,
index,
thumbDims,