From 26c48373e29643ca7a3248315506131d53d05b8b Mon Sep 17 00:00:00 2001
From: dan <dan.abramov@gmail.com>
Date: Mon, 4 Nov 2024 21:31:21 +0000
Subject: Refactor lightbox prop drilling (#6073)

* Refactor lightbox footer to render prop

* Unify lightbox types

* Unindent

* Refactor LightboxFooter props

* Move LightboxFooter into the implementation file
---
 src/view/com/lightbox/ImageViewing/index.tsx | 146 +++++++++++++++++++++++----
 1 file changed, 126 insertions(+), 20 deletions(-)

(limited to 'src/view/com/lightbox/ImageViewing/index.tsx')

diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx
index 0d0ac4df1..40df4c819 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>
       <View style={[styles.container, {backgroundColor}]}>
         <Animated.View style={[styles.header, animatedHeaderStyle]}>
-          {typeof HeaderComponent !== 'undefined' ? (
-            React.createElement(HeaderComponent, {
-              imageIndex,
-            })
-          ) : (
-            <ImageDefaultHeader onRequestClose={onRequestClose} />
-          )}
+          <ImageDefaultHeader onRequestClose={onRequestClose} />
         </Animated.View>
         <PagerView
           scrollEnabled={!isScaled}
@@ -129,18 +138,100 @@ function ImageViewing({
             </View>
           ))}
         </PagerView>
-        {typeof FooterComponent !== 'undefined' && (
-          <Animated.View style={[styles.footer, animatedFooterStyle]}>
-            {React.createElement(FooterComponent, {
-              imageIndex,
-            })}
-          </Animated.View>
-        )}
+        <Animated.View style={[styles.footer, animatedFooterStyle]}>
+          <LightboxFooter
+            images={images}
+            index={imageIndex}
+            onPressSave={onPressSave}
+            onPressShare={onPressShare}
+          />
+        </Animated.View>
       </View>
     </SafeAreaView>
   )
 }
 
+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 (
+    <ScrollView
+      style={[
+        {
+          backgroundColor: '#000d',
+        },
+        {maxHeight: svMaxHeight},
+      ]}
+      scrollEnabled={isAltExpanded}
+      onMomentumScrollBegin={() => {
+        isMomentumScrolling.current = true
+      }}
+      onMomentumScrollEnd={() => {
+        isMomentumScrolling.current = false
+      }}
+      contentContainerStyle={{
+        paddingTop: 16,
+        paddingBottom: insets.bottom + 10,
+        paddingHorizontal: 24,
+      }}>
+      {altText ? (
+        <View accessibilityRole="button" style={styles.footerText}>
+          <Text
+            style={[s.gray3]}
+            numberOfLines={isAltExpanded ? undefined : 3}
+            selectable
+            onPress={() => {
+              if (isMomentumScrolling.current) {
+                return
+              }
+              LayoutAnimation.configureNext({
+                duration: 450,
+                update: {type: 'spring', springDamping: 1},
+              })
+              setAltExpanded(prev => !prev)
+            }}
+            onLongPress={() => {}}>
+            {altText}
+          </Text>
+        </View>
+      ) : null}
+      <View style={styles.footerBtns}>
+        <Button
+          type="primary-outline"
+          style={styles.footerBtn}
+          onPress={() => onPressSave(uri)}>
+          <FontAwesomeIcon icon={['far', 'floppy-disk']} style={s.white} />
+          <Text type="xl" style={s.white}>
+            <Trans context="action">Save</Trans>
+          </Text>
+        </Button>
+        <Button
+          type="primary-outline"
+          style={styles.footerBtn}
+          onPress={() => onPressShare(uri)}>
+          <FontAwesomeIcon icon="arrow-up-from-bracket" style={s.white} />
+          <Text type="xl" style={s.white}>
+            <Trans context="action">Share</Trans>
+          </Text>
+        </Button>
+      </View>
+    </ScrollView>
+  )
+}
+
 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) => (
-- 
cgit 1.4.1

