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 showControls={showControls}
126 />
127 </View>
128 ))}
129 </PagerView>
130 {typeof FooterComponent !== 'undefined' && (
131 <Animated.View style={[styles.footer, animatedFooterStyle]}>
132 {React.createElement(FooterComponent, {
133 imageIndex,
134 })}
135 </Animated.View>
136 )}
137 </View>
138 </SafeAreaView>
139 )
140}
141
142const styles = StyleSheet.create({
143 screen: {
144 position: 'absolute',
145 top: 0,
146 left: 0,
147 bottom: 0,
148 right: 0,
149 },
150 container: {
151 flex: 1,
152 backgroundColor: '#000',
153 },
154 pager: {
155 flex: 1,
156 },
157 header: {
158 position: 'absolute',
159 width: '100%',
160 zIndex: 1,
161 top: 0,
162 pointerEvents: 'box-none',
163 },
164 footer: {
165 position: 'absolute',
166 width: '100%',
167 zIndex: 1,
168 bottom: 0,
169 },
170})
171
172const EnhancedImageViewing = (props: Props) => (
173 <ImageViewing key={props.initialImageIndex} {...props} />
174)
175
176function withClampedSpring(value: any) {
177 'worklet'
178 return withSpring(value, {overshootClamping: true, stiffness: 300})
179}
180
181export default EnhancedImageViewing