Replace image modal by image gallery screen
This improves performance and allows multi-images view
This commit is contained in:
parent
6e7d891d5c
commit
6ec87821e8
9 changed files with 258 additions and 58 deletions
42
package-lock.json
generated
42
package-lock.json
generated
|
@ -10146,6 +10146,15 @@
|
|||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"react-mixin": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-mixin/-/react-mixin-3.1.1.tgz",
|
||||
"integrity": "sha512-z9fZ0aCRDjlgxLdMeWkJ9TwhmVLhQ09r8RFpin/cEPA2T6jsb7YHNWcIe0Oii+hhJNyMymdy91CSya5mRkuCkg==",
|
||||
"requires": {
|
||||
"object-assign": "^4.0.1",
|
||||
"smart-mixin": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"react-native": {
|
||||
"version": "0.63.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.63.2.tgz",
|
||||
|
@ -10383,11 +10392,34 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"react-native-image-gallery": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-image-gallery/-/react-native-image-gallery-2.1.5.tgz",
|
||||
"integrity": "sha512-xC7nuPu4GUH0da6byofQ10LjtqlKj+VaLc0NHBJmeMHVvdvmRvFEO6UOq0Q0m/ePx3OQiPTNwGbf5BSPJEKa0w==",
|
||||
"requires": {
|
||||
"prop-types": "^15.6.0",
|
||||
"react-mixin": "^3.0.5",
|
||||
"react-timer-mixin": "^0.13.3"
|
||||
}
|
||||
},
|
||||
"react-native-image-modal": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/react-native-image-modal/-/react-native-image-modal-1.0.9.tgz",
|
||||
"integrity": "sha512-d01lx82CgC2s8zzpmKAbSAK/rABFl8BhKHSrp6GLqColNuUHaNuDIuu67TdqzHsY9pT4OHY0rduWyvrmRiSrLw=="
|
||||
},
|
||||
"react-native-image-pan-zoom": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/react-native-image-pan-zoom/-/react-native-image-pan-zoom-2.1.12.tgz",
|
||||
"integrity": "sha512-BF66XeP6dzuANsPmmFsJshM2Jyh/Mo1t8FsGc1L9Q9/sVP8MJULDabB1hms+eAoqgtyhMr5BuXV3E1hJ5U5H6Q=="
|
||||
},
|
||||
"react-native-image-zoom-viewer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-image-zoom-viewer/-/react-native-image-zoom-viewer-3.0.1.tgz",
|
||||
"integrity": "sha512-la6s5DNSuq4GCRLsi5CZ29FPjgTpdCuGIRdO5T9rUrAtxrlpBPhhSnHrbmPVxsdtOUvxHacTh2Gfa9+RraMZQA==",
|
||||
"requires": {
|
||||
"react-native-image-pan-zoom": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"react-native-iphone-x-helper": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.2.1.tgz",
|
||||
|
@ -10632,6 +10664,11 @@
|
|||
"scheduler": "^0.19.1"
|
||||
}
|
||||
},
|
||||
"react-timer-mixin": {
|
||||
"version": "0.13.4",
|
||||
"resolved": "https://registry.npmjs.org/react-timer-mixin/-/react-timer-mixin-0.13.4.tgz",
|
||||
"integrity": "sha512-4+ow23tp/Tv7hBM5Az5/Be/eKKF7DIvJ09voz5LyHGQaqqz9WV8YMs31eFvcYQs7d451LSg7kDJV70XYN/Ug/Q=="
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
|
||||
|
@ -11227,6 +11264,11 @@
|
|||
"resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
|
||||
"integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
|
||||
},
|
||||
"smart-mixin": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/smart-mixin/-/smart-mixin-2.0.0.tgz",
|
||||
"integrity": "sha1-o0oQVeMqdbMNK048oyPcmctT9Dc="
|
||||
},
|
||||
"snapdragon": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
"react-native-camera": "^3.35.0",
|
||||
"react-native-collapsible": "^1.5.3",
|
||||
"react-native-gesture-handler": "^1.7.0",
|
||||
"react-native-image-modal": "^1.0.9",
|
||||
"react-native-image-zoom-viewer": "^3.0.1",
|
||||
"react-native-keychain": "^6.1.1",
|
||||
"react-native-linear-gradient": "^2.5.6",
|
||||
"react-native-localize": "^1.4.1",
|
||||
|
|
|
@ -5,11 +5,11 @@ import {Button, Card, Text, TouchableRipple} from 'react-native-paper';
|
|||
import {Image, View} from 'react-native';
|
||||
import Autolink from 'react-native-autolink';
|
||||
import i18n from 'i18n-js';
|
||||
import ImageModal from 'react-native-image-modal';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import type {FeedItemType} from '../../screens/Home/HomeScreen';
|
||||
import NewsSourcesConstants from '../../constants/NewsSourcesConstants';
|
||||
import type {NewsSourceType} from '../../constants/NewsSourcesConstants';
|
||||
import ImageGalleryButton from '../Media/ImageGalleryButton';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp,
|
||||
|
@ -82,16 +82,13 @@ class FeedItem extends React.Component<PropsType> {
|
|||
/>
|
||||
{hasImage ? (
|
||||
<View style={{marginLeft: 'auto', marginRight: 'auto'}}>
|
||||
<ImageModal
|
||||
resizeMode="contain"
|
||||
imageBackgroundColor="#000"
|
||||
<ImageGalleryButton
|
||||
navigation={props.navigation}
|
||||
images={[{url: item.image}]}
|
||||
style={{
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
}}
|
||||
source={{
|
||||
uri: item.image,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
|
|
40
src/components/Media/ImageGalleryButton.js
Normal file
40
src/components/Media/ImageGalleryButton.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {TouchableRipple, withTheme} from 'react-native-paper';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import {Image} from 'react-native-animatable';
|
||||
import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
|
||||
import type {CustomThemeType} from '../../managers/ThemeManager';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp,
|
||||
images: Array<{url: string}>,
|
||||
theme: CustomThemeType,
|
||||
style: ViewStyleProp,
|
||||
};
|
||||
|
||||
class ImageGalleryButton extends React.Component<PropsType> {
|
||||
onPress = () => {
|
||||
const {navigation, images} = this.props;
|
||||
navigation.navigate('gallery', {images});
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const {style, images} = this.props;
|
||||
return (
|
||||
<TouchableRipple onPress={this.onPress} style={style}>
|
||||
<Image
|
||||
resizeMode="contain"
|
||||
source={{uri: images[0].url}}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
/>
|
||||
</TouchableRipple>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(ImageGalleryButton);
|
|
@ -31,6 +31,7 @@ import EquipmentLendScreen from '../screens/Amicale/Equipment/EquipmentRentScree
|
|||
import EquipmentConfirmScreen from '../screens/Amicale/Equipment/EquipmentConfirmScreen';
|
||||
import DashboardEditScreen from '../screens/Other/Settings/DashboardEditScreen';
|
||||
import GameStartScreen from '../screens/Game/screens/GameStartScreen';
|
||||
import ImageGalleryScreen from '../screens/Media/ImageGalleryScreen';
|
||||
|
||||
const modalTransition =
|
||||
Platform.OS === 'ios'
|
||||
|
@ -62,6 +63,14 @@ function MainStackComponent(props: {
|
|||
title: i18n.t('screens.home.title'),
|
||||
}}
|
||||
/>
|
||||
<MainStack.Screen
|
||||
name="gallery"
|
||||
component={ImageGalleryScreen}
|
||||
options={{
|
||||
headerShown: false,
|
||||
...modalTransition,
|
||||
}}
|
||||
/>
|
||||
{createScreenCollapsibleStack(
|
||||
'settings',
|
||||
MainStack,
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
Paragraph,
|
||||
withTheme,
|
||||
} from 'react-native-paper';
|
||||
import ImageModal from 'react-native-image-modal';
|
||||
import i18n from 'i18n-js';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen';
|
||||
|
@ -22,6 +21,7 @@ import {ERROR_TYPE} from '../../../utils/WebData';
|
|||
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
||||
import type {ApiGenericDataType} from '../../../utils/WebData';
|
||||
import type {CardTitleIconPropsType} from '../../../constants/PaperStyles';
|
||||
import ImageGalleryButton from '../../../components/Media/ImageGalleryButton';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp,
|
||||
|
@ -188,7 +188,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
|||
}
|
||||
|
||||
getScreen = (response: Array<ApiGenericDataType | null>): React.Node => {
|
||||
const {props} = this;
|
||||
const {navigation} = this.props;
|
||||
let data: ClubType | null = null;
|
||||
if (response[0] != null) {
|
||||
[data] = response;
|
||||
|
@ -199,25 +199,18 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
|||
<CollapsibleScrollView style={{paddingLeft: 5, paddingRight: 5}} hasTab>
|
||||
{this.getCategoriesRender(data.category)}
|
||||
{data.logo !== null ? (
|
||||
<View
|
||||
<ImageGalleryButton
|
||||
navigation={navigation}
|
||||
images={[{url: data.logo}]}
|
||||
style={{
|
||||
width: 300,
|
||||
height: 300,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
}}>
|
||||
<ImageModal
|
||||
resizeMode="contain"
|
||||
imageBackgroundColor={props.theme.colors.background}
|
||||
style={{
|
||||
width: 300,
|
||||
height: 300,
|
||||
}}
|
||||
source={{
|
||||
uri: data.logo,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<View />
|
||||
)}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Linking, View} from 'react-native';
|
||||
import {Linking} from 'react-native';
|
||||
import {Avatar, Card, Text, withTheme} from 'react-native-paper';
|
||||
import ImageModal from 'react-native-image-modal';
|
||||
import Autolink from 'react-native-autolink';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import MaterialHeaderButtons, {
|
||||
|
@ -12,6 +11,7 @@ import MaterialHeaderButtons, {
|
|||
import CustomTabBar from '../../components/Tabbar/CustomTabBar';
|
||||
import type {FeedItemType} from './HomeScreen';
|
||||
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
|
||||
import ImageGalleryButton from '../../components/Media/ImageGalleryButton';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp,
|
||||
|
@ -69,6 +69,7 @@ class FeedItemScreen extends React.Component<PropsType> {
|
|||
};
|
||||
|
||||
render(): React.Node {
|
||||
const {navigation} = this.props;
|
||||
const hasImage =
|
||||
this.displayData.image !== '' && this.displayData.image != null;
|
||||
return (
|
||||
|
@ -85,19 +86,16 @@ class FeedItemScreen extends React.Component<PropsType> {
|
|||
)}
|
||||
/>
|
||||
{hasImage ? (
|
||||
<View style={{marginLeft: 'auto', marginRight: 'auto'}}>
|
||||
<ImageModal
|
||||
resizeMode="contain"
|
||||
imageBackgroundColor="#000"
|
||||
style={{
|
||||
width: 250,
|
||||
height: 250,
|
||||
}}
|
||||
source={{
|
||||
uri: this.displayData.image,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<ImageGalleryButton
|
||||
navigation={navigation}
|
||||
images={[{url: this.displayData.image}]}
|
||||
style={{
|
||||
width: 250,
|
||||
height: 250,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<Card.Content style={{paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
|
||||
{this.displayData.message !== undefined ? (
|
||||
|
|
126
src/screens/Media/ImageGalleryScreen.js
Normal file
126
src/screens/Media/ImageGalleryScreen.js
Normal file
|
@ -0,0 +1,126 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {IconButton, Text} from 'react-native-paper';
|
||||
import ImageViewer from 'react-native-image-zoom-viewer';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp,
|
||||
route: {params: {images: Array<{url: string}>}},
|
||||
};
|
||||
|
||||
class ImageGalleryScreen extends React.Component<PropsType> {
|
||||
images: Array<{url: string}>;
|
||||
|
||||
closeButtonRef: {current: null | Animatable.View};
|
||||
|
||||
indicatorRef: {current: null | Animatable.View};
|
||||
|
||||
controlsShown: boolean;
|
||||
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
this.closeButtonRef = React.createRef();
|
||||
this.indicatorRef = React.createRef();
|
||||
this.controlsShown = true;
|
||||
if (props.route.params != null) this.images = props.route.params.images;
|
||||
}
|
||||
|
||||
goBack = () => {
|
||||
const {navigation} = this.props;
|
||||
navigation.goBack();
|
||||
};
|
||||
|
||||
getRenderHeader = (): React.Node => {
|
||||
return (
|
||||
<Animatable.View
|
||||
ref={this.closeButtonRef}
|
||||
useNativeDriver
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
left: 10,
|
||||
zIndex: 1000,
|
||||
}}>
|
||||
<IconButton
|
||||
onPress={this.goBack}
|
||||
icon="close"
|
||||
size={30}
|
||||
style={{backgroundColor: 'rgba(0,0,0,0.6)'}}
|
||||
/>
|
||||
</Animatable.View>
|
||||
);
|
||||
};
|
||||
|
||||
getRenderIndicator = (
|
||||
currentIndex?: number,
|
||||
allSize?: number,
|
||||
): React.Node => {
|
||||
if (currentIndex != null && allSize != null && allSize !== 1)
|
||||
return (
|
||||
<Animatable.View
|
||||
ref={this.indicatorRef}
|
||||
useNativeDriver
|
||||
style={{
|
||||
width: '100%',
|
||||
height: 50,
|
||||
position: 'absolute',
|
||||
}}>
|
||||
<Text
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
fontSize: 15,
|
||||
backgroundColor: 'rgba(0,0,0,0.6)',
|
||||
padding: 10,
|
||||
borderRadius: 20,
|
||||
}}>
|
||||
{currentIndex}/{allSize}
|
||||
</Text>
|
||||
</Animatable.View>
|
||||
);
|
||||
return null;
|
||||
};
|
||||
|
||||
onImageClick = () => {
|
||||
if (this.controlsShown) this.hideControls();
|
||||
else this.showControls();
|
||||
this.controlsShown = !this.controlsShown;
|
||||
};
|
||||
|
||||
hideControls() {
|
||||
if (this.closeButtonRef.current != null)
|
||||
this.closeButtonRef.current.fadeOutUp(200);
|
||||
if (this.indicatorRef.current != null)
|
||||
this.indicatorRef.current.fadeOutUp(300);
|
||||
}
|
||||
|
||||
showControls() {
|
||||
if (this.closeButtonRef.current != null)
|
||||
this.closeButtonRef.current.fadeInDown(300);
|
||||
if (this.indicatorRef.current != null)
|
||||
this.indicatorRef.current.fadeInDown(400);
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
return (
|
||||
<ImageViewer
|
||||
useNativeDriver
|
||||
imageUrls={this.images}
|
||||
enableSwipeDown
|
||||
onSwipeDown={this.goBack}
|
||||
renderHeader={this.getRenderHeader}
|
||||
renderIndicator={this.getRenderIndicator}
|
||||
pageAnimateTime={100}
|
||||
onClick={this.onImageClick}
|
||||
doubleClickInterval={250}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ImageGalleryScreen;
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {Card, withTheme} from 'react-native-paper';
|
||||
import ImageModal from 'react-native-image-modal';
|
||||
import {Card} from 'react-native-paper';
|
||||
import i18n from 'i18n-js';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import {getDateOnlyString, getFormattedEventTime} from '../../utils/Planning';
|
||||
|
@ -13,14 +12,13 @@ import {apiRequest, ERROR_TYPE} from '../../utils/WebData';
|
|||
import ErrorView from '../../components/Screens/ErrorView';
|
||||
import CustomHTML from '../../components/Overrides/CustomHTML';
|
||||
import CustomTabBar from '../../components/Tabbar/CustomTabBar';
|
||||
import type {CustomThemeType} from '../../managers/ThemeManager';
|
||||
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
|
||||
import type {PlanningEventType} from '../../utils/Planning';
|
||||
import ImageGalleryButton from '../../components/Media/ImageGalleryButton';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp,
|
||||
route: {params: {data: PlanningEventType, id: number, eventId: number}},
|
||||
theme: CustomThemeType,
|
||||
};
|
||||
|
||||
type StateType = {
|
||||
|
@ -95,7 +93,7 @@ class PlanningDisplayScreen extends React.Component<PropsType, StateType> {
|
|||
* @returns {*}
|
||||
*/
|
||||
getContent(): React.Node {
|
||||
const {theme} = this.props;
|
||||
const {navigation} = this.props;
|
||||
const {displayData} = this;
|
||||
if (displayData == null) return null;
|
||||
let subtitle = getFormattedEventTime(
|
||||
|
@ -111,19 +109,16 @@ class PlanningDisplayScreen extends React.Component<PropsType, StateType> {
|
|||
<CollapsibleScrollView style={{paddingLeft: 5, paddingRight: 5}} hasTab>
|
||||
<Card.Title title={displayData.title} subtitle={subtitle} />
|
||||
{displayData.logo !== null ? (
|
||||
<View style={{marginLeft: 'auto', marginRight: 'auto'}}>
|
||||
<ImageModal
|
||||
resizeMode="contain"
|
||||
imageBackgroundColor={theme.colors.background}
|
||||
style={{
|
||||
width: 300,
|
||||
height: 300,
|
||||
}}
|
||||
source={{
|
||||
uri: displayData.logo,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<ImageGalleryButton
|
||||
navigation={navigation}
|
||||
images={[{url: displayData.logo}]}
|
||||
style={{
|
||||
width: 300,
|
||||
height: 300,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{displayData.description !== null ? (
|
||||
|
@ -181,4 +176,4 @@ class PlanningDisplayScreen extends React.Component<PropsType, StateType> {
|
|||
}
|
||||
}
|
||||
|
||||
export default withTheme(PlanningDisplayScreen);
|
||||
export default PlanningDisplayScreen;
|
||||
|
|
Loading…
Reference in a new issue