mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Commit
This commit is contained in:
@ -4,7 +4,7 @@ import { StyleSheet, Text } from 'react-native'
|
||||
import HTMLView from 'react-native-htmlview'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
|
||||
import Emojis from 'src/components/TootTimeline/Emojis'
|
||||
import Emojis from 'src/components/Toot/Emojis'
|
||||
|
||||
function renderNode ({ node, index, navigation, mentions, showFullLink }) {
|
||||
if (node.name == 'a') {
|
||||
@ -72,7 +72,8 @@ export default function ParseContent ({
|
||||
emojis,
|
||||
emojiSize = 14,
|
||||
mentions,
|
||||
showFullLink = false
|
||||
showFullLink = false,
|
||||
linesTruncated = 10
|
||||
}) {
|
||||
const navigation = useNavigation()
|
||||
|
||||
@ -80,13 +81,16 @@ export default function ParseContent ({
|
||||
<HTMLView
|
||||
value={content}
|
||||
stylesheet={HTMLstyles}
|
||||
addLineBreaks={null}
|
||||
paragraphBreak={null}
|
||||
renderNode={(node, index) =>
|
||||
renderNode({ node, index, navigation, mentions, showFullLink })
|
||||
}
|
||||
TextComponent={({ children }) => (
|
||||
<Emojis content={children} emojis={emojis} dimension={emojiSize} />
|
||||
)}
|
||||
RootComponent={({ children }) => {
|
||||
return <Text numberOfLines={linesTruncated}>{children}</Text>
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -115,5 +119,6 @@ ParseContent.propTypes = {
|
||||
acct: PropTypes.string.isRequired
|
||||
})
|
||||
),
|
||||
showFullLink: PropTypes.bool
|
||||
showFullLink: PropTypes.bool,
|
||||
linesTruncated: PropTypes.number
|
||||
}
|
||||
|
129
src/components/Toot.jsx
Normal file
129
src/components/Toot.jsx
Normal file
@ -0,0 +1,129 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Dimensions, Pressable, StyleSheet, View } from 'react-native'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
|
||||
import Reblog from './Toot/Reblog'
|
||||
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 Card from './Toot/Card'
|
||||
import Actions from './Toot/Actions'
|
||||
|
||||
// Maybe break away notification types? https://docs.joinmastodon.org/entities/notification/
|
||||
|
||||
export default function Toot ({ item, notification }) {
|
||||
const navigation = useNavigation()
|
||||
|
||||
let actualContent
|
||||
if (notification && item.status) {
|
||||
actualContent = item.status
|
||||
} else if (item.reblog) {
|
||||
actualContent = item.reblog
|
||||
} else {
|
||||
actualContent = item
|
||||
}
|
||||
|
||||
const toot = useMemo(() => {
|
||||
return (
|
||||
<View style={styles.tootTimeline}>
|
||||
{item.reblog && (
|
||||
<Reblog
|
||||
name={item.account.display_name || item.account.username}
|
||||
emojis={item.account.emojis}
|
||||
/>
|
||||
)}
|
||||
<View style={styles.toot}>
|
||||
<Avatar
|
||||
uri={actualContent.account.avatar}
|
||||
id={actualContent.account.id}
|
||||
/>
|
||||
<View style={styles.details}>
|
||||
<Header
|
||||
name={
|
||||
actualContent.account.display_name ||
|
||||
actualContent.account.username
|
||||
}
|
||||
emojis={actualContent.account.emojis}
|
||||
account={actualContent.account.acct}
|
||||
created_at={item.created_at}
|
||||
application={item.application}
|
||||
/>
|
||||
{/* Can pass toot info to next page to speed up performance */}
|
||||
<Pressable
|
||||
onPress={() =>
|
||||
navigation.navigate('Toot', { toot: actualContent.id })
|
||||
}
|
||||
>
|
||||
{actualContent.content ? (
|
||||
<Content
|
||||
content={actualContent.content}
|
||||
emojis={actualContent.emojis}
|
||||
mentions={actualContent.mentions}
|
||||
spoiler_text={actualContent.spoiler_text}
|
||||
tags={actualContent.tags}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{actualContent.poll && <Poll poll={actualContent.poll} />}
|
||||
{actualContent.media_attachments && (
|
||||
<Media
|
||||
media_attachments={actualContent.media_attachments}
|
||||
sensitive={actualContent.sensitive}
|
||||
width={Dimensions.get('window').width - 24 - 50 - 8}
|
||||
/>
|
||||
)}
|
||||
{actualContent.card && <Card card={actualContent.card} />}
|
||||
</Pressable>
|
||||
<Actions
|
||||
replies_count={actualContent.replies_count}
|
||||
reblogs_count={actualContent.reblogs_count}
|
||||
reblogged={actualContent.reblogged}
|
||||
favourites_count={actualContent.favourites_count}
|
||||
favourited={actualContent.favourited}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
})
|
||||
|
||||
return toot
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
tootTimeline: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
padding: 12
|
||||
},
|
||||
toot: {
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
details: {
|
||||
flex: 1,
|
||||
flexGrow: 1
|
||||
}
|
||||
})
|
||||
|
||||
Toot.propTypes = {
|
||||
item: PropTypes.shape({
|
||||
account: PropTypes.shape({
|
||||
avatar: PropTypes.string.isRequired,
|
||||
display_name: PropTypes.string.isRequired,
|
||||
acct: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
created_at: PropTypes.string.isRequired,
|
||||
application: PropTypes.exact({
|
||||
name: PropTypes.string.isRequired,
|
||||
website: PropTypes.string
|
||||
}),
|
||||
content: PropTypes.string
|
||||
}).isRequired,
|
||||
notification: PropTypes.bool
|
||||
}
|
48
src/components/Toot/Actions.jsx
Normal file
48
src/components/Toot/Actions.jsx
Normal file
@ -0,0 +1,48 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
||||
import { Feather } from '@expo/vector-icons'
|
||||
|
||||
export default function Actions ({
|
||||
replies_count,
|
||||
reblogs_count,
|
||||
reblogged,
|
||||
favourites_count,
|
||||
favourited
|
||||
}) {
|
||||
return (
|
||||
<View style={styles.actions}>
|
||||
<Pressable style={styles.action}>
|
||||
<Feather name='message-circle' />
|
||||
<Text>{replies_count}</Text>
|
||||
</Pressable>
|
||||
<Pressable style={styles.action}>
|
||||
<Feather name='repeat' />
|
||||
<Text>{reblogs_count}</Text>
|
||||
</Pressable>
|
||||
<Pressable style={styles.action}>
|
||||
<Feather name='heart' />
|
||||
<Text>{favourites_count}</Text>
|
||||
</Pressable>
|
||||
<Pressable style={styles.action}>
|
||||
<Feather name='share' />
|
||||
</Pressable>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
actions: {
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
action: {
|
||||
width: '25%',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center'
|
||||
}
|
||||
})
|
||||
|
||||
// Actions.propTypes = {
|
||||
// uri: PropTypes.string
|
||||
// }
|
73
src/components/Toot/Card.jsx
Normal file
73
src/components/Toot/Card.jsx
Normal file
@ -0,0 +1,73 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Image, Pressable, StyleSheet, Text, View } from 'react-native'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
|
||||
export default function Card ({ card }) {
|
||||
const navigation = useNavigation()
|
||||
return (
|
||||
card && (
|
||||
<Pressable
|
||||
style={styles.card}
|
||||
onPress={() => {
|
||||
navigation.navigate('Webview', {
|
||||
uri: card.url
|
||||
})
|
||||
}}
|
||||
>
|
||||
{card.image && (
|
||||
<View style={styles.left}>
|
||||
<Image source={{ uri: card.image }} style={styles.image} />
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.right}>
|
||||
<Text numberOfLines={1}>{card.title}</Text>
|
||||
{card.description ? (
|
||||
<Text numberOfLines={2}>{card.description}</Text>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<Text numberOfLines={1}>{card.url}</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
height: 70,
|
||||
marginTop: 12
|
||||
},
|
||||
left: {
|
||||
width: 70
|
||||
},
|
||||
image: {
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
},
|
||||
right: {
|
||||
flex: 1
|
||||
}
|
||||
})
|
||||
|
||||
Card.propTypes = {
|
||||
card: PropTypes.exact({
|
||||
url: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
description: PropTypes.string,
|
||||
type: PropTypes.oneOf(['link', 'photo', 'video']),
|
||||
author_name: PropTypes.string,
|
||||
author_url: PropTypes.string,
|
||||
provider_name: PropTypes.string,
|
||||
provider_url: PropTypes.string,
|
||||
html: PropTypes.string,
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
image: PropTypes.string,
|
||||
embed_url: PropTypes.string,
|
||||
blurhash: PropTypes.string
|
||||
}).isRequired
|
||||
}
|
48
src/components/Toot/Content.jsx
Normal file
48
src/components/Toot/Content.jsx
Normal file
@ -0,0 +1,48 @@
|
||||
import React, { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Text } from 'react-native'
|
||||
import Collapsible from 'react-native-collapsible'
|
||||
|
||||
import ParseContent from 'src/components/ParseContent'
|
||||
|
||||
export default function Content ({ content, emojis, mentions, spoiler_text }) {
|
||||
const [spoilerCollapsed, setSpoilerCollapsed] = useState(true)
|
||||
|
||||
return (
|
||||
<>
|
||||
{content &&
|
||||
(spoiler_text ? (
|
||||
<>
|
||||
<Text>
|
||||
{spoiler_text}{' '}
|
||||
<Text onPress={() => setSpoilerCollapsed(!spoilerCollapsed)}>
|
||||
点击展开
|
||||
</Text>
|
||||
</Text>
|
||||
<Collapsible collapsed={spoilerCollapsed}>
|
||||
<ParseContent
|
||||
content={content}
|
||||
emojis={emojis}
|
||||
emojiSize={14}
|
||||
mentions={mentions}
|
||||
/>
|
||||
</Collapsible>
|
||||
</>
|
||||
) : (
|
||||
<ParseContent
|
||||
content={content}
|
||||
emojis={emojis}
|
||||
emojiSize={14}
|
||||
mentions={mentions}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Content.propTypes = {
|
||||
content: ParseContent.propTypes.content,
|
||||
emojis: ParseContent.propTypes.emojis,
|
||||
mentions: ParseContent.propTypes.mentions,
|
||||
spoiler_text: PropTypes.string
|
||||
}
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Image, Text } from 'react-native'
|
||||
|
||||
const regexEmoji = new RegExp(/(:.*?:)/g)
|
||||
const regexEmoji = new RegExp(/(:[a-z0-9_]+:)/)
|
||||
|
||||
export default function Emojis ({ content, emojis, dimension }) {
|
||||
const hasEmojis = content.match(regexEmoji)
|
||||
@ -13,7 +13,11 @@ export default function Emojis ({ content, emojis, dimension }) {
|
||||
const emojiIndex = emojis.findIndex(emoji => {
|
||||
return emojiShortcode === `:${emoji.shortcode}:`
|
||||
})
|
||||
return (
|
||||
return emojiIndex === -1 ? (
|
||||
<Text key={i} style={{ color: 'red' }}>
|
||||
Something wrong with emoji!
|
||||
</Text>
|
||||
) : (
|
||||
<Image
|
||||
key={i}
|
||||
source={{ uri: emojis[emojiIndex].url }}
|
@ -27,7 +27,9 @@ export default function Header ({
|
||||
<View style={styles.name}>
|
||||
<Emojis content={name} emojis={emojis} dimension={14} />
|
||||
</View>
|
||||
<Text style={styles.account}>@{account}</Text>
|
||||
<Text style={styles.account} numberOfLines={1}>
|
||||
@{account}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.meta}>
|
||||
<View>
|
@ -9,12 +9,9 @@ import {
|
||||
Pressable,
|
||||
View
|
||||
} from 'react-native'
|
||||
import Collapsible from 'react-native-collapsible'
|
||||
import ImageViewer from 'react-native-image-zoom-viewer'
|
||||
|
||||
import ParseContent from 'src/components/ParseContent'
|
||||
|
||||
function Media ({ media_attachments, sensitive, width }) {
|
||||
export default function Media ({ media_attachments, sensitive, width }) {
|
||||
const [mediaSensitive, setMediaSensitive] = useState(sensitive)
|
||||
const [imageModalVisible, setImageModalVisible] = useState(false)
|
||||
const [imageModalIndex, setImageModalIndex] = useState(0)
|
||||
@ -26,6 +23,7 @@ function Media ({ media_attachments, sensitive, width }) {
|
||||
}
|
||||
}, [mediaSensitive])
|
||||
|
||||
let media
|
||||
let images = []
|
||||
if (width) {
|
||||
media_attachments = media_attachments.map((m, i) => {
|
||||
@ -41,7 +39,7 @@ function Media ({ media_attachments, sensitive, width }) {
|
||||
return (
|
||||
<Pressable
|
||||
key={i}
|
||||
style={{ flexGrow: 1, height: width / 5, margin: 4 }}
|
||||
style={{ flexGrow: 1, height: width / 2, margin: 4 }}
|
||||
onPress={() => {
|
||||
setImageModalIndex(i)
|
||||
setImageModalVisible(true)
|
||||
@ -57,7 +55,7 @@ function Media ({ media_attachments, sensitive, width }) {
|
||||
}
|
||||
})
|
||||
if (images) {
|
||||
return (
|
||||
media = (
|
||||
<>
|
||||
<View style={styles.media}>
|
||||
{media_attachments}
|
||||
@ -95,69 +93,25 @@ function Media ({ media_attachments, sensitive, width }) {
|
||||
</>
|
||||
)
|
||||
} else {
|
||||
return <View style={styles.media}>{media_attachments}</View>
|
||||
media = <View style={styles.media}>{media_attachments}</View>
|
||||
}
|
||||
} else {
|
||||
return <></>
|
||||
media = <></>
|
||||
}
|
||||
}
|
||||
|
||||
export default function Content ({
|
||||
content,
|
||||
emojis,
|
||||
media_attachments,
|
||||
mentions,
|
||||
sensitive,
|
||||
spoiler_text,
|
||||
width
|
||||
}) {
|
||||
const [spoilerCollapsed, setSpoilerCollapsed] = useState(true)
|
||||
|
||||
return (
|
||||
<>
|
||||
{content &&
|
||||
(spoiler_text ? (
|
||||
<>
|
||||
<Text>
|
||||
{spoiler_text}{' '}
|
||||
<Text onPress={() => setSpoilerCollapsed(!spoilerCollapsed)}>
|
||||
点击展开
|
||||
</Text>
|
||||
</Text>
|
||||
<Collapsible collapsed={spoilerCollapsed}>
|
||||
<ParseContent
|
||||
content={content}
|
||||
emojis={emojis}
|
||||
emojiSize={14}
|
||||
mentions={mentions}
|
||||
/>
|
||||
</Collapsible>
|
||||
</>
|
||||
) : (
|
||||
<ParseContent
|
||||
content={content}
|
||||
emojis={emojis}
|
||||
emojiSize={14}
|
||||
mentions={mentions}
|
||||
/>
|
||||
))}
|
||||
{media_attachments.length > 0 && (
|
||||
<View
|
||||
style={{
|
||||
width: width + 8,
|
||||
height: width / 2,
|
||||
marginTop: 4,
|
||||
marginLeft: -4
|
||||
}}
|
||||
>
|
||||
<Media
|
||||
media_attachments={media_attachments}
|
||||
sensitive={sensitive}
|
||||
width={width}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
media_attachments.length > 0 && (
|
||||
<View
|
||||
style={{
|
||||
width: width + 8,
|
||||
height: width / 2,
|
||||
marginTop: 4,
|
||||
marginLeft: -4
|
||||
}}
|
||||
>
|
||||
{media}
|
||||
</View>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -176,12 +130,8 @@ const styles = StyleSheet.create({
|
||||
}
|
||||
})
|
||||
|
||||
Content.propTypes = {
|
||||
content: ParseContent.propTypes.content,
|
||||
emojis: ParseContent.propTypes.emojis,
|
||||
Media.propTypes = {
|
||||
// media_attachments
|
||||
mentions: ParseContent.propTypes.mentions,
|
||||
sensitive: PropTypes.bool.isRequired,
|
||||
spoiler_text: PropTypes.string,
|
||||
width: PropTypes.number.isRequired
|
||||
}
|
63
src/components/Toot/Poll.jsx
Normal file
63
src/components/Toot/Poll.jsx
Normal file
@ -0,0 +1,63 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
import Emojis from './Emojis'
|
||||
|
||||
export default function Poll ({ poll }) {
|
||||
return (
|
||||
<View>
|
||||
{poll.options.map((option, index) => (
|
||||
<View key={index}>
|
||||
<View style={{ flexDirection: 'row' }}>
|
||||
<Text>
|
||||
{Math.round((option.votes_count / poll.votes_count) * 100)}%
|
||||
</Text>
|
||||
<Text>{option.title}</Text>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
width: `${Math.round(
|
||||
(option.votes_count / poll.votes_count) * 100
|
||||
)}%`,
|
||||
height: 5,
|
||||
backgroundColor: 'blue'
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
avatar: {
|
||||
width: 50,
|
||||
height: 50,
|
||||
marginRight: 8
|
||||
},
|
||||
image: {
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}
|
||||
})
|
||||
|
||||
Poll.propTypes = {
|
||||
poll: PropTypes.exact({
|
||||
id: PropTypes.string.isRequired,
|
||||
expires_at: PropTypes.string.isRequired,
|
||||
expired: PropTypes.bool.isRequired,
|
||||
multiple: PropTypes.bool.isRequired,
|
||||
votes_count: PropTypes.number,
|
||||
voters_count: PropTypes.number,
|
||||
voted: PropTypes.bool.isRequired,
|
||||
own_votes: PropTypes.array,
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.exact({
|
||||
title: PropTypes.string.isRequired,
|
||||
votes_count: PropTypes.number.isRequired
|
||||
})
|
||||
),
|
||||
emojis: Emojis.propTypes.emojis
|
||||
}).isRequired
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
import React, { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Dimensions, StyleSheet, View } from 'react-native'
|
||||
|
||||
import Reblog from './TootTimeline/Reblog'
|
||||
import Avatar from './TootTimeline/Avatar'
|
||||
import Header from './TootTimeline/Header'
|
||||
import Content from './TootTimeline/Content'
|
||||
import Actions from './TootTimeline/Actions'
|
||||
|
||||
// Maybe break away notification types? https://docs.joinmastodon.org/entities/notification/
|
||||
|
||||
export default function TootTimeline ({ item, notification }) {
|
||||
let contentAggregated = {}
|
||||
let actualContent
|
||||
if (notification && item.status) {
|
||||
actualContent = item.status
|
||||
} else if (item.reblog) {
|
||||
actualContent = item.reblog
|
||||
} else {
|
||||
actualContent = item
|
||||
}
|
||||
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 (
|
||||
<View style={styles.tootTimeline}>
|
||||
{item.reblog && (
|
||||
<Reblog
|
||||
name={item.account.display_name || item.account.username}
|
||||
emojis={item.account.emojis}
|
||||
/>
|
||||
)}
|
||||
<View style={styles.toot}>
|
||||
<Avatar
|
||||
uri={item.reblog?.account.avatar || item.account.avatar}
|
||||
id={item.reblog?.account.id || item.account.id}
|
||||
/>
|
||||
<View style={styles.details}>
|
||||
<Header
|
||||
name={
|
||||
(item.reblog?.account.display_name
|
||||
? item.reblog?.account.display_name
|
||||
: item.reblog?.account.username) ||
|
||||
(item.account.display_name
|
||||
? item.account.display_name
|
||||
: item.account.username)
|
||||
}
|
||||
emojis={item.reblog?.account.emojis || item.account.emojis}
|
||||
account={item.reblog?.account.acct || item.account.acct}
|
||||
created_at={item.created_at}
|
||||
application={item.application || null}
|
||||
/>
|
||||
<Content
|
||||
{...contentAggregated}
|
||||
style={{ flex: 1 }}
|
||||
width={Dimensions.get('window').width - 24 - 50 - 8}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<Actions />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
tootTimeline: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
padding: 12
|
||||
},
|
||||
toot: {
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
details: {
|
||||
flex: 1,
|
||||
flexGrow: 1
|
||||
}
|
||||
})
|
||||
|
||||
TootTimeline.propTypes = {
|
||||
item: PropTypes.shape({
|
||||
account: PropTypes.shape({
|
||||
avatar: PropTypes.string.isRequired,
|
||||
display_name: PropTypes.string.isRequired,
|
||||
acct: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
created_at: PropTypes.string.isRequired,
|
||||
application: PropTypes.exact({
|
||||
name: PropTypes.string.isRequired,
|
||||
website: PropTypes.string
|
||||
}),
|
||||
content: PropTypes.string
|
||||
}).isRequired,
|
||||
notification: PropTypes.bool
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StyleSheet } from 'react-native'
|
||||
|
||||
export default function Actions () {
|
||||
return <></>
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
width: 50,
|
||||
height: 50
|
||||
})
|
||||
|
||||
// Actions.propTypes = {
|
||||
// uri: PropTypes.string
|
||||
// }
|
Reference in New Issue
Block a user