Skip to content

Commit

Permalink
Merge pull request #2864 from ecency/nt/gallery-mode
Browse files Browse the repository at this point in the history
Nt/gallery mode
  • Loading branch information
feruzm committed Apr 24, 2024
2 parents b06aa86 + 5e966bb commit 14e9372
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 245 deletions.
23 changes: 23 additions & 0 deletions src/components/imageViewer/imageViewer.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import EStyleSheet from 'react-native-extended-stylesheet';

export default EStyleSheet.create({
imageViewerHeaderContainer: {
alignItems: 'center',
paddingVertical: 10,
},
leftContainer: {
position: 'absolute',
flexDirection:'row',
alignItems:'center',
left: 8,
},
rightContainer: {
position: 'absolute',
flexDirection:'row',
right: 8,
},
imageGalleryHeaderText: {
color: '$iconColor',
fontSize: 16
},
});
165 changes: 165 additions & 0 deletions src/components/imageViewer/imageViewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React, { forwardRef, useImperativeHandle, useState } from 'react';
import { View, Text, Platform, SafeAreaView, Share, Alert } from 'react-native';
import styles from './imageViewer.styles';
import EStyleSheet from 'react-native-extended-stylesheet';
import ImageViewing from 'react-native-image-viewing';
import { useIntl } from 'react-intl';
import IconButton from '../iconButton';
import { useDispatch } from 'react-redux';
import { PermissionsAndroid } from 'react-native';
import { CameraRoll } from '@react-native-camera-roll/camera-roll';
import { Image as ExpoImage } from 'expo-image';
import RNFetchBlob from 'rn-fetch-blob';


export const ImageViewer = forwardRef(({ }, ref) => {
const intl = useIntl();
const dispatch = useDispatch();
// intl.formatMessage({ id: 'post.copy_link' }),
// intl.formatMessage({ id: 'post.gallery_mode' }),
// intl.formatMessage({ id: 'post.save_to_local' }),

const [visible, setVisible] = useState(false);
const [imageUrls, setImageUrls] = useState<string[]>([]);
const [selectedIndex, setSelectedIndex] = useState(0);


useImperativeHandle(ref, () => ({
show(selectedUrl: string, _imageUrls: string[]) {
setImageUrls(_imageUrls)
setSelectedIndex(_imageUrls.indexOf(selectedUrl))
setVisible(true);

if (Platform.OS === 'ios') {
ExpoImage.prefetch(_imageUrls, "memory");
}

},
}));

const checkAndroidPermission = async () => {
try {
const permission = PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE;
await PermissionsAndroid.request(permission);
Promise.resolve();
} catch (error) {
Promise.reject(error);
}
};


const _downloadImage = async (uri: string) => {
return RNFetchBlob.config({
fileCache: true,
appendExt: 'jpg',
})
.fetch('GET', uri)
.then((res) => {
const { status } = res.info();

if (status == 200) {
return res.path();
} else {
Promise.reject();
}
})
.catch((errorMessage) => {
Promise.reject(errorMessage);
});
};


const _onSavePress = async (index: number) => {

try {
if (Platform.OS === 'android') {
await checkAndroidPermission();
}

const url = imageUrls[index];

const imagePath = Platform.select({
ios: await ExpoImage.getCachePathAsync(url),
android: await _downloadImage(url)
})

if (!imagePath) {
return;
}

const uri = `file://${imagePath}`
await CameraRoll.saveAsset(uri, { album: 'Ecency' })

Alert.alert(intl.formatMessage({ id: "post.image_saved" }));


} catch (err) {
console.warn("fail to save image", err.message)
}
};



const _onCopyPress = (index: number) => {
const url = imageUrls[index]
Share.share(Platform.OS === 'ios' ?
{ url } : { message: url })
}


const _renderIconButton = (iconName: string, onPress: () => void) => (
<IconButton
name={iconName}
iconType="MaterialCommunityIcons"
color={EStyleSheet.value('$iconColor')}
style={{ marginHorizontal: 4 }}
size={24}
onPress={onPress}
/>
)


const _renderImageViewerHeader = (imageIndex: number) => {
return (
<SafeAreaView
style={{
marginTop: Platform.select({ ios: 0, android: 25 }),
}}>
<View style={styles.imageViewerHeaderContainer}>
<View style={styles.leftContainer}>
{_renderIconButton('close', _onCloseImageViewer)}
<Text style={styles.imageGalleryHeaderText}>{
`Preview (${imageIndex + 1}/${imageUrls.length})`}</Text>
</View>

<View style={styles.rightContainer}>
{_renderIconButton('content-copy', () => _onCopyPress(imageIndex))}
{_renderIconButton('download', () => _onSavePress(imageIndex))}
</View>

</View>
</SafeAreaView>
);
};


const _onCloseImageViewer = () => {
setVisible(false);
setImageUrls([])
}


return (
<ImageViewing
images={imageUrls.map((url) => ({ uri: url }))}
imageIndex={selectedIndex}
visible={visible}
animationType="slide"
swipeToCloseEnabled
onRequestClose={_onCloseImageViewer}
HeaderComponent={(data) => _renderImageViewerHeader(data.imageIndex)}
/>
);
});


1 change: 1 addition & 0 deletions src/components/imageViewer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './imageViewer';
2 changes: 2 additions & 0 deletions src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ import TextBoxWithCopy from './textBoxWithCopy/textBoxWithCopy';
import WebViewModal from './webViewModal/webViewModal';
import OrDivider from './orDivider/orDividerView';
import PostTranslationModal from './post-translation-modal/postTranslationModal';
import { ImageViewer } from './imageViewer';

// Basic UI Elements
import {
Expand Down Expand Up @@ -257,4 +258,5 @@ export {
WebViewModal,
OrDivider,
PostTranslationModal,
ImageViewer
};
98 changes: 9 additions & 89 deletions src/components/postElements/body/view/postBodyView.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React, { Fragment, useState, useEffect, useRef } from 'react';
import { SafeAreaView, PermissionsAndroid, Platform, View, Text } from 'react-native';
import { PermissionsAndroid, Platform, View } from 'react-native';
import {CameraRoll} from '@react-native-camera-roll/camera-roll';
import { useIntl } from 'react-intl';
import EStyleSheet from 'react-native-extended-stylesheet';
import ImageView from 'react-native-image-viewing';
import RNFetchBlob from 'rn-fetch-blob';
import ActionSheetView from 'react-native-actions-sheet';

Expand All @@ -21,32 +20,26 @@ import { default as ROUTES } from '../../../../constants/routeNames';
import { OptionsModal } from '../../../atoms';
import { isCommunity } from '../../../../utils/communityValidation';
import { GLOBAL_POST_FILTERS_VALUE } from '../../../../constants/options/filters';
import { PostHtmlRenderer, VideoPlayer } from '../../..';
import { ImageViewer, PostHtmlRenderer, VideoPlayer } from '../../..';
import getWindowDimensions from '../../../../utils/getWindowDimensions';
import { useAppDispatch } from '../../../../hooks';
import { IconButton } from '../../../buttons';
import styles from './postBodyStyles';
import { isHiveUri } from '../../../../utils/hive-uri';

const WIDTH = getWindowDimensions().width;

const PostBody = ({ body, metadata, onLoadEnd, width }) => {
const intl = useIntl();
const navigation = useNavigation();
const dispatch = useAppDispatch();

const [isImageModalOpen, setIsImageModalOpen] = useState(false);

const [postImages, setPostImages] = useState([]);
const [selectedImage, setSelectedImage] = useState(null);
const [selectedLink, setSelectedLink] = useState(null);
const [html, setHtml] = useState('');
const [youtubeVideoId, setYoutubeVideoId] = useState(null);
const [videoUrl, setVideoUrl] = useState(null);
const [videoStartTime, setVideoStartTime] = useState(0);

const intl = useIntl();
const actionImage = useRef(null);
const actionLink = useRef(null);
const imageViewerRef = useRef(null);
const youtubePlayerRef = useRef(null);

useEffect(() => {
Expand All @@ -71,31 +64,6 @@ const PostBody = ({ body, metadata, onLoadEnd, width }) => {
}
};

const handleImagePress = (ind) => {
if (ind === 1) {
// open gallery mode
setIsImageModalOpen(true);
return;
}
if (ind === 0) {
// copy to clipboard
writeToClipboard(selectedImage).then(() => {
dispatch(
toastNotification(
intl.formatMessage({
id: 'alert.copied',
}),
),
);
});
}
// if (ind === 2) {
// // save to local
// _saveImage(selectedImage);
// }

setSelectedImage(null);
};

const handleLinkPress = (ind) => {
if (ind === 1) {
Expand Down Expand Up @@ -263,51 +231,17 @@ const PostBody = ({ body, metadata, onLoadEnd, width }) => {
};

const _handleSetSelectedImage = (imageLink, postImgUrls) => {
if (postImages.length !== postImgUrls.length) {
setPostImages(postImgUrls);
if(imageViewerRef.current){
imageViewerRef.current.show(imageLink, postImgUrls);
}
setSelectedImage(imageLink);
actionImage.current.show();
};

const _onCloseImageViewer = () => {
setIsImageModalOpen(false);
setSelectedImage(null);
};

const _renderImageViewerHeader = (imageIndex) => {
return (
<SafeAreaView
style={{
marginTop: Platform.select({ ios: 0, android: 25 }),
}}
>
<View style={styles.imageViewerHeaderContainer}>
<Text style={styles.imageGalleryHeaderText}>{`${imageIndex + 1}/${
postImages.length
}`}</Text>
<IconButton
name="close"
color={EStyleSheet.value('$primaryDarkText')}
buttonStyle={styles.closeIconButton}
size={20}
handleOnPress={_onCloseImageViewer}
/>
</View>
</SafeAreaView>
);
};

return (
<Fragment>
<ImageView
images={postImages.map((url) => ({ uri: url }))}
imageIndex={postImages.indexOf(selectedImage)}
visible={isImageModalOpen}
animationType="slide"
swipeToCloseEnabled
onRequestClose={_onCloseImageViewer}
HeaderComponent={(imageIndex) => _renderImageViewerHeader(imageIndex.imageIndex)}

<ImageViewer
ref={imageViewerRef}
/>

<ActionSheetView
Expand All @@ -329,20 +263,6 @@ const PostBody = ({ body, metadata, onLoadEnd, width }) => {
/>
</ActionSheetView>

<OptionsModal
ref={actionImage}
options={[
intl.formatMessage({ id: 'post.copy_link' }),
intl.formatMessage({ id: 'post.gallery_mode' }),
// intl.formatMessage({ id: 'post.save_to_local' }),
intl.formatMessage({ id: 'alert.cancel' }),
]}
title={intl.formatMessage({ id: 'post.image' })}
cancelButtonIndex={2}
onPress={(index) => {
handleImagePress(index);
}}
/>
<OptionsModal
ref={actionLink}
options={[
Expand Down
Loading

0 comments on commit 14e9372

Please sign in to comment.