diff --git a/package-lock.json b/package-lock.json index ba24ea9..a7385be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index c18877a..03f5d98 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/Home/FeedItem.js b/src/components/Home/FeedItem.js index 0db7f7f..c2920ce 100644 --- a/src/components/Home/FeedItem.js +++ b/src/components/Home/FeedItem.js @@ -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 { /> {hasImage ? ( - ) : null} diff --git a/src/components/Media/ImageGalleryButton.js b/src/components/Media/ImageGalleryButton.js new file mode 100644 index 0000000..4fc4392 --- /dev/null +++ b/src/components/Media/ImageGalleryButton.js @@ -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 { + onPress = () => { + const {navigation, images} = this.props; + navigation.navigate('gallery', {images}); + }; + + render(): React.Node { + const {style, images} = this.props; + return ( + + + + ); + } +} + +export default withTheme(ImageGalleryButton); diff --git a/src/navigation/MainNavigator.js b/src/navigation/MainNavigator.js index 800ae2f..d06dbf1 100644 --- a/src/navigation/MainNavigator.js +++ b/src/navigation/MainNavigator.js @@ -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'), }} /> + {createScreenCollapsibleStack( 'settings', MainStack, diff --git a/src/screens/Amicale/Clubs/ClubDisplayScreen.js b/src/screens/Amicale/Clubs/ClubDisplayScreen.js index 29559a6..c481622 100644 --- a/src/screens/Amicale/Clubs/ClubDisplayScreen.js +++ b/src/screens/Amicale/Clubs/ClubDisplayScreen.js @@ -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 { } getScreen = (response: Array): 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 { {this.getCategoriesRender(data.category)} {data.logo !== null ? ( - - - + }} + /> ) : ( )} diff --git a/src/screens/Home/FeedItemScreen.js b/src/screens/Home/FeedItemScreen.js index f531e07..112d372 100644 --- a/src/screens/Home/FeedItemScreen.js +++ b/src/screens/Home/FeedItemScreen.js @@ -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 { }; 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 { )} /> {hasImage ? ( - - - + ) : null} {this.displayData.message !== undefined ? ( diff --git a/src/screens/Media/ImageGalleryScreen.js b/src/screens/Media/ImageGalleryScreen.js new file mode 100644 index 0000000..eaed249 --- /dev/null +++ b/src/screens/Media/ImageGalleryScreen.js @@ -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 { + 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 ( + + + + ); + }; + + getRenderIndicator = ( + currentIndex?: number, + allSize?: number, + ): React.Node => { + if (currentIndex != null && allSize != null && allSize !== 1) + return ( + + + {currentIndex}/{allSize} + + + ); + 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 ( + + ); + } +} + +export default ImageGalleryScreen; diff --git a/src/screens/Planning/PlanningDisplayScreen.js b/src/screens/Planning/PlanningDisplayScreen.js index d72322f..c8765af 100644 --- a/src/screens/Planning/PlanningDisplayScreen.js +++ b/src/screens/Planning/PlanningDisplayScreen.js @@ -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 { * @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 { {displayData.logo !== null ? ( - - - + ) : null} {displayData.description !== null ? ( @@ -181,4 +176,4 @@ class PlanningDisplayScreen extends React.Component { } } -export default withTheme(PlanningDisplayScreen); +export default PlanningDisplayScreen;