tooot/src/components/Timeline/Shared/Attachment/Video.tsx

175 lines
4.6 KiB
TypeScript
Raw Normal View History

2020-12-26 23:05:17 +01:00
import Button from '@components/Button'
2021-01-18 00:23:40 +01:00
import { StyleConstants } from '@utils/styles/constants'
import { Video } from 'expo-av'
2021-09-15 22:52:21 +02:00
import React, { useCallback, useEffect, useRef, useState } from 'react'
import {
AppState,
AppStateStatus,
Pressable,
StyleSheet,
View
} from 'react-native'
2021-01-28 01:31:19 +01:00
import { Blurhash } from 'react-native-blurhash'
2021-01-18 00:23:40 +01:00
import attachmentAspectRatio from './aspectRatio'
2021-01-24 02:25:43 +01:00
import analytics from '@components/analytics'
2020-12-25 18:20:09 +01:00
export interface Props {
2021-01-18 00:23:40 +01:00
total: number
index: number
2020-12-25 18:20:09 +01:00
sensitiveShown: boolean
video: Mastodon.AttachmentVideo | Mastodon.AttachmentGifv
gifv?: boolean
2020-12-25 18:20:09 +01:00
}
2021-01-18 00:23:40 +01:00
const AttachmentVideo: React.FC<Props> = ({
total,
index,
sensitiveShown,
video,
gifv = false
2021-01-18 00:23:40 +01:00
}) => {
2020-12-25 18:20:09 +01:00
const videoPlayer = useRef<Video>(null)
const [videoLoading, setVideoLoading] = useState(false)
2020-12-25 18:20:09 +01:00
const [videoLoaded, setVideoLoaded] = useState(false)
const [videoPosition, setVideoPosition] = useState<number>(0)
const playOnPress = useCallback(async () => {
2021-01-24 02:25:43 +01:00
analytics('timeline_shared_attachment_video_length', {
length: video.meta?.length
})
analytics('timeline_shared_attachment_vide_play_press', {
id: video.id,
timestamp: Date.now()
})
setVideoLoading(true)
2020-12-25 18:20:09 +01:00
if (!videoLoaded) {
await videoPlayer.current?.loadAsync({ uri: video.url })
}
await videoPlayer.current?.setPositionAsync(videoPosition)
await videoPlayer.current?.presentFullscreenPlayer()
videoPlayer.current?.playAsync()
setVideoLoading(false)
2020-12-25 18:20:09 +01:00
videoPlayer.current?.setOnPlaybackStatusUpdate(props => {
if (props.isLoaded) {
setVideoLoaded(true)
2021-09-15 22:52:21 +02:00
if (props.positionMillis) {
setVideoPosition(props.positionMillis)
}
2020-12-25 18:20:09 +01:00
}
})
}, [videoLoaded, videoPosition])
2021-09-15 22:52:21 +02:00
const appState = useRef(AppState.currentState)
useEffect(() => {
2022-01-16 23:26:05 +01:00
const appState = AppState.addEventListener('change', _handleAppStateChange)
2021-09-15 22:52:21 +02:00
2022-01-16 23:26:05 +01:00
return () => appState.remove()
2021-09-15 22:52:21 +02:00
}, [])
const _handleAppStateChange = async (nextAppState: AppStateStatus) => {
if (appState.current.match(/active/) && nextAppState.match(/inactive/)) {
await videoPlayer.current?.pauseAsync()
} else if (
gifv &&
appState.current.match(/background/) &&
nextAppState.match(/active/)
) {
await videoPlayer.current?.setIsMutedAsync(true)
await videoPlayer.current?.playAsync()
}
appState.current = nextAppState
}
const playerStatus = useRef<any>(null)
useEffect(() => {
videoPlayer.current?.setOnPlaybackStatusUpdate(playbackStatus => {
playerStatus.current = playbackStatus
})
}, [])
2020-12-25 18:20:09 +01:00
return (
2021-01-18 00:23:40 +01:00
<View
style={[
styles.base,
{ aspectRatio: attachmentAspectRatio({ total, index }) }
]}
>
2020-12-25 18:20:09 +01:00
<Video
2021-04-09 21:43:12 +02:00
accessibilityLabel={video.description}
2020-12-25 18:20:09 +01:00
ref={videoPlayer}
style={{
2020-12-28 16:54:19 +01:00
width: '100%',
2020-12-28 17:30:20 +01:00
height: '100%',
opacity: sensitiveShown ? 0 : 1
2020-12-25 18:20:09 +01:00
}}
usePoster
2021-03-27 00:47:14 +01:00
{...(gifv
? {
shouldPlay: true,
isMuted: true,
isLooping: true,
source: { uri: video.url }
}
2021-03-27 00:47:14 +01:00
: {
resizeMode: 'cover',
posterSource: { uri: video.preview_url },
posterStyle: { resizeMode: 'cover' }
})}
2020-12-25 18:20:09 +01:00
useNativeControls={false}
2021-09-15 22:52:21 +02:00
onFullscreenUpdate={async event => {
2021-08-11 00:17:37 +02:00
if (
event.fullscreenUpdate ===
Video.FULLSCREEN_UPDATE_PLAYER_DID_DISMISS
) {
if (gifv) {
2021-09-15 22:52:21 +02:00
await videoPlayer.current?.pauseAsync()
2021-08-11 00:17:37 +02:00
} else {
2021-09-15 22:52:21 +02:00
await videoPlayer.current?.pauseAsync()
2021-08-11 00:17:37 +02:00
}
2021-01-16 00:00:31 +01:00
}
}}
2020-12-25 18:20:09 +01:00
/>
2021-08-11 00:17:37 +02:00
<Pressable style={styles.overlay} onPress={gifv ? playOnPress : null}>
2020-12-25 18:20:09 +01:00
{sensitiveShown ? (
2020-12-30 11:55:51 +01:00
video.blurhash ? (
2021-01-28 01:31:19 +01:00
<Blurhash
blurhash={video.blurhash}
2020-12-30 11:55:51 +01:00
style={{
width: '100%',
height: '100%'
}}
2021-01-28 01:31:19 +01:00
/>
2020-12-30 11:55:51 +01:00
) : null
2021-09-15 22:52:21 +02:00
) : !gifv || (gifv && playerStatus.current === false) ? (
2020-12-26 23:05:17 +01:00
<Button
2020-12-28 16:54:19 +01:00
round
2020-12-26 23:05:17 +01:00
overlay
size='L'
type='icon'
content='PlayCircle'
2020-12-26 23:05:17 +01:00
onPress={playOnPress}
loading={videoLoading}
2020-12-26 23:05:17 +01:00
/>
2021-03-27 00:47:14 +01:00
) : null}
2020-12-25 18:20:09 +01:00
</Pressable>
2020-12-28 16:54:19 +01:00
</View>
2020-12-25 18:20:09 +01:00
)
}
const styles = StyleSheet.create({
2020-12-28 16:54:19 +01:00
base: {
flex: 1,
flexBasis: '50%',
padding: StyleConstants.Spacing.XS / 2
},
2020-12-25 18:20:09 +01:00
overlay: {
position: 'absolute',
width: '100%',
height: '100%',
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
export default AttachmentVideo