Rocket.Chat.ReactNative/app/views/AttachmentView.tsx

196 lines
6.5 KiB
TypeScript
Raw Normal View History

import { CameraRoll } from '@react-native-camera-roll/camera-roll';
import { HeaderBackground, useHeaderHeight } from '@react-navigation/elements';
import { StackNavigationOptions } from '@react-navigation/stack';
import { ResizeMode, Video } from 'expo-av';
import { sha256 } from 'js-sha256';
import React from 'react';
import { PermissionsAndroid, View, useWindowDimensions } from 'react-native';
2019-12-18 21:13:11 +00:00
import * as mime from 'react-native-mime-types';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { shallowEqual } from 'react-redux';
import RNFetchBlob from 'rn-fetch-blob';
2019-12-18 21:13:11 +00:00
import RCActivityIndicator from '../containers/ActivityIndicator';
import * as HeaderButton from '../containers/HeaderButton';
import { ImageViewer } from '../containers/ImageViewer';
import StatusBar from '../containers/StatusBar';
import { LISTENER } from '../containers/Toast';
import { IAttachment } from '../definitions';
import I18n from '../i18n';
import { useAppSelector } from '../lib/hooks';
import { useAppNavigation, useAppRoute } from '../lib/hooks/navigation';
import { formatAttachmentUrl, isAndroid } from '../lib/methods/helpers';
import EventEmitter from '../lib/methods/helpers/events';
import { getUserSelector } from '../selectors/login';
import { TNavigation } from '../stacks/stackType';
import { useTheme } from '../theme';
const RenderContent = ({
setLoading,
attachment
}: {
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
attachment: IAttachment;
}) => {
const videoRef = React.useRef<Video>(null);
const insets = useSafeAreaInsets();
const { width, height } = useWindowDimensions();
const headerHeight = useHeaderHeight();
const navigation = useAppNavigation<TNavigation, 'AttachmentView'>();
const { baseUrl, user } = useAppSelector(
state => ({
baseUrl: state.server.server,
user: { id: getUserSelector(state).id, token: getUserSelector(state).token }
}),
shallowEqual
);
2019-12-18 21:13:11 +00:00
React.useLayoutEffect(() => {
const blurSub = navigation.addListener('blur', () => {
if (videoRef.current && videoRef.current.stopAsync) {
videoRef.current.stopAsync();
}
});
return () => {
blurSub();
};
}, [navigation]);
if (attachment.image_url) {
const url = formatAttachmentUrl(attachment.title_link || attachment.image_url, user.id, user.token, baseUrl);
const uri = encodeURI(url);
return (
<ImageViewer
uri={uri}
onLoadEnd={() => setLoading(false)}
width={width}
height={height - insets.top - insets.bottom - (headerHeight || 0)}
/>
);
2019-12-18 21:13:11 +00:00
}
if (attachment.video_url) {
const url = formatAttachmentUrl(attachment.video_url, user.id, user.token, baseUrl);
const uri = encodeURI(url);
return (
<Video
source={{ uri }}
rate={1.0}
volume={1.0}
isMuted={false}
resizeMode={ResizeMode.CONTAIN}
shouldPlay
isLooping={false}
style={{ flex: 1 }}
useNativeControls
onLoad={() => setLoading(false)}
onError={console.log}
ref={videoRef}
/>
);
}
return null;
};
const AttachmentView = (): React.ReactElement => {
const navigation = useAppNavigation<TNavigation, 'AttachmentView'>();
const {
params: { attachment }
} = useAppRoute<TNavigation, 'AttachmentView'>();
const [loading, setLoading] = React.useState(true);
const { colors } = useTheme();
const { baseUrl, user, Allow_Save_Media_to_Gallery } = useAppSelector(
state => ({
baseUrl: state.server.server,
user: { id: getUserSelector(state).id, token: getUserSelector(state).token },
Allow_Save_Media_to_Gallery: (state.settings.Allow_Save_Media_to_Gallery as boolean) ?? true
}),
shallowEqual
);
2019-12-18 21:13:11 +00:00
const setHeader = () => {
let { title } = attachment;
try {
if (title) {
title = decodeURI(title);
}
} catch {
// Do nothing
}
const options: StackNavigationOptions = {
title: title || '',
headerTitleAlign: 'center',
headerTitleStyle: { color: colors.previewTintColor },
headerTintColor: colors.previewTintColor,
headerTitleContainerStyle: { flex: 1, maxWidth: undefined },
headerLeftContainerStyle: { flexGrow: undefined, flexBasis: undefined },
headerRightContainerStyle: { flexGrow: undefined, flexBasis: undefined },
headerLeft: () => (
<HeaderButton.CloseModal testID='close-attachment-view' navigation={navigation} color={colors.previewTintColor} />
),
headerRight: () =>
Allow_Save_Media_to_Gallery ? (
<HeaderButton.Download testID='save-image' onPress={handleSave} color={colors.previewTintColor} />
) : null,
headerBackground: () => (
<HeaderBackground style={{ backgroundColor: colors.previewBackground, shadowOpacity: 0, elevation: 0 }} />
)
[CHORE] Update react-navigation to v5 (#2154) * react-navigation v5 installed * compiling * Outside working * InsideStack compiling * Switch stack * Starting room * RoomView header * SafeAreaView * Slide from right stack animation * stash * Fix params * Create channel * inapp notification * Custom status * Add server working * Refactor appStart * Attachment * in-app notification * AuthLoadingView * Remove compat * Navigation * Outside animations * Fix new server icon * block modal * AttachmentView header * Remove unnecessary code * SelectedUsersView header * StatusView * CreateDiscussionView * RoomInfoView * RoomInfoEditView style * RoomMembersView * RoomsListView header * RoomView header * Share extension * getParam * Focus/blur * Trying to fix inapp * Lint * Simpler app container * Update libs * Revert "Simpler app container" This reverts commit 1e49d80bb49481c34f415831b9da5e9d53e66057. * Load messages faster * Fix safearea on ReactionsModal * Update safe area to v3 * lint * Fix transition * stash - drawer replace working * stash - modal nav * RoomActionsView as tablet modal * RoomStack * Stop showing RoomView header when there's no room * Custom Header and different navigation based on stack * Refactor setHeader * MasterDetailContext * RoomView header * Fix isMasterDetail rule * KeyCommands kind of working * Create channel on tablet * RoomView sCU * Remove withSplit * Settings opening as modal * Settings * StatusView headerLeft * Admin panel * TwoFactor style * DirectoryView * ServerDropdown and SortDropdown animations * ThreadMessagesView * Navigate to empty RoomView on server switch when in master detail * ProfileView header * Fix navigation issues * Nav to any room info on tablet * Room info * Refactoring * Fix rooms search * Roomslist commands * SearchMessagesView close modal * Key commands * Fix undefined subscription * Disallow navigate to focused room * isFocused state on RoomsListView * Blur text inputs when focus is lost * Replace animation * Default nav theme * Refactoring * Always open Attachment with close modal button * ModalContainer backdrop following themes * Screen tracking * Refactor get active route for in-app notification * Only mark room as focused when in master detail layout * Lint * Open modals as fade from bottom on Android * typo * Fixing tests * Fix in-app update * Fixing goRoom issues * Refactor stack names * Fix unreadsCount * Fix stack * Fix header animation * Refactor ShareNavigation * Refactor navigation theme * Make sure title is set * Fix create discussion navigation * Remove unused variable * Create discussions from actions fixed * Layout animation * Screen lock on share extension * Unnecessary change * Admin border * Set header after state callback * Fix key commands on outside stack * Fix back button pressed * Remove layout animations from Android * Tweak animations on Android * Disable swipe gesture to open drawer * Fix current item on RoomsListView * Fix add server * Fix drawer * Fix broadcast * LayoutAnimation instead of Transitions * Fix onboarding back press * Fix assorted tests * Create discussion fix * RoomInfoView header * Drawer active item
2020-06-15 14:00:46 +00:00
};
navigation.setOptions(options);
};
[CHORE] Update react-navigation to v5 (#2154) * react-navigation v5 installed * compiling * Outside working * InsideStack compiling * Switch stack * Starting room * RoomView header * SafeAreaView * Slide from right stack animation * stash * Fix params * Create channel * inapp notification * Custom status * Add server working * Refactor appStart * Attachment * in-app notification * AuthLoadingView * Remove compat * Navigation * Outside animations * Fix new server icon * block modal * AttachmentView header * Remove unnecessary code * SelectedUsersView header * StatusView * CreateDiscussionView * RoomInfoView * RoomInfoEditView style * RoomMembersView * RoomsListView header * RoomView header * Share extension * getParam * Focus/blur * Trying to fix inapp * Lint * Simpler app container * Update libs * Revert "Simpler app container" This reverts commit 1e49d80bb49481c34f415831b9da5e9d53e66057. * Load messages faster * Fix safearea on ReactionsModal * Update safe area to v3 * lint * Fix transition * stash - drawer replace working * stash - modal nav * RoomActionsView as tablet modal * RoomStack * Stop showing RoomView header when there's no room * Custom Header and different navigation based on stack * Refactor setHeader * MasterDetailContext * RoomView header * Fix isMasterDetail rule * KeyCommands kind of working * Create channel on tablet * RoomView sCU * Remove withSplit * Settings opening as modal * Settings * StatusView headerLeft * Admin panel * TwoFactor style * DirectoryView * ServerDropdown and SortDropdown animations * ThreadMessagesView * Navigate to empty RoomView on server switch when in master detail * ProfileView header * Fix navigation issues * Nav to any room info on tablet * Room info * Refactoring * Fix rooms search * Roomslist commands * SearchMessagesView close modal * Key commands * Fix undefined subscription * Disallow navigate to focused room * isFocused state on RoomsListView * Blur text inputs when focus is lost * Replace animation * Default nav theme * Refactoring * Always open Attachment with close modal button * ModalContainer backdrop following themes * Screen tracking * Refactor get active route for in-app notification * Only mark room as focused when in master detail layout * Lint * Open modals as fade from bottom on Android * typo * Fixing tests * Fix in-app update * Fixing goRoom issues * Refactor stack names * Fix unreadsCount * Fix stack * Fix header animation * Refactor ShareNavigation * Refactor navigation theme * Make sure title is set * Fix create discussion navigation * Remove unused variable * Create discussions from actions fixed * Layout animation * Screen lock on share extension * Unnecessary change * Admin border * Set header after state callback * Fix key commands on outside stack * Fix back button pressed * Remove layout animations from Android * Tweak animations on Android * Disable swipe gesture to open drawer * Fix current item on RoomsListView * Fix add server * Fix drawer * Fix broadcast * LayoutAnimation instead of Transitions * Fix onboarding back press * Fix assorted tests * Create discussion fix * RoomInfoView header * Drawer active item
2020-06-15 14:00:46 +00:00
React.useLayoutEffect(() => {
setHeader();
}, [navigation]);
const handleSave = async () => {
const { title_link, image_url, image_type, video_url, video_type } = attachment;
const url = title_link || image_url || video_url;
2019-12-18 21:13:11 +00:00
if (!url) {
return;
}
const mediaAttachment = formatAttachmentUrl(url, user.id, user.token, baseUrl);
2019-12-18 21:13:11 +00:00
if (isAndroid) {
const rationale = {
title: I18n.t('Write_External_Permission'),
message: I18n.t('Write_External_Permission_Message'),
buttonPositive: 'Ok'
2019-12-18 21:13:11 +00:00
};
const result = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, rationale);
if (!(result || result === PermissionsAndroid.RESULTS.GRANTED)) {
return;
}
}
setLoading(true);
2019-12-18 21:13:11 +00:00
try {
const extension = image_url
? `.${mime.extension(image_type) || 'jpg'}`
: `.${(video_type === 'video/quicktime' && 'mov') || mime.extension(video_type) || 'mp4'}`;
// The return of mime.extension('video/quicktime') is .qt,
// this format the iOS isn't recognize and can't save on gallery
const documentDir = `${RNFetchBlob.fs.dirs.DocumentDir}/`;
const path = `${documentDir + sha256(url) + extension}`;
const file = await RNFetchBlob.config({ path }).fetch('GET', mediaAttachment);
await CameraRoll.save(path, { album: 'Rocket.Chat' });
file.flush();
2019-12-18 21:13:11 +00:00
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') });
2019-12-18 21:13:11 +00:00
}
setLoading(false);
2019-12-18 21:13:11 +00:00
};
return (
<View style={{ backgroundColor: colors.backgroundColor, flex: 1 }}>
<StatusBar barStyle='light-content' backgroundColor={colors.previewBackground} />
<RenderContent attachment={attachment} setLoading={setLoading} />
{loading ? <RCActivityIndicator absolute size='large' /> : null}
</View>
2019-12-18 21:13:11 +00:00
);
};
2019-12-18 21:13:11 +00:00
export default AttachmentView;