mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1/**
2 * Copyright (c) JOB TODAY S.A. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8// Original code copied and simplified from the link below as the codebase is currently not maintained:
9// https://github.com/jobtoday/react-native-image-viewing
10
11import React, {ComponentType, useCallback, useMemo, useState} from 'react'
12import {StyleSheet, View, Platform} from 'react-native'
13
14import ImageItem from './components/ImageItem/ImageItem'
15import ImageDefaultHeader from './components/ImageDefaultHeader'
16
17import {ImageSource} from './@types'
18import Animated, {useAnimatedStyle, withSpring} from 'react-native-reanimated'
19import {Edge, SafeAreaView} from 'react-native-safe-area-context'
20import PagerView from 'react-native-pager-view'
21
22type Props = {
23 images: ImageSource[]
24 initialImageIndex: number
25 visible: boolean
26 onRequestClose: () => void
27 backgroundColor?: string
28 HeaderComponent?: ComponentType<{imageIndex: number}>
29 FooterComponent?: ComponentType<{imageIndex: number}>
30}
31
32const DEFAULT_BG_COLOR = '#000'
33
34function ImageViewing({
35 images,
36 initialImageIndex,
37 visible,
38 onRequestClose,
39 backgroundColor = DEFAULT_BG_COLOR,
40 HeaderComponent,
41 FooterComponent,
42}: Props) {
43 const [isScaled, setIsScaled] = useState(false)
44 const [isDragging, setIsDragging] = useState(false)
45 const [imageIndex, setImageIndex] = useState(initialImageIndex)
46 const [showControls, setShowControls] = useState(true)
47
48 const animatedHeaderStyle = useAnimatedStyle(() => ({
49 pointerEvents: showControls ? 'auto' : 'none',
50 opacity: withClampedSpring(showControls ? 1 : 0),
51 transform: [
52 {
53 translateY: withClampedSpring(showControls ? 0 : -30),
54 },
55 ],
56 }))
57 const animatedFooterStyle = useAnimatedStyle(() => ({
58 pointerEvents: showControls ? 'auto' : 'none',
59 opacity: withClampedSpring(showControls ? 1 : 0),
60 transform: [
61 {
62 translateY: withClampedSpring(showControls ? 0 : 30),
63 },
64 ],
65 }))
66
67 const onTap = useCallback(() => {
68 setShowControls(show => !show)
69 }, [])
70
71 const onZoom = useCallback((nextIsScaled: boolean) => {
72 setIsScaled(nextIsScaled)
73 if (nextIsScaled) {
74 setShowControls(false)
75 }
76 }, [])
77
78 const edges = useMemo(() => {
79 if (Platform.OS === 'android') {
80 return ['top', 'bottom', 'left', 'right'] satisfies Edge[]
81 }
82 return ['left', 'right'] satisfies Edge[] // iOS, so no top/bottom safe area
83 }, [])
84
85 if (!visible) {
86 return null
87 }
88
89 return (
90 <SafeAreaView
91 style={styles.screen}
92 edges={edges}
93 aria-modal
94 accessibilityViewIsModal>
95 <View style={[styles.container, {backgroundColor}]}>
96 <Animated.View style={[styles.header, animatedHeaderStyle]}>
97 {typeof HeaderComponent !== 'undefined' ? (
98 React.createElement(HeaderComponent, {
99 imageIndex,
100 })
101 ) : (
102 <ImageDefaultHeader onRequestClose={onRequestClose} />
103 )}
104 </Animated.View>
105 <PagerView
106 scrollEnabled={!isScaled}
107 initialPage={initialImageIndex}
108 onPageSelected={e => {
109 setImageIndex(e.nativeEvent.position)
110 setIsScaled(false)
111 }}
112 onPageScrollStateChanged={e => {
113 setIsDragging(e.nativeEvent.pageScrollState !== 'idle')
114 }}
115 overdrag={true}
116 style={styles.pager}>
117 {images.map(imageSrc => (
118 <View key={imageSrc.uri}>
119 <ImageItem
120 onTap={onTap}
121 onZoom={onZoom}
122 imageSrc={imageSrc}
123 onRequestClose={onRequestClose}
124 isScrollViewBeingDragged={isDragging}
125 />
126 </View>
127 ))}
128 </PagerView>
129 {typeof FooterComponent !== 'undefined' && (
130 <Animated.View style={[styles.footer, animatedFooterStyle]}>
131 {React.createElement(FooterComponent, {
132 imageIndex,
133 })}
134 </Animated.View>
135 )}
136 </View>
137 </SafeAreaView>
138 )
139}
140
141const styles = StyleSheet.create({
142 screen: {
143 position: 'absolute',
144 top: 0,
145 left: 0,
146 bottom: 0,
147 right: 0,
148 },
149 container: {
150 flex: 1,
151 backgroundColor: '#000',
152 },
153 pager: {
154 flex: 1,
155 },
156 header: {
157 position: 'absolute',
158 width: '100%',
159 zIndex: 1,
160 top: 0,
161 pointerEvents: 'box-none',
162 },
163 footer: {
164 position: 'absolute',
165 width: '100%',
166 zIndex: 1,
167 bottom: 0,
168 },
169})
170
171const EnhancedImageViewing = (props: Props) => (
172 <ImageViewing key={props.initialImageIndex} {...props} />
173)
174
175function withClampedSpring(value: any) {
176 'worklet'
177 return withSpring(value, {overshootClamping: true, stiffness: 300})
178}
179
180export default EnhancedImageViewing