import React from 'react'; import { StyleSheet, View, PermissionsAndroid } from 'react-native'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import CameraRoll from '@react-native-community/cameraroll'; import * as mime from 'react-native-mime-types'; import RNFetchBlob from 'rn-fetch-blob'; import { Video } from 'expo-av'; import SHA256 from 'js-sha256'; import { withSafeAreaInsets } from 'react-native-safe-area-context'; import { LISTENER } from '../containers/Toast'; import EventEmitter from '../utils/events'; import I18n from '../i18n'; import { withTheme } from '../theme'; import { ImageViewer } from '../presentation/ImageViewer'; import { themes } from '../constants/colors'; import { formatAttachmentUrl } from '../lib/utils'; import RCActivityIndicator from '../containers/ActivityIndicator'; import * as HeaderButton from '../containers/HeaderButton'; import { isAndroid } from '../utils/deviceInfo'; import { getUserSelector } from '../selectors/login'; import { withDimensions } from '../dimensions'; import { getHeaderHeight } from '../containers/Header'; import StatusBar from '../containers/StatusBar'; const styles = StyleSheet.create({ container: { flex: 1 } }); class AttachmentView extends React.Component { static propTypes = { navigation: PropTypes.object, route: PropTypes.object, theme: PropTypes.string, baseUrl: PropTypes.string, width: PropTypes.number, height: PropTypes.number, insets: PropTypes.object, user: PropTypes.shape({ id: PropTypes.string, token: PropTypes.string }), Allow_Save_Media_to_Gallery: PropTypes.bool } constructor(props) { super(props); const attachment = props.route.params?.attachment; this.state = { attachment, loading: true }; this.setHeader(); } componentDidMount() { const { navigation } = this.props; this.unsubscribeBlur = navigation.addListener('blur', () => { if (this.videoRef && this.videoRef.stopAsync) { this.videoRef.stopAsync(); } }); } componentWillUnmount() { if (this.unsubscribeBlur) { this.unsubscribeBlur(); } } setHeader = () => { const { route, navigation, theme, Allow_Save_Media_to_Gallery } = this.props; const attachment = route.params?.attachment; let { title } = attachment; try { title = decodeURI(title); } catch { // Do nothing } const options = { title, headerLeft: () => , headerRight: () => ( Allow_Save_Media_to_Gallery ? : null ), headerBackground: () => , headerTintColor: themes[theme].previewTintColor, headerTitleStyle: { color: themes[theme].previewTintColor, marginHorizontal: 10 } }; navigation.setOptions(options); } getVideoRef = ref => this.videoRef = ref; handleSave = async() => { const { attachment } = this.state; const { user, baseUrl } = this.props; const { image_url, image_type, video_url, video_type } = attachment; const url = image_url || video_url; const mediaAttachment = formatAttachmentUrl(url, user.id, user.token, baseUrl); if (isAndroid) { const rationale = { title: I18n.t('Write_External_Permission'), message: I18n.t('Write_External_Permission_Message') }; const result = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, rationale); if (!(result || result === PermissionsAndroid.RESULTS.GRANTED)) { return; } } this.setState({ loading: true }); try { const extension = image_url ? `.${ mime.extension(image_type) || 'jpg' }` : `.${ mime.extension(video_type) || 'mp4' }`; const path = `${ RNFetchBlob.fs.dirs.DocumentDir + SHA256(url) + extension }`; const file = await RNFetchBlob.config({ path }).fetch('GET', mediaAttachment).then(res => res.path()); await CameraRoll.save(file, { album: 'Rocket.Chat' }); EventEmitter.emit(LISTENER, { message: I18n.t('saved_to_gallery') }); } catch (e) { EventEmitter.emit(LISTENER, { message: I18n.t(image_url ? 'error-save-image' : 'error-save-video') }); } this.setState({ loading: false }); }; renderImage = (uri) => { const { theme, width, height, insets } = this.props; const headerHeight = getHeaderHeight(width > height); return ( this.setState({ loading: false })} theme={theme} width={width} height={height - insets.top - insets.bottom - headerHeight} /> ); } renderVideo = uri => (