mirror of
https://github.com/tooot-app/app
synced 2025-04-24 23:18:47 +02:00
Basic images working
This commit is contained in:
parent
eefa7e01bd
commit
3f8e451099
13
package-lock.json
generated
13
package-lock.json
generated
@ -6590,6 +6590,19 @@
|
|||||||
"htmlparser2-without-node-native": "^3.9.2"
|
"htmlparser2-without-node-native": "^3.9.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-native-image-pan-zoom": {
|
||||||
|
"version": "2.1.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-image-pan-zoom/-/react-native-image-pan-zoom-2.1.12.tgz",
|
||||||
|
"integrity": "sha512-BF66XeP6dzuANsPmmFsJshM2Jyh/Mo1t8FsGc1L9Q9/sVP8MJULDabB1hms+eAoqgtyhMr5BuXV3E1hJ5U5H6Q=="
|
||||||
|
},
|
||||||
|
"react-native-image-zoom-viewer": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-image-zoom-viewer/-/react-native-image-zoom-viewer-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-la6s5DNSuq4GCRLsi5CZ29FPjgTpdCuGIRdO5T9rUrAtxrlpBPhhSnHrbmPVxsdtOUvxHacTh2Gfa9+RraMZQA==",
|
||||||
|
"requires": {
|
||||||
|
"react-native-image-pan-zoom": "^2.1.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-native-iphone-x-helper": {
|
"react-native-iphone-x-helper": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.0.tgz",
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
"react-native": "https://github.com/expo/react-native/archive/sdk-39.0.3.tar.gz",
|
"react-native": "https://github.com/expo/react-native/archive/sdk-39.0.3.tar.gz",
|
||||||
"react-native-gesture-handler": "~1.7.0",
|
"react-native-gesture-handler": "~1.7.0",
|
||||||
"react-native-htmlview": "^0.16.0",
|
"react-native-htmlview": "^0.16.0",
|
||||||
|
"react-native-image-zoom-viewer": "^3.0.1",
|
||||||
"react-native-reanimated": "~1.13.0",
|
"react-native-reanimated": "~1.13.0",
|
||||||
"react-native-safe-area-context": "3.1.4",
|
"react-native-safe-area-context": "3.1.4",
|
||||||
"react-native-screens": "~2.10.1",
|
"react-native-screens": "~2.10.1",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
import React, { useState } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import React from 'react'
|
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { StyleSheet, View } from 'react-native'
|
||||||
|
|
||||||
import Reblog from './TootTimeline/Reblog'
|
import Reblog from './TootTimeline/Reblog'
|
||||||
@ -11,31 +11,25 @@ import Actions from './TootTimeline/Actions'
|
|||||||
// Maybe break away notification types? https://docs.joinmastodon.org/entities/notification/
|
// Maybe break away notification types? https://docs.joinmastodon.org/entities/notification/
|
||||||
|
|
||||||
export default function TootTimeline ({ item, notification }) {
|
export default function TootTimeline ({ item, notification }) {
|
||||||
|
const [viewWidth, setViewWidth] = useState()
|
||||||
|
|
||||||
let contentAggregated = {}
|
let contentAggregated = {}
|
||||||
|
let actualContent
|
||||||
if (notification && item.status) {
|
if (notification && item.status) {
|
||||||
contentAggregated = {
|
actualContent = item.status
|
||||||
content: item.status.content,
|
|
||||||
emojis: item.status.emojis,
|
|
||||||
media_attachments: item.status.media_attachments,
|
|
||||||
mentions: item.status.mentions,
|
|
||||||
tags: item.status.tags
|
|
||||||
}
|
|
||||||
} else if (item.reblog) {
|
} else if (item.reblog) {
|
||||||
contentAggregated = {
|
actualContent = item.reblog
|
||||||
content: item.reblog.content,
|
|
||||||
emojis: item.reblog.emojis,
|
|
||||||
media_attachments: item.reblog.media_attachments,
|
|
||||||
mentions: item.reblog.mentions,
|
|
||||||
tags: item.reblog.tags
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
contentAggregated = {
|
actualContent = item
|
||||||
content: item.content,
|
|
||||||
emojis: item.emojis,
|
|
||||||
media_attachments: item.media_attachments,
|
|
||||||
mentions: item.mentions,
|
|
||||||
tags: item.tags
|
|
||||||
}
|
}
|
||||||
|
contentAggregated = {
|
||||||
|
content: actualContent.content,
|
||||||
|
emojis: actualContent.emojis,
|
||||||
|
media_attachments: actualContent.media_attachments,
|
||||||
|
mentions: actualContent.mentions,
|
||||||
|
sensitive: actualContent.sensitive,
|
||||||
|
spoiler_text: actualContent.spoiler_text,
|
||||||
|
tags: actualContent.tags
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -48,7 +42,10 @@ export default function TootTimeline ({ item, notification }) {
|
|||||||
)}
|
)}
|
||||||
<View style={styles.toot}>
|
<View style={styles.toot}>
|
||||||
<Avatar uri={item.reblog?.account.avatar || item.account.avatar} />
|
<Avatar uri={item.reblog?.account.avatar || item.account.avatar} />
|
||||||
<View style={styles.details}>
|
<View
|
||||||
|
style={styles.details}
|
||||||
|
onLayout={e => setViewWidth(e.nativeEvent.layout.width)}
|
||||||
|
>
|
||||||
<Header
|
<Header
|
||||||
name={
|
name={
|
||||||
(item.reblog?.account.display_name
|
(item.reblog?.account.display_name
|
||||||
@ -63,7 +60,7 @@ export default function TootTimeline ({ item, notification }) {
|
|||||||
created_at={item.created_at}
|
created_at={item.created_at}
|
||||||
application={item.application || null}
|
application={item.application || null}
|
||||||
/>
|
/>
|
||||||
<Content {...contentAggregated} />
|
<Content {...contentAggregated} width={viewWidth} />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Actions />
|
<Actions />
|
||||||
@ -82,7 +79,8 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: 'row'
|
flexDirection: 'row'
|
||||||
},
|
},
|
||||||
details: {
|
details: {
|
||||||
flex: 1
|
flex: 1,
|
||||||
|
flexGrow: 1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
import React from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { StyleSheet, Text } from 'react-native'
|
import {
|
||||||
import { useNavigation } from '@react-navigation/native'
|
Button,
|
||||||
|
Image,
|
||||||
|
Modal,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TouchableHighlight,
|
||||||
|
View
|
||||||
|
} from 'react-native'
|
||||||
import HTMLView from 'react-native-htmlview'
|
import HTMLView from 'react-native-htmlview'
|
||||||
|
import ImageViewer from 'react-native-image-zoom-viewer'
|
||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
|
||||||
import Emojis from './Emojis'
|
import Emojis from './Emojis'
|
||||||
|
|
||||||
@ -67,17 +76,121 @@ function renderNode (navigation, node, index, mentions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 images = []
|
||||||
|
if (width) {
|
||||||
|
const calWidth = i => {
|
||||||
|
if (media_attachments.length === 1) {
|
||||||
|
return { flexGrow: 1, aspectRatio: 16 / 9 }
|
||||||
|
} else if (media_attachments.length === 3 && i === 2) {
|
||||||
|
return { flexGrow: 1, aspectRatio: 16 / 9 }
|
||||||
|
} else {
|
||||||
|
return { flexBasis: width / 2 - 4, aspectRatio: 16 / 9 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<TouchableHighlight
|
||||||
|
key={i}
|
||||||
|
style={calWidth(i)}
|
||||||
|
onPress={() => {
|
||||||
|
setImageModalIndex(i)
|
||||||
|
setImageModalVisible(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
source={{ uri: m.preview_url }}
|
||||||
|
style={styles.image}
|
||||||
|
blurRadius={mediaSensitive ? 50 : 0}
|
||||||
|
/>
|
||||||
|
</TouchableHighlight>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (images) {
|
||||||
|
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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return <View style={styles.media}>{media_attachments}</View>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function Content ({
|
export default function Content ({
|
||||||
content,
|
content,
|
||||||
emojis,
|
emojis,
|
||||||
media_attachments,
|
media_attachments,
|
||||||
mentions,
|
mentions,
|
||||||
tags
|
sensitive,
|
||||||
|
spoiler_text,
|
||||||
|
tags,
|
||||||
|
width
|
||||||
}) {
|
}) {
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
|
|
||||||
return content ? (
|
let fullContent = []
|
||||||
|
if (content) {
|
||||||
|
fullContent.push(
|
||||||
<HTMLView
|
<HTMLView
|
||||||
|
key='content'
|
||||||
value={content}
|
value={content}
|
||||||
renderNode={(node, index) =>
|
renderNode={(node, index) =>
|
||||||
renderNode(navigation, node, index, mentions)
|
renderNode(navigation, node, index, mentions)
|
||||||
@ -86,12 +199,31 @@ export default function Content ({
|
|||||||
<Emojis content={children} emojis={emojis} dimension={14} />
|
<Emojis content={children} emojis={emojis} dimension={14} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
) : (
|
)
|
||||||
<></>
|
}
|
||||||
|
if (media_attachments) {
|
||||||
|
fullContent.push(
|
||||||
|
<Media
|
||||||
|
key='media'
|
||||||
|
media_attachments={media_attachments}
|
||||||
|
sensitive={sensitive}
|
||||||
|
width={width}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fullContent
|
||||||
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
media: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%'
|
||||||
|
},
|
||||||
a: {
|
a: {
|
||||||
color: 'blue'
|
color: 'blue'
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,9 @@ export default function Timeline ({ page, hashtag, list }) {
|
|||||||
<FlatList
|
<FlatList
|
||||||
data={state.toots}
|
data={state.toots}
|
||||||
keyExtractor={({ id }) => id}
|
keyExtractor={({ id }) => id}
|
||||||
renderItem={TootTimeline}
|
renderItem={({ item, index, separators }) => (
|
||||||
|
<TootTimeline key={item.key} item={item} />
|
||||||
|
)}
|
||||||
onRefresh={() =>
|
onRefresh={() =>
|
||||||
dispatch(fetch({ page, query: { since_id: state.toots[0].id } }))
|
dispatch(fetch({ page, query: { since_id: state.toots[0].id } }))
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user