Browse Source

Replace image modal by image gallery screen

This improves performance and allows multi-images view
Arnaud Vergnet 1 year ago
parent
commit
6ec87821e8

+ 42
- 0
package-lock.json View File

@@ -10146,6 +10146,15 @@
10146 10146
       "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
10147 10147
       "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
10148 10148
     },
10149
+    "react-mixin": {
10150
+      "version": "3.1.1",
10151
+      "resolved": "https://registry.npmjs.org/react-mixin/-/react-mixin-3.1.1.tgz",
10152
+      "integrity": "sha512-z9fZ0aCRDjlgxLdMeWkJ9TwhmVLhQ09r8RFpin/cEPA2T6jsb7YHNWcIe0Oii+hhJNyMymdy91CSya5mRkuCkg==",
10153
+      "requires": {
10154
+        "object-assign": "^4.0.1",
10155
+        "smart-mixin": "^2.0.0"
10156
+      }
10157
+    },
10149 10158
     "react-native": {
10150 10159
       "version": "0.63.2",
10151 10160
       "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.63.2.tgz",
@@ -10383,11 +10392,34 @@
10383 10392
         }
10384 10393
       }
10385 10394
     },
10395
+    "react-native-image-gallery": {
10396
+      "version": "2.1.5",
10397
+      "resolved": "https://registry.npmjs.org/react-native-image-gallery/-/react-native-image-gallery-2.1.5.tgz",
10398
+      "integrity": "sha512-xC7nuPu4GUH0da6byofQ10LjtqlKj+VaLc0NHBJmeMHVvdvmRvFEO6UOq0Q0m/ePx3OQiPTNwGbf5BSPJEKa0w==",
10399
+      "requires": {
10400
+        "prop-types": "^15.6.0",
10401
+        "react-mixin": "^3.0.5",
10402
+        "react-timer-mixin": "^0.13.3"
10403
+      }
10404
+    },
10386 10405
     "react-native-image-modal": {
10387 10406
       "version": "1.0.9",
10388 10407
       "resolved": "https://registry.npmjs.org/react-native-image-modal/-/react-native-image-modal-1.0.9.tgz",
10389 10408
       "integrity": "sha512-d01lx82CgC2s8zzpmKAbSAK/rABFl8BhKHSrp6GLqColNuUHaNuDIuu67TdqzHsY9pT4OHY0rduWyvrmRiSrLw=="
10390 10409
     },
10410
+    "react-native-image-pan-zoom": {
10411
+      "version": "2.1.12",
10412
+      "resolved": "https://registry.npmjs.org/react-native-image-pan-zoom/-/react-native-image-pan-zoom-2.1.12.tgz",
10413
+      "integrity": "sha512-BF66XeP6dzuANsPmmFsJshM2Jyh/Mo1t8FsGc1L9Q9/sVP8MJULDabB1hms+eAoqgtyhMr5BuXV3E1hJ5U5H6Q=="
10414
+    },
10415
+    "react-native-image-zoom-viewer": {
10416
+      "version": "3.0.1",
10417
+      "resolved": "https://registry.npmjs.org/react-native-image-zoom-viewer/-/react-native-image-zoom-viewer-3.0.1.tgz",
10418
+      "integrity": "sha512-la6s5DNSuq4GCRLsi5CZ29FPjgTpdCuGIRdO5T9rUrAtxrlpBPhhSnHrbmPVxsdtOUvxHacTh2Gfa9+RraMZQA==",
10419
+      "requires": {
10420
+        "react-native-image-pan-zoom": "^2.1.12"
10421
+      }
10422
+    },
10391 10423
     "react-native-iphone-x-helper": {
10392 10424
       "version": "1.2.1",
10393 10425
       "resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.2.1.tgz",
@@ -10632,6 +10664,11 @@
10632 10664
         "scheduler": "^0.19.1"
10633 10665
       }
10634 10666
     },
10667
+    "react-timer-mixin": {
10668
+      "version": "0.13.4",
10669
+      "resolved": "https://registry.npmjs.org/react-timer-mixin/-/react-timer-mixin-0.13.4.tgz",
10670
+      "integrity": "sha512-4+ow23tp/Tv7hBM5Az5/Be/eKKF7DIvJ09voz5LyHGQaqqz9WV8YMs31eFvcYQs7d451LSg7kDJV70XYN/Ug/Q=="
10671
+    },
10635 10672
     "read-pkg": {
10636 10673
       "version": "2.0.0",
10637 10674
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@@ -11227,6 +11264,11 @@
11227 11264
       "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
11228 11265
       "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
11229 11266
     },
11267
+    "smart-mixin": {
11268
+      "version": "2.0.0",
11269
+      "resolved": "https://registry.npmjs.org/smart-mixin/-/smart-mixin-2.0.0.tgz",
11270
+      "integrity": "sha1-o0oQVeMqdbMNK048oyPcmctT9Dc="
11271
+    },
11230 11272
     "snapdragon": {
11231 11273
       "version": "0.8.2",
11232 11274
       "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",

+ 1
- 1
package.json View File

@@ -39,7 +39,7 @@
39 39
     "react-native-camera": "^3.35.0",
40 40
     "react-native-collapsible": "^1.5.3",
41 41
     "react-native-gesture-handler": "^1.7.0",
42
-    "react-native-image-modal": "^1.0.9",
42
+    "react-native-image-zoom-viewer": "^3.0.1",
43 43
     "react-native-keychain": "^6.1.1",
44 44
     "react-native-linear-gradient": "^2.5.6",
45 45
     "react-native-localize": "^1.4.1",

+ 4
- 7
src/components/Home/FeedItem.js View File

@@ -5,11 +5,11 @@ import {Button, Card, Text, TouchableRipple} from 'react-native-paper';
5 5
 import {Image, View} from 'react-native';
6 6
 import Autolink from 'react-native-autolink';
7 7
 import i18n from 'i18n-js';
8
-import ImageModal from 'react-native-image-modal';
9 8
 import {StackNavigationProp} from '@react-navigation/stack';
10 9
 import type {FeedItemType} from '../../screens/Home/HomeScreen';
11 10
 import NewsSourcesConstants from '../../constants/NewsSourcesConstants';
12 11
 import type {NewsSourceType} from '../../constants/NewsSourcesConstants';
12
+import ImageGalleryButton from '../Media/ImageGalleryButton';
13 13
 
14 14
 type PropsType = {
15 15
   navigation: StackNavigationProp,
@@ -82,16 +82,13 @@ class FeedItem extends React.Component<PropsType> {
82 82
             />
83 83
             {hasImage ? (
84 84
               <View style={{marginLeft: 'auto', marginRight: 'auto'}}>
85
-                <ImageModal
86
-                  resizeMode="contain"
87
-                  imageBackgroundColor="#000"
85
+                <ImageGalleryButton
86
+                  navigation={props.navigation}
87
+                  images={[{url: item.image}]}
88 88
                   style={{
89 89
                     width: imageSize,
90 90
                     height: imageSize,
91 91
                   }}
92
-                  source={{
93
-                    uri: item.image,
94
-                  }}
95 92
                 />
96 93
               </View>
97 94
             ) : null}

+ 40
- 0
src/components/Media/ImageGalleryButton.js View File

@@ -0,0 +1,40 @@
1
+// @flow
2
+
3
+import * as React from 'react';
4
+import {TouchableRipple, withTheme} from 'react-native-paper';
5
+import {StackNavigationProp} from '@react-navigation/stack';
6
+import {Image} from 'react-native-animatable';
7
+import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
8
+import type {CustomThemeType} from '../../managers/ThemeManager';
9
+
10
+type PropsType = {
11
+  navigation: StackNavigationProp,
12
+  images: Array<{url: string}>,
13
+  theme: CustomThemeType,
14
+  style: ViewStyleProp,
15
+};
16
+
17
+class ImageGalleryButton extends React.Component<PropsType> {
18
+  onPress = () => {
19
+    const {navigation, images} = this.props;
20
+    navigation.navigate('gallery', {images});
21
+  };
22
+
23
+  render(): React.Node {
24
+    const {style, images} = this.props;
25
+    return (
26
+      <TouchableRipple onPress={this.onPress} style={style}>
27
+        <Image
28
+          resizeMode="contain"
29
+          source={{uri: images[0].url}}
30
+          style={{
31
+            width: '100%',
32
+            height: '100%',
33
+          }}
34
+        />
35
+      </TouchableRipple>
36
+    );
37
+  }
38
+}
39
+
40
+export default withTheme(ImageGalleryButton);

+ 9
- 0
src/navigation/MainNavigator.js View File

@@ -31,6 +31,7 @@ import EquipmentLendScreen from '../screens/Amicale/Equipment/EquipmentRentScree
31 31
 import EquipmentConfirmScreen from '../screens/Amicale/Equipment/EquipmentConfirmScreen';
32 32
 import DashboardEditScreen from '../screens/Other/Settings/DashboardEditScreen';
33 33
 import GameStartScreen from '../screens/Game/screens/GameStartScreen';
34
+import ImageGalleryScreen from '../screens/Media/ImageGalleryScreen';
34 35
 
35 36
 const modalTransition =
36 37
   Platform.OS === 'ios'
@@ -62,6 +63,14 @@ function MainStackComponent(props: {
62 63
           title: i18n.t('screens.home.title'),
63 64
         }}
64 65
       />
66
+      <MainStack.Screen
67
+        name="gallery"
68
+        component={ImageGalleryScreen}
69
+        options={{
70
+          headerShown: false,
71
+          ...modalTransition,
72
+        }}
73
+      />
65 74
       {createScreenCollapsibleStack(
66 75
         'settings',
67 76
         MainStack,

+ 9
- 16
src/screens/Amicale/Clubs/ClubDisplayScreen.js View File

@@ -10,7 +10,6 @@ import {
10 10
   Paragraph,
11 11
   withTheme,
12 12
 } from 'react-native-paper';
13
-import ImageModal from 'react-native-image-modal';
14 13
 import i18n from 'i18n-js';
15 14
 import {StackNavigationProp} from '@react-navigation/stack';
16 15
 import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen';
@@ -22,6 +21,7 @@ import {ERROR_TYPE} from '../../../utils/WebData';
22 21
 import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
23 22
 import type {ApiGenericDataType} from '../../../utils/WebData';
24 23
 import type {CardTitleIconPropsType} from '../../../constants/PaperStyles';
24
+import ImageGalleryButton from '../../../components/Media/ImageGalleryButton';
25 25
 
26 26
 type PropsType = {
27 27
   navigation: StackNavigationProp,
@@ -188,7 +188,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
188 188
   }
189 189
 
190 190
   getScreen = (response: Array<ApiGenericDataType | null>): React.Node => {
191
-    const {props} = this;
191
+    const {navigation} = this.props;
192 192
     let data: ClubType | null = null;
193 193
     if (response[0] != null) {
194 194
       [data] = response;
@@ -199,25 +199,18 @@ class ClubDisplayScreen extends React.Component<PropsType> {
199 199
         <CollapsibleScrollView style={{paddingLeft: 5, paddingRight: 5}} hasTab>
200 200
           {this.getCategoriesRender(data.category)}
201 201
           {data.logo !== null ? (
202
-            <View
202
+            <ImageGalleryButton
203
+              navigation={navigation}
204
+              images={[{url: data.logo}]}
203 205
               style={{
206
+                width: 300,
207
+                height: 300,
204 208
                 marginLeft: 'auto',
205 209
                 marginRight: 'auto',
206 210
                 marginTop: 10,
207 211
                 marginBottom: 10,
208
-              }}>
209
-              <ImageModal
210
-                resizeMode="contain"
211
-                imageBackgroundColor={props.theme.colors.background}
212
-                style={{
213
-                  width: 300,
214
-                  height: 300,
215
-                }}
216
-                source={{
217
-                  uri: data.logo,
218
-                }}
219
-              />
220
-            </View>
212
+              }}
213
+            />
221 214
           ) : (
222 215
             <View />
223 216
           )}

+ 13
- 15
src/screens/Home/FeedItemScreen.js View File

@@ -1,9 +1,8 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {Linking, View} from 'react-native';
4
+import {Linking} from 'react-native';
5 5
 import {Avatar, Card, Text, withTheme} from 'react-native-paper';
6
-import ImageModal from 'react-native-image-modal';
7 6
 import Autolink from 'react-native-autolink';
8 7
 import {StackNavigationProp} from '@react-navigation/stack';
9 8
 import MaterialHeaderButtons, {
@@ -12,6 +11,7 @@ import MaterialHeaderButtons, {
12 11
 import CustomTabBar from '../../components/Tabbar/CustomTabBar';
13 12
 import type {FeedItemType} from './HomeScreen';
14 13
 import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
14
+import ImageGalleryButton from '../../components/Media/ImageGalleryButton';
15 15
 
16 16
 type PropsType = {
17 17
   navigation: StackNavigationProp,
@@ -69,6 +69,7 @@ class FeedItemScreen extends React.Component<PropsType> {
69 69
   };
70 70
 
71 71
   render(): React.Node {
72
+    const {navigation} = this.props;
72 73
     const hasImage =
73 74
       this.displayData.image !== '' && this.displayData.image != null;
74 75
     return (
@@ -85,19 +86,16 @@ class FeedItemScreen extends React.Component<PropsType> {
85 86
           )}
86 87
         />
87 88
         {hasImage ? (
88
-          <View style={{marginLeft: 'auto', marginRight: 'auto'}}>
89
-            <ImageModal
90
-              resizeMode="contain"
91
-              imageBackgroundColor="#000"
92
-              style={{
93
-                width: 250,
94
-                height: 250,
95
-              }}
96
-              source={{
97
-                uri: this.displayData.image,
98
-              }}
99
-            />
100
-          </View>
89
+          <ImageGalleryButton
90
+            navigation={navigation}
91
+            images={[{url: this.displayData.image}]}
92
+            style={{
93
+              width: 250,
94
+              height: 250,
95
+              marginLeft: 'auto',
96
+              marginRight: 'auto',
97
+            }}
98
+          />
101 99
         ) : null}
102 100
         <Card.Content style={{paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
103 101
           {this.displayData.message !== undefined ? (

+ 126
- 0
src/screens/Media/ImageGalleryScreen.js View File

@@ -0,0 +1,126 @@
1
+// @flow
2
+
3
+import * as React from 'react';
4
+import {IconButton, Text} from 'react-native-paper';
5
+import ImageViewer from 'react-native-image-zoom-viewer';
6
+import {StackNavigationProp} from '@react-navigation/stack';
7
+import * as Animatable from 'react-native-animatable';
8
+
9
+type PropsType = {
10
+  navigation: StackNavigationProp,
11
+  route: {params: {images: Array<{url: string}>}},
12
+};
13
+
14
+class ImageGalleryScreen extends React.Component<PropsType> {
15
+  images: Array<{url: string}>;
16
+
17
+  closeButtonRef: {current: null | Animatable.View};
18
+
19
+  indicatorRef: {current: null | Animatable.View};
20
+
21
+  controlsShown: boolean;
22
+
23
+  constructor(props: PropsType) {
24
+    super(props);
25
+    this.closeButtonRef = React.createRef();
26
+    this.indicatorRef = React.createRef();
27
+    this.controlsShown = true;
28
+    if (props.route.params != null) this.images = props.route.params.images;
29
+  }
30
+
31
+  goBack = () => {
32
+    const {navigation} = this.props;
33
+    navigation.goBack();
34
+  };
35
+
36
+  getRenderHeader = (): React.Node => {
37
+    return (
38
+      <Animatable.View
39
+        ref={this.closeButtonRef}
40
+        useNativeDriver
41
+        style={{
42
+          position: 'absolute',
43
+          top: 10,
44
+          left: 10,
45
+          zIndex: 1000,
46
+        }}>
47
+        <IconButton
48
+          onPress={this.goBack}
49
+          icon="close"
50
+          size={30}
51
+          style={{backgroundColor: 'rgba(0,0,0,0.6)'}}
52
+        />
53
+      </Animatable.View>
54
+    );
55
+  };
56
+
57
+  getRenderIndicator = (
58
+    currentIndex?: number,
59
+    allSize?: number,
60
+  ): React.Node => {
61
+    if (currentIndex != null && allSize != null && allSize !== 1)
62
+      return (
63
+        <Animatable.View
64
+          ref={this.indicatorRef}
65
+          useNativeDriver
66
+          style={{
67
+            width: '100%',
68
+            height: 50,
69
+            position: 'absolute',
70
+          }}>
71
+          <Text
72
+            style={{
73
+              marginLeft: 'auto',
74
+              marginRight: 'auto',
75
+              marginTop: 'auto',
76
+              marginBottom: 'auto',
77
+              fontSize: 15,
78
+              backgroundColor: 'rgba(0,0,0,0.6)',
79
+              padding: 10,
80
+              borderRadius: 20,
81
+            }}>
82
+            {currentIndex}/{allSize}
83
+          </Text>
84
+        </Animatable.View>
85
+      );
86
+    return null;
87
+  };
88
+
89
+  onImageClick = () => {
90
+    if (this.controlsShown) this.hideControls();
91
+    else this.showControls();
92
+    this.controlsShown = !this.controlsShown;
93
+  };
94
+
95
+  hideControls() {
96
+    if (this.closeButtonRef.current != null)
97
+      this.closeButtonRef.current.fadeOutUp(200);
98
+    if (this.indicatorRef.current != null)
99
+      this.indicatorRef.current.fadeOutUp(300);
100
+  }
101
+
102
+  showControls() {
103
+    if (this.closeButtonRef.current != null)
104
+      this.closeButtonRef.current.fadeInDown(300);
105
+    if (this.indicatorRef.current != null)
106
+      this.indicatorRef.current.fadeInDown(400);
107
+  }
108
+
109
+  render(): React.Node {
110
+    return (
111
+      <ImageViewer
112
+        useNativeDriver
113
+        imageUrls={this.images}
114
+        enableSwipeDown
115
+        onSwipeDown={this.goBack}
116
+        renderHeader={this.getRenderHeader}
117
+        renderIndicator={this.getRenderIndicator}
118
+        pageAnimateTime={100}
119
+        onClick={this.onImageClick}
120
+        doubleClickInterval={250}
121
+      />
122
+    );
123
+  }
124
+}
125
+
126
+export default ImageGalleryScreen;

+ 14
- 19
src/screens/Planning/PlanningDisplayScreen.js View File

@@ -2,8 +2,7 @@
2 2
 
3 3
 import * as React from 'react';
4 4
 import {View} from 'react-native';
5
-import {Card, withTheme} from 'react-native-paper';
6
-import ImageModal from 'react-native-image-modal';
5
+import {Card} from 'react-native-paper';
7 6
 import i18n from 'i18n-js';
8 7
 import {StackNavigationProp} from '@react-navigation/stack';
9 8
 import {getDateOnlyString, getFormattedEventTime} from '../../utils/Planning';
@@ -13,14 +12,13 @@ import {apiRequest, ERROR_TYPE} from '../../utils/WebData';
13 12
 import ErrorView from '../../components/Screens/ErrorView';
14 13
 import CustomHTML from '../../components/Overrides/CustomHTML';
15 14
 import CustomTabBar from '../../components/Tabbar/CustomTabBar';
16
-import type {CustomThemeType} from '../../managers/ThemeManager';
17 15
 import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
18 16
 import type {PlanningEventType} from '../../utils/Planning';
17
+import ImageGalleryButton from '../../components/Media/ImageGalleryButton';
19 18
 
20 19
 type PropsType = {
21 20
   navigation: StackNavigationProp,
22 21
   route: {params: {data: PlanningEventType, id: number, eventId: number}},
23
-  theme: CustomThemeType,
24 22
 };
25 23
 
26 24
 type StateType = {
@@ -95,7 +93,7 @@ class PlanningDisplayScreen extends React.Component<PropsType, StateType> {
95 93
    * @returns {*}
96 94
    */
97 95
   getContent(): React.Node {
98
-    const {theme} = this.props;
96
+    const {navigation} = this.props;
99 97
     const {displayData} = this;
100 98
     if (displayData == null) return null;
101 99
     let subtitle = getFormattedEventTime(
@@ -111,19 +109,16 @@ class PlanningDisplayScreen extends React.Component<PropsType, StateType> {
111 109
       <CollapsibleScrollView style={{paddingLeft: 5, paddingRight: 5}} hasTab>
112 110
         <Card.Title title={displayData.title} subtitle={subtitle} />
113 111
         {displayData.logo !== null ? (
114
-          <View style={{marginLeft: 'auto', marginRight: 'auto'}}>
115
-            <ImageModal
116
-              resizeMode="contain"
117
-              imageBackgroundColor={theme.colors.background}
118
-              style={{
119
-                width: 300,
120
-                height: 300,
121
-              }}
122
-              source={{
123
-                uri: displayData.logo,
124
-              }}
125
-            />
126
-          </View>
112
+          <ImageGalleryButton
113
+            navigation={navigation}
114
+            images={[{url: displayData.logo}]}
115
+            style={{
116
+              width: 300,
117
+              height: 300,
118
+              marginLeft: 'auto',
119
+              marginRight: 'auto',
120
+            }}
121
+          />
127 122
         ) : null}
128 123
 
129 124
         {displayData.description !== null ? (
@@ -181,4 +176,4 @@ class PlanningDisplayScreen extends React.Component<PropsType, StateType> {
181 176
   }
182 177
 }
183 178
 
184
-export default withTheme(PlanningDisplayScreen);
179
+export default PlanningDisplayScreen;

Loading…
Cancel
Save