Video working

This commit is contained in:
Zhiyuan Zheng 2020-10-30 20:03:44 +01:00
parent 2fae98cb9e
commit aa53533534
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
11 changed files with 319 additions and 182 deletions

View File

@ -75,7 +75,8 @@ const styles = StyleSheet.create({
})
Actioned.propTypes = {
action: PropTypes.oneOf(['favourite', 'follow', 'poll', 'reblog']).isRequired,
action: PropTypes.oneOf(['favourite', 'follow', 'mention', 'poll', 'reblog'])
.isRequired,
name: PropTypes.string,
emojis: PropTypes.arrayOf(propTypesEmoji),
notification: PropTypes.bool

View File

@ -0,0 +1,81 @@
import React from 'react'
import PropTypes from 'prop-types'
import propTypesAttachment from 'src/prop-types/attachment'
import { Text, View } from 'react-native'
import AttachmentImage from './Attachment/AttachmentImage'
import AttachmentVideo from './Attachment/AttachmentVideo'
export default function Attachment ({ media_attachments, sensitive, width }) {
let attachment
let attachmentHeight
// if (width) {}
switch (media_attachments[0].type) {
case 'unknown':
attachment = <Text>文件不支持</Text>
attachmentHeight = 25
break
case 'image':
attachment = (
<AttachmentImage
media_attachments={media_attachments}
sensitive={sensitive}
width={width}
/>
)
attachmentHeight = width / 2
break
case 'gifv':
attachment = (
<AttachmentVideo
media_attachments={media_attachments}
sensitive={sensitive}
width={width}
/>
)
attachmentHeight =
(width / media_attachments[0].meta.original.width) *
media_attachments[0].meta.original.height
break
case 'video':
attachment = (
<AttachmentVideo
media_attachments={media_attachments}
sensitive={sensitive}
width={width}
/>
)
attachmentHeight =
(width / media_attachments[0].meta.original.width) *
media_attachments[0].meta.original.height
break
// case 'audio':
// attachment = (
// <AttachmentAudio
// media_attachments={media_attachments}
// sensitive={sensitive}
// width={width}
// />
// )
// break
}
return (
<View
style={{
width: width + 8,
height: attachmentHeight,
marginTop: 4,
marginLeft: -4
}}
>
{attachment}
</View>
)
}
Attachment.propTypes = {
media_attachments: PropTypes.arrayOf(propTypesAttachment),
sensitive: PropTypes.bool.isRequired,
width: PropTypes.number.isRequired
}

View File

@ -0,0 +1,102 @@
import React, { useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import propTypesAttachment from 'src/prop-types/attachment'
import { Button, Image, Modal, StyleSheet, Pressable, View } from 'react-native'
import ImageViewer from 'react-native-image-zoom-viewer'
export default function AttachmentImage ({ media_attachments, sensitive, width }) {
const [mediaSensitive, setMediaSensitive] = useState(sensitive)
const [imageModalVisible, setImageModalVisible] = useState(false)
const [imageModalIndex, setImageModalIndex] = useState(0)
useEffect(() => {
if (sensitive && mediaSensitive === false) {
setTimeout(() => {
setMediaSensitive(true)
}, 10000)
}
}, [mediaSensitive])
let images = []
media_attachments = media_attachments.map((m, i) => {
images.push({
url: m.url,
width: m.meta.original.width,
height: m.meta.original.height
})
return (
<Pressable
key={i}
style={{ flexGrow: 1, height: width / 2, margin: 4 }}
onPress={() => {
setImageModalIndex(i)
setImageModalVisible(true)
}}
>
<Image
source={{ uri: m.preview_url }}
style={styles.image}
blurRadius={mediaSensitive ? width / 5 : 0}
/>
</Pressable>
)
})
return (
<>
<View style={styles.media}>
{media_attachments}
{mediaSensitive && (
<View
style={{
position: 'absolute',
width: '100%',
height: '100%'
}}
>
<Button
title='Press me'
onPress={() => {
setMediaSensitive(false)
}}
/>
</View>
)}
</View>
<Modal
visible={imageModalVisible}
transparent={true}
animationType='fade'
>
<ImageViewer
imageUrls={images}
index={imageModalIndex}
onSwipeDown={() => setImageModalVisible(false)}
enableSwipeDown={true}
swipeDownThreshold={100}
useNativeDriver={true}
/>
</Modal>
</>
)
}
const styles = StyleSheet.create({
media: {
flex: 1,
flexDirection: 'column',
flexWrap: 'wrap',
justifyContent: 'space-between',
alignItems: 'stretch',
alignContent: 'stretch'
},
image: {
width: '100%',
height: '100%'
}
})
AttachmentImage.propTypes = {
media_attachments: PropTypes.arrayOf(propTypesAttachment),
sensitive: PropTypes.bool.isRequired,
width: PropTypes.number.isRequired
}

View File

@ -0,0 +1,72 @@
import React, { useRef, useState } from 'react'
import PropTypes from 'prop-types'
import propTypesAttachment from 'src/prop-types/attachment'
import { Pressable, View } from 'react-native'
import { Video } from 'expo-av'
import { Feather } from '@expo/vector-icons'
export default function AttachmentVideo ({
media_attachments,
sensitive,
width
}) {
const videoPlayer = useRef()
const [mediaSensitive, setMediaSensitive] = useState(sensitive)
const [videoPlay, setVideoPlay] = useState(false)
const video = media_attachments[0]
const videoWidth = width
const videoHeight =
(width / video.meta.original.width) * video.meta.original.height
return (
<View
style={{
width: videoWidth,
height: videoHeight
}}
>
<Video
ref={videoPlayer}
source={{ uri: video.remote_url }}
style={{
width: videoWidth,
height: videoHeight
}}
resizeMode='cover'
usePoster
posterSourceThe={{ uri: video.preview_url }}
useNativeControls
shouldPlay={videoPlay}
/>
{!videoPlay && (
<Pressable
onPress={() => {
setMediaSensitive(false)
videoPlayer.current.presentFullscreenPlayer()
setVideoPlay(true)
}}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.25)'
}}
>
<Feather name='play' size={36} color='black' />
</Pressable>
)}
</View>
)
}
AttachmentVideo.propTypes = {
media_attachments: PropTypes.arrayOf(propTypesAttachment),
sensitive: PropTypes.bool.isRequired,
width: PropTypes.number.isRequired
}

View File

@ -1,138 +0,0 @@
import React, { useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import propTypesAttachment from 'src/prop-types/attachment'
import {
Button,
Image,
Modal,
StyleSheet,
Text,
Pressable,
View
} from 'react-native'
import ImageViewer from 'react-native-image-zoom-viewer'
export default function Media ({ media_attachments, sensitive, width }) {
const [mediaSensitive, setMediaSensitive] = useState(sensitive)
const [imageModalVisible, setImageModalVisible] = useState(false)
const [imageModalIndex, setImageModalIndex] = useState(0)
useEffect(() => {
if (sensitive && mediaSensitive === false) {
setTimeout(() => {
setMediaSensitive(true)
}, 10000)
}
}, [mediaSensitive])
let media
let images = []
if (width) {
media_attachments = media_attachments.map((m, i) => {
switch (m.type) {
case 'unknown':
return <Text key={i}>文件不支持</Text>
case 'image':
images.push({
url: m.url,
width: m.meta.original.width,
height: m.meta.original.height
})
return (
<Pressable
key={i}
style={{ flexGrow: 1, height: width / 2, margin: 4 }}
onPress={() => {
setImageModalIndex(i)
setImageModalVisible(true)
}}
>
<Image
source={{ uri: m.preview_url }}
style={styles.image}
blurRadius={mediaSensitive ? width / 5 : 0}
/>
</Pressable>
)
}
})
if (images) {
media = (
<>
<View style={styles.media}>
{media_attachments}
{mediaSensitive && (
<View
style={{
position: 'absolute',
width: '100%',
height: '100%'
}}
>
<Button
title='Press me'
onPress={() => {
setMediaSensitive(false)
}}
/>
</View>
)}
</View>
<Modal
visible={imageModalVisible}
transparent={true}
animationType='fade'
>
<ImageViewer
imageUrls={images}
index={imageModalIndex}
onSwipeDown={() => setImageModalVisible(false)}
enableSwipeDown={true}
swipeDownThreshold={100}
useNativeDriver={true}
/>
</Modal>
</>
)
} else {
media = <View style={styles.media}>{media_attachments}</View>
}
} else {
media = <></>
}
return (
media_attachments.length > 0 && (
<View
style={{
width: width + 8,
height: width / 2,
marginTop: 4,
marginLeft: -4
}}
>
{media}
</View>
)
)
}
const styles = StyleSheet.create({
media: {
flex: 1,
flexDirection: 'column',
flexWrap: 'wrap',
justifyContent: 'space-between',
alignItems: 'stretch',
alignContent: 'stretch'
},
image: {
width: '100%',
height: '100%'
}
})
Media.propTypes = {
media_attachments: PropTypes.arrayOf(propTypesAttachment),
sensitive: PropTypes.bool.isRequired,
width: PropTypes.number.isRequired
}

View File

@ -8,7 +8,7 @@ import Avatar from './Toot/Avatar'
import Header from './Toot/Header'
import Content from './Toot/Content'
import Poll from './Toot/Poll'
import Media from './Toot/Media'
import Attachment from './Toot/Attachment'
import Card from './Toot/Card'
import Actions from './Toot/Actions'
@ -53,7 +53,7 @@ export default function TootNotification ({ toot }) {
)}
{toot.status.poll && <Poll poll={toot.status.poll} />}
{toot.status.media_attachments && (
<Media
<Attachment
media_attachments={toot.status.media_attachments}
sensitive={toot.status.sensitive}
width={Dimensions.get('window').width - 24 - 50 - 8}

View File

@ -8,7 +8,7 @@ import Avatar from './Toot/Avatar'
import Header from './Toot/Header'
import Content from './Toot/Content'
import Poll from './Toot/Poll'
import Media from './Toot/Media'
import Attachment from './Toot/Attachment'
import Card from './Toot/Card'
import Actions from './Toot/Actions'
@ -50,9 +50,9 @@ export default function TootTimeline ({ toot }) {
/>
{/* Can pass toot info to next page to speed up performance */}
<Pressable
onPress={() =>
navigation.navigate('Toot', { toot: actualContent.id })
}
// onPress={() =>
// navigation.navigate('Toot', { toot: actualContent.id })
// }
>
{actualContent.content ? (
<Content
@ -67,8 +67,8 @@ export default function TootTimeline ({ toot }) {
<></>
)}
{actualContent.poll && <Poll poll={actualContent.poll} />}
{actualContent.media_attachments && (
<Media
{actualContent.media_attachments.length > 0 && (
<Attachment
media_attachments={actualContent.media_attachments}
sensitive={actualContent.sensitive}
width={Dimensions.get('window').width - 24 - 50 - 8}

View File

@ -3,6 +3,7 @@ import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import { Feather } from '@expo/vector-icons'
import Timeline from 'src/stacks/common/Timeline'
import sharedScreens from 'src/stacks/Shared/sharedScreens'
const Stack = createNativeStackNavigator()
@ -28,6 +29,8 @@ export default function Notifications () {
<Stack.Screen name='Notifications'>
{() => <Timeline page='Notifications' />}
</Stack.Screen>
{sharedScreens(Stack)}
</Stack.Navigator>
)
}

View File

@ -0,0 +1,45 @@
import React from 'react'
import Account from 'src/stacks/Shared/Account'
import Hashtag from 'src/stacks/Shared/Hashtag'
import Toot from 'src/stacks/Shared/Toot'
import Webview from 'src/stacks/Shared/Webview'
export default function sharedScreens (Stack) {
return [
<Stack.Screen
key='Account'
name='Account'
component={Account}
options={{
headerTranslucent: true,
headerStyle: { backgroundColor: 'rgba(255, 255, 255, 0)' },
headerCenter: () => {}
}}
/>,
<Stack.Screen
key='Hashtag'
name='Hashtag'
component={Hashtag}
options={({ route }) => ({
title: `#${decodeURIComponent(route.params.hashtag)}`
})}
/>,
<Stack.Screen
key='Toot'
name='Toot'
component={Toot}
options={() => ({
title: '对话'
})}
/>,
<Stack.Screen
key='Webview'
name='Webview'
component={Webview}
// options={({ route }) => ({
// title: `${route.params.domain}`
// })}
/>
]
}

View File

@ -22,10 +22,12 @@ export default function Timeline ({
const [timelineReady, setTimelineReady] = useState(false)
useEffect(() => {
if (state.status === 'idle') {
let mounted = true
if (state.status === 'idle' && mounted) {
dispatch(fetch({ page, hashtag, list, toot, account }))
setTimelineReady(true)
}
return () => (mounted = false)
}, [state, dispatch])
let content

View File

@ -6,10 +6,7 @@ import SegmentedControl from '@react-native-community/segmented-control'
import { Feather } from '@expo/vector-icons'
import Timeline from './Timeline'
import Account from 'src/stacks/Shared/Account'
import Hashtag from 'src/stacks/Shared/Hashtag'
import Toot from 'src/stacks/Shared/Toot'
import Webview from 'src/stacks/Shared/Webview'
import sharedScreens from 'src/stacks/Shared/sharedScreens'
const Stack = createNativeStackNavigator()
@ -93,36 +90,8 @@ export default function TimelinesCombined ({ name, content }) {
/>
)}
</Stack.Screen>
<Stack.Screen
name='Account'
component={Account}
options={{
headerTranslucent: true,
headerStyle: { backgroundColor: 'rgba(255, 255, 255, 0)' },
headerCenter: () => {}
}}
/>
<Stack.Screen
name='Hashtag'
component={Hashtag}
options={({ route }) => ({
title: `#${decodeURIComponent(route.params.hashtag)}`
})}
/>
<Stack.Screen
name='Toot'
component={Toot}
options={() => ({
title: '对话'
})}
/>
<Stack.Screen
name='Webview'
component={Webview}
// options={({ route }) => ({
// title: `${route.params.domain}`
// })}
/>
{sharedScreens(Stack)}
</Stack.Navigator>
)
}