From ee8169f1683bd55a8a85d0e94ce3a519f15629a0 Mon Sep 17 00:00:00 2001 From: balzack Date: Sun, 5 Jan 2025 13:14:04 -0800 Subject: [PATCH] adding playback controls for audio and video --- .../message/audioAsset/AudioAsset.styled.ts | 30 +++++++++- .../src/message/audioAsset/AudioAsset.tsx | 47 +++++++++++++-- .../message/audioAsset/useAudioAsset.hook.ts | 4 +- .../message/videoAsset/VideoAsset.styled.ts | 9 ++- .../src/message/videoAsset/VideoAsset.tsx | 58 +++++++++++++++++-- 5 files changed, 131 insertions(+), 17 deletions(-) diff --git a/app/client/mobile/src/message/audioAsset/AudioAsset.styled.ts b/app/client/mobile/src/message/audioAsset/AudioAsset.styled.ts index 059b0957..ab8eede4 100644 --- a/app/client/mobile/src/message/audioAsset/AudioAsset.styled.ts +++ b/app/client/mobile/src/message/audioAsset/AudioAsset.styled.ts @@ -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: { diff --git a/app/client/mobile/src/message/audioAsset/AudioAsset.tsx b/app/client/mobile/src/message/audioAsset/AudioAsset.tsx index 225f9487..62a2e890 100644 --- a/app/client/mobile/src/message/audioAsset/AudioAsset.tsx +++ b/app/client/mobile/src/message/audioAsset/AudioAsset.tsx @@ -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(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 ( @@ -48,6 +75,7 @@ export function AudioAsset({ topicId, asset, loaded, show }: { topicId: string, + { asset.audio?.label || asset.encrypted?.label } @@ -59,8 +87,18 @@ export function AudioAsset({ topicId, asset, loaded, show }: { topicId: string, source={thumb} /> { state.dataUrl && ( - diff --git a/app/client/mobile/src/message/audioAsset/useAudioAsset.hook.ts b/app/client/mobile/src/message/audioAsset/useAudioAsset.hook.ts index 790576d6..5c3cffa1 100644 --- a/app/client/mobile/src/message/audioAsset/useAudioAsset.hook.ts +++ b/app/client/mobile/src/message/audioAsset/useAudioAsset.hook.ts @@ -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 } diff --git a/app/client/mobile/src/message/videoAsset/VideoAsset.styled.ts b/app/client/mobile/src/message/videoAsset/VideoAsset.styled.ts index 26c89ca5..07fa2026 100644 --- a/app/client/mobile/src/message/videoAsset/VideoAsset.styled.ts +++ b/app/client/mobile/src/message/videoAsset/VideoAsset.styled.ts @@ -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, diff --git a/app/client/mobile/src/message/videoAsset/VideoAsset.tsx b/app/client/mobile/src/message/videoAsset/VideoAsset.tsx index d98aed03..d97d7b19 100644 --- a/app/client/mobile/src/message/videoAsset/VideoAsset.tsx +++ b/app/client/mobile/src/message/videoAsset/VideoAsset.tsx @@ -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(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 ( { state.thumbUrl && ( @@ -54,7 +90,7 @@ export function VideoAsset({ topicId, asset, loaded, show }: { topicId: string, )} - + { state.dataUrl && ( - );