thumbnail video and video behavior

This commit is contained in:
Reinaldo Neto 2023-07-04 00:35:31 -03:00
parent b88603173f
commit d39a5c7da2
4 changed files with 80 additions and 42 deletions

View File

@ -10,23 +10,27 @@ import { CustomIcon, TIconsName } from '../../../CustomIcon';
const BlurComponent = ({ const BlurComponent = ({
loading = false, loading = false,
style = {}, style = {},
iconName iconName,
blurAmount = 2
}: { }: {
loading: boolean; loading: boolean;
style: StyleProp<ViewStyle>; style: StyleProp<ViewStyle>;
iconName: TIconsName; iconName: TIconsName;
blurAmount?: number;
}) => { }) => {
const { theme, colors } = useTheme(); const { colors } = useTheme();
// const { theme, colors } = useTheme();
return ( return (
<> <>
<BlurView <BlurView
style={[style, styles.blurView]} style={[style, styles.blurView]}
blurType={theme === 'light' ? 'light' : 'dark'} blurType={'dark'}
blurAmount={10} // blurType={theme === 'light' ? 'light' : 'dark'}
reducedTransparencyFallbackColor='white' blurAmount={blurAmount}
/> />
<View style={[style, styles.blurIndicator]}> <View style={[style, styles.blurIndicator]}>
{loading ? <RCActivityIndicator /> : <CustomIcon color={colors.buttonText} name={iconName} size={54} />} {loading ? <RCActivityIndicator size={54} /> : <CustomIcon color={colors.buttonText} name={iconName} size={54} />}
</View> </View>
</> </>
); );

View File

@ -37,7 +37,7 @@ const Button = React.memo(({ children, onPress, disabled }: IMessageButton) => {
<Touchable <Touchable
disabled={disabled} disabled={disabled}
onPress={onPress} onPress={onPress}
style={styles.imageContainer} style={[styles.imageContainer, styles.mustWrapBlur]}
background={Touchable.Ripple(colors.bannerBackground)} background={Touchable.Ripple(colors.bannerBackground)}
> >
{children} {children}

View File

@ -1,11 +1,13 @@
import React, { useContext, useEffect, useRef, useState } from 'react'; import React, { useContext, useEffect, useRef, useState } from 'react';
import { StyleProp, StyleSheet, TextStyle, View, Text } from 'react-native'; import { StyleProp, StyleSheet, TextStyle, View, Text } from 'react-native';
import { dequal } from 'dequal'; import { dequal } from 'dequal';
import * as VideoThumbnails from 'expo-video-thumbnails';
import FastImage from 'react-native-fast-image';
import messageStyles from './styles';
import Touchable from './Touchable'; import Touchable from './Touchable';
import Markdown from '../markdown'; import Markdown from '../markdown';
import { isIOS } from '../../lib/methods/helpers'; import { isIOS } from '../../lib/methods/helpers';
import { CustomIcon } from '../CustomIcon';
import { themes } from '../../lib/constants'; import { themes } from '../../lib/constants';
import MessageContext from './Context'; import MessageContext from './Context';
import { fileDownload } from './helpers/fileDownload'; import { fileDownload } from './helpers/fileDownload';
@ -13,19 +15,13 @@ import EventEmitter from '../../lib/methods/helpers/events';
import { LISTENER } from '../Toast'; import { LISTENER } from '../Toast';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { IAttachment } from '../../definitions/IAttachment'; import { IAttachment } from '../../definitions/IAttachment';
import RCActivityIndicator from '../ActivityIndicator';
import { TGetCustomEmoji } from '../../definitions/IEmoji'; import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { useTheme } from '../../theme'; import { useTheme } from '../../theme';
import { formatAttachmentUrl } from '../../lib/methods/helpers/formatAttachmentUrl'; import { formatAttachmentUrl } from '../../lib/methods/helpers/formatAttachmentUrl';
import { import { cancelDownload, downloadMediaFile, isDownloadActive, getMediaCache } from '../../lib/methods/handleMediaDownload';
LOCAL_DOCUMENT_DIRECTORY,
cancelDownload,
downloadMediaFile,
isDownloadActive,
getMediaCache
} from '../../lib/methods/handleMediaDownload';
import { fetchAutoDownloadEnabled } from '../../lib/methods/autoDownloadPreference'; import { fetchAutoDownloadEnabled } from '../../lib/methods/autoDownloadPreference';
import sharedStyles from '../../views/Styles'; import sharedStyles from '../../views/Styles';
import BlurComponent from './Components/BlurComponent';
const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])]; const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])];
const isTypeSupported = (type: string) => SUPPORTED_TYPES.indexOf(type) !== -1; const isTypeSupported = (type: string) => SUPPORTED_TYPES.indexOf(type) !== -1;
@ -47,6 +43,11 @@ const styles = StyleSheet.create({
text: { text: {
...sharedStyles.textRegular, ...sharedStyles.textRegular,
fontSize: 12 fontSize: 12
},
thumbnailImage: {
borderRadius: 4,
width: '100%',
height: '100%'
} }
}); });
@ -58,16 +59,37 @@ interface IMessageVideo {
isReply?: boolean; isReply?: boolean;
} }
const DownloadIndicator = ({ handleCancelDownload }: { handleCancelDownload(): void }) => { const CancelIndicator = () => {
const { colors } = useTheme(); const { colors } = useTheme();
return (
<View style={styles.cancelContainer}>
<Text style={[styles.text, { color: colors.auxiliaryText }]}>{I18n.t('Cancel')}</Text>
</View>
);
};
const Thumbnail = ({ loading, video, cached }: { loading: boolean; video: string; cached: boolean }) => {
const [thumbnailImage, setThumbnailImage] = useState('');
useEffect(() => {
const generateThumbnail = async () => {
try {
const { uri } = await VideoThumbnails.getThumbnailAsync(video, {
time: 1
});
setThumbnailImage(uri);
} catch (e) {
// do nothing
}
};
generateThumbnail();
}, []);
return ( return (
<> <>
<View style={styles.cancelContainer}> {thumbnailImage ? <FastImage style={styles.thumbnailImage} source={{ uri: thumbnailImage }} /> : null}
<Touchable background={Touchable.Ripple(colors.bannerBackground)} onPress={handleCancelDownload}> <BlurComponent iconName={cached ? 'play-filled' : 'arrow-down-circle'} loading={loading} style={styles.button} />
<Text style={[styles.text, { color: colors.auxiliaryText }]}>{I18n.t('Cancel')}</Text> {loading ? <CancelIndicator /> : null}
</Touchable>
</View>
<RCActivityIndicator size={48} />
</> </>
); );
}; };
@ -75,7 +97,8 @@ const DownloadIndicator = ({ handleCancelDownload }: { handleCancelDownload(): v
const Video = React.memo( const Video = React.memo(
({ file, showAttachment, getCustomEmoji, style, isReply }: IMessageVideo) => { ({ file, showAttachment, getCustomEmoji, style, isReply }: IMessageVideo) => {
const [videoCached, setVideoCached] = useState(file); const [videoCached, setVideoCached] = useState(file);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(true);
const [cached, setCached] = useState(false);
const { baseUrl, user } = useContext(MessageContext); const { baseUrl, user } = useContext(MessageContext);
const { theme } = useTheme(); const { theme } = useTheme();
const filePath = useRef(''); const filePath = useRef('');
@ -96,13 +119,20 @@ const Video = React.memo(
...prev, ...prev,
video_url: cachedVideoResult.file?.uri video_url: cachedVideoResult.file?.uri
})); }));
setCached(true);
setLoading(false);
if (downloadActive) { if (downloadActive) {
cancelDownload(video); cancelDownload(video);
} }
return; return;
} }
if (isReply) return; if (downloadActive && !isReply) {
if (downloadActive) return setLoading(true); return;
}
setLoading(false);
if (isReply) {
return;
}
await handleAutoDownload(); await handleAutoDownload();
} }
}; };
@ -131,18 +161,24 @@ const Video = React.memo(
...prev, ...prev,
video_url: videoUri video_url: videoUri
})); }));
setCached(true);
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; };
const onPress = async () => { const onPress = async () => {
if (file.video_type && isTypeSupported(file.video_type) && showAttachment) { if (file.video_type && cached && isTypeSupported(file.video_type) && showAttachment) {
if (LOCAL_DOCUMENT_DIRECTORY && !videoCached.video_url?.startsWith(LOCAL_DOCUMENT_DIRECTORY) && !loading) { showAttachment(videoCached);
// Keep the video downloading while showing the video buffering return;
handleDownload(); }
} if (!loading) {
return showAttachment(videoCached); handleDownload();
return;
}
if (loading) {
handleCancelDownload();
return;
} }
if (!isIOS && file.video_url) { if (!isIOS && file.video_url) {
await downloadVideoToGallery(video); await downloadVideoToGallery(video);
@ -154,7 +190,7 @@ const Video = React.memo(
const handleCancelDownload = () => { const handleCancelDownload = () => {
if (loading) { if (loading) {
cancelDownload(video); cancelDownload(video);
return setLoading(false); setLoading(false);
} }
}; };
@ -182,14 +218,10 @@ const Video = React.memo(
<Touchable <Touchable
disabled={isReply} disabled={isReply}
onPress={onPress} onPress={onPress}
style={[styles.button, { backgroundColor: themes[theme].videoBackground }]} style={[styles.button, messageStyles.mustWrapBlur, { backgroundColor: themes[theme].videoBackground }]}
background={Touchable.Ripple(themes[theme].bannerBackground)} background={Touchable.Ripple(themes[theme].bannerBackground)}
> >
{loading ? ( <Thumbnail loading={loading} video={video} cached={cached} />
<DownloadIndicator handleCancelDownload={handleCancelDownload} />
) : (
<CustomIcon name='play-filled' size={54} color={themes[theme].buttonText} />
)}
</Touchable> </Touchable>
</> </>
); );

View File

@ -96,9 +96,7 @@ export default StyleSheet.create({
}, },
imageContainer: { imageContainer: {
flexDirection: 'column', flexDirection: 'column',
borderRadius: 4, borderRadius: 4
// https://github.com/Kureev/react-native-blur/issues/520#issuecomment-1378339192 Fix BlurView
overflow: isAndroid ? 'hidden' : 'visible'
}, },
image: { image: {
width: '100%', width: '100%',
@ -184,5 +182,9 @@ export default StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
borderWidth: 0 borderWidth: 0
},
mustWrapBlur: {
// https://github.com/Kureev/react-native-blur/issues/520#issuecomment-1378339192 Fix BlurView
overflow: isAndroid ? 'hidden' : 'visible'
} }
}); });