mirror of
https://github.com/balzack/databag.git
synced 2025-04-23 01:55:17 +00:00
adding playback controls for audio and video
This commit is contained in:
parent
b3dc1459ca
commit
ee8169f168
@ -1,5 +1,5 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
import {Colors} from '../constants/Colors';
|
||||
import {Colors} from '../../constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
modal: {
|
||||
@ -9,6 +9,12 @@ export const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
failed: {
|
||||
color: Colors.offsync,
|
||||
},
|
||||
control: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
button: {
|
||||
position: 'absolute',
|
||||
borderRadius: 4,
|
||||
@ -44,12 +50,30 @@ export const styles = StyleSheet.create({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
close: {
|
||||
info: {
|
||||
fontSize: 12,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
},
|
||||
label: {
|
||||
flexGrow: 1,
|
||||
fontSize: 32,
|
||||
paddingLeft: 16,
|
||||
minWidth: 0,
|
||||
textOverflow: 'ellipsis',
|
||||
flexShrink: 1,
|
||||
},
|
||||
close: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
position: 'absolute',
|
||||
alignItems: 'center',
|
||||
top: 0,
|
||||
minWidth: 0,
|
||||
width: '100%',
|
||||
},
|
||||
closeIcon: {
|
||||
flexShrink: 0,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
progress: {
|
||||
|
@ -1,17 +1,20 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { SafeAreaView, Modal, Pressable, View, Image, Animated, useAnimatedValue } from 'react-native'
|
||||
import { Icon, ProgressBar, IconButton } from 'react-native-paper'
|
||||
import { Icon, Text, ProgressBar, IconButton } from 'react-native-paper'
|
||||
import { useAudioAsset } from './useAudioAsset.hook';
|
||||
import { MediaAsset } from '../../conversation/Conversation';
|
||||
import { styles } from './AudioAsset.styled'
|
||||
import {BlurView} from '@react-native-community/blur';
|
||||
import Video from 'react-native-video'
|
||||
import Video, { VideoRef } from 'react-native-video'
|
||||
import thumb from '../../images/audio.png';
|
||||
import {Colors} from '../../constants/Colors';
|
||||
|
||||
export function AudioAsset({ topicId, asset, loaded, show }: { topicId: string, asset: MediaAsset, loaded: ()=>void, show: boolean }) {
|
||||
const { state, actions } = useAudioAsset(topicId, asset);
|
||||
const [modal, setModal] = useState(false);
|
||||
const opacity = useAnimatedValue(0);
|
||||
const videoRef = useRef<VideoRef>(null as null | VideoRef);
|
||||
const [status, setStatus] = useState('loading');
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
@ -33,6 +36,30 @@ export function AudioAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
actions.cancelLoad();
|
||||
}
|
||||
|
||||
const play = () => {
|
||||
videoRef.current.resume();
|
||||
}
|
||||
|
||||
const pause = () => {
|
||||
videoRef.current.pause();
|
||||
}
|
||||
|
||||
const error = () => {
|
||||
setStatus('failed');
|
||||
}
|
||||
|
||||
const end = () => {
|
||||
videoRef.current.seek(0);
|
||||
}
|
||||
|
||||
const playbackRateChange = (e) => {
|
||||
if (e.playbackRate === 0) {
|
||||
setStatus('paused');
|
||||
} else {
|
||||
setStatus('playing');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.audio}>
|
||||
<Pressable onPress={showAudio}>
|
||||
@ -48,6 +75,7 @@ export function AudioAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
<View style={styles.button}>
|
||||
<Icon size={28} source="play-box-outline" />
|
||||
</View>
|
||||
<Text style={styles.info} numberOfLines={1}>{ asset.audio?.label || asset.encrypted?.label }</Text>
|
||||
</Animated.View>
|
||||
</Pressable>
|
||||
<Modal animationType="fade" transparent={true} supportedOrientations={['portrait', 'landscape']} visible={modal} onRequestClose={hideAudio}>
|
||||
@ -59,8 +87,18 @@ export function AudioAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
source={thumb}
|
||||
/>
|
||||
{ state.dataUrl && (
|
||||
<Video source={{ uri: state.dataUrl }} style={styles.full} paused={false}
|
||||
onLoad={(e)=>console.log(e)} onError={(e)=>console.log(e)} controls={false} resizeMode="contain" />
|
||||
<Video source={{ uri: state.dataUrl }} style={styles.full} paused={false} ref={videoRef}
|
||||
onPlaybackRateChange={playbackRateChange} onEnd={end} onError={error}
|
||||
controls={false} resizeMode="contain" />
|
||||
)}
|
||||
{ status === 'failed' && (
|
||||
<Icon color={Colors.offsync} size={64} source="fire" />
|
||||
)}
|
||||
{ status === 'playing' && (
|
||||
<IconButton style={styles.control} size={64} icon="pause" onPress={pause} />
|
||||
)}
|
||||
{ status === 'paused' && (
|
||||
<IconButton style={styles.control} size={64} icon="play" onPress={play} />
|
||||
)}
|
||||
{ state.loading && (
|
||||
<View style={styles.progress}>
|
||||
@ -68,6 +106,7 @@ export function AudioAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
</View>
|
||||
)}
|
||||
<SafeAreaView style={styles.close}>
|
||||
<Text style={styles.label} adjustsFontSizeToFit={true} numberOfLines={1}>{ asset.audio?.label || asset.encrypted?.label }</Text>
|
||||
<IconButton style={styles.closeIcon} icon="close" compact="true" mode="contained" size={28} onPress={hideAudio} />
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
|
@ -31,15 +31,13 @@ export function useAudioAsset(topicId: string, asset: MediaAsset) {
|
||||
updateState({ loading: true, loadPercent: 0 });
|
||||
try {
|
||||
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }); return !cancelled.current });
|
||||
console.log("AUDIO", dataUrl);
|
||||
|
||||
updateState({ dataUrl });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
updateState({ loading: false });
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return { state, actions }
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
import {Colors} from '../constants/Colors';
|
||||
import {Colors} from '../../constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
modal: {
|
||||
@ -9,6 +9,13 @@ export const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
failed: {
|
||||
color: Colors.offsync,
|
||||
},
|
||||
control: {
|
||||
position: 'absolute',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
button: {
|
||||
position: 'absolute',
|
||||
borderRadius: 13,
|
||||
|
@ -5,13 +5,17 @@ import { useVideoAsset } from './useVideoAsset.hook';
|
||||
import { MediaAsset } from '../../conversation/Conversation';
|
||||
import { styles } from './VideoAsset.styled'
|
||||
import {BlurView} from '@react-native-community/blur';
|
||||
import Video from 'react-native-video'
|
||||
import Video, { VideoRef } from 'react-native-video'
|
||||
|
||||
export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string, asset: MediaAsset, loaded: ()=>void, show: boolean }) {
|
||||
const { state, actions } = useVideoAsset(topicId, asset);
|
||||
const [modal, setModal] = useState(false);
|
||||
const opacity = useAnimatedValue(0);
|
||||
|
||||
const videoRef = useRef<VideoRef>(null as null | VideoRef);
|
||||
const [status, setStatus] = useState('loading');
|
||||
const [showControl, setShowControl] = useState(false);
|
||||
const clear = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
if (state.loaded && show) {
|
||||
Animated.timing(opacity, {
|
||||
@ -35,6 +39,38 @@ export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
actions.cancelLoad();
|
||||
}
|
||||
|
||||
const controls = () => {
|
||||
clearTimeout(clear.current);
|
||||
setShowControl(true);
|
||||
clear.current = setTimeout(() => {
|
||||
setShowControl(false);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
const play = () => {
|
||||
videoRef.current.resume();
|
||||
}
|
||||
|
||||
const pause = () => {
|
||||
videoRef.current.pause();
|
||||
}
|
||||
|
||||
const error = () => {
|
||||
setStatus('failed');
|
||||
}
|
||||
|
||||
const end = () => {
|
||||
videoRef.current.seek(0);
|
||||
}
|
||||
|
||||
const playbackRateChange = (e) => {
|
||||
if (e.playbackRate === 0) {
|
||||
setStatus('paused');
|
||||
} else {
|
||||
setStatus('playing');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.video}>
|
||||
{ state.thumbUrl && (
|
||||
@ -54,7 +90,7 @@ export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
</Pressable>
|
||||
)}
|
||||
<Modal animationType="fade" transparent={true} supportedOrientations={['portrait', 'landscape']} visible={modal} onRequestClose={hideVideo}>
|
||||
<View style={styles.modal}>
|
||||
<Pressable style={styles.modal} onPress={controls}>
|
||||
<BlurView style={styles.blur} blurType="dark" blurAmount={16} reducedTransparencyFallbackColor="dark" />
|
||||
<Image
|
||||
style={styles.full}
|
||||
@ -62,8 +98,18 @@ export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
source={{ uri: state.thumbUrl }}
|
||||
/>
|
||||
{ state.dataUrl && (
|
||||
<Video source={{ uri: state.dataUrl }} style={styles.full}
|
||||
controls={false} resizeMode="contain" />
|
||||
<Video source={{ uri: state.dataUrl }} style={styles.full} ref={videoRef}
|
||||
onPlaybackRateChange={playbackRateChange} onEnd={end} onError={error}
|
||||
controls={false} resizeMode="contain" />
|
||||
)}
|
||||
{ status === 'failed' && (
|
||||
<Icon color={Colors.offsync} size={64} source="fire" />
|
||||
)}
|
||||
{ status === 'playing' && showControl && (
|
||||
<IconButton style={styles.control} size={64} icon="pause" onPress={pause} />
|
||||
)}
|
||||
{ status === 'paused' && showControl && (
|
||||
<IconButton style={styles.control} size={64} icon="play" onPress={play} />
|
||||
)}
|
||||
{ state.loading && (
|
||||
<View style={styles.progress}>
|
||||
@ -73,7 +119,7 @@ export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string,
|
||||
<SafeAreaView style={styles.close}>
|
||||
<IconButton style={styles.closeIcon} icon="close" compact="true" mode="contained" size={28} onPress={hideVideo} />
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
</Pressable>
|
||||
</Modal>
|
||||
</View>
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user