Add centrally located prop-types

This commit is contained in:
Zhiyuan Zheng 2020-10-30 00:10:25 +01:00
parent 2ab227a370
commit 301772e735
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
20 changed files with 353 additions and 75 deletions

View File

@ -1,5 +1,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import propTypesEmoji from 'src/prop-types/emoji'
import propTypesMention from 'src/prop-types/mention'
import { StyleSheet, Text } from 'react-native'
import HTMLView from 'react-native-htmlview'
import { useNavigation } from '@react-navigation/native'
@ -109,16 +111,9 @@ const HTMLstyles = StyleSheet.create({
ParseContent.propTypes = {
content: PropTypes.string.isRequired,
emojis: Emojis.propTypes.emojis,
emojis: PropTypes.arrayOf(propTypesEmoji),
emojiSize: PropTypes.number,
mentions: PropTypes.arrayOf(
PropTypes.exact({
id: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
acct: PropTypes.string.isRequired
})
),
mentions: PropTypes.arrayOf(propTypesMention),
showFullLink: PropTypes.bool,
linesTruncated: PropTypes.number
}

View File

@ -1,5 +1,5 @@
import React from 'react'
import PropTypes from 'prop-types'
import propTypesCard from 'src/prop-types/card'
import { Image, Pressable, StyleSheet, Text, View } from 'react-native'
import { useNavigation } from '@react-navigation/native'
@ -54,20 +54,5 @@ const styles = StyleSheet.create({
})
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
card: propTypesCard
}

View File

@ -1,5 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import propTypesEmoji from 'src/prop-types/emoji'
import { Image, Text } from 'react-native'
const regexEmoji = new RegExp(/(:[a-z0-9_]+:)/)
@ -44,13 +45,5 @@ export default function Emojis ({ content, emojis, dimension }) {
Emojis.propTypes = {
content: PropTypes.string.isRequired,
emojis: PropTypes.arrayOf(
PropTypes.exact({
shortcode: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
static_url: PropTypes.string.isRequired,
visible_in_picker: PropTypes.bool.isRequired,
category: PropTypes.string
})
)
emojis: PropTypes.arrayOf(propTypesEmoji)
}

View File

@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import propTypesAttachment from 'src/prop-types/attachment'
import {
Button,
Image,
@ -131,7 +132,7 @@ const styles = StyleSheet.create({
})
Media.propTypes = {
// media_attachments
media_attachments: PropTypes.arrayOf(propTypesAttachment),
sensitive: PropTypes.bool.isRequired,
width: PropTypes.number.isRequired
}

View File

@ -1,5 +1,5 @@
import React from 'react'
import PropTypes from 'prop-types'
import propTypesPoll from 'src/prop-types/poll'
import { StyleSheet, Text, View } from 'react-native'
import Emojis from './Emojis'
@ -43,21 +43,5 @@ const styles = StyleSheet.create({
})
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
poll: propTypesPoll
}

View File

@ -1,5 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import propTypesEmoji from 'src/prop-types/emoji'
import { StyleSheet, Text, View } from 'react-native'
import { Feather } from '@expo/vector-icons'
@ -32,5 +33,5 @@ const styles = StyleSheet.create({
Reblog.propTypes = {
name: PropTypes.string.isRequired,
emojis: Emojis.propTypes.emojis
emojis: PropTypes.arrayOf(propTypesEmoji)
}

View File

@ -1,5 +1,6 @@
import React, { useMemo } from 'react'
import PropTypes from 'prop-types'
import propTypesNotification from 'src/prop-types/notification'
import { Dimensions, Pressable, StyleSheet, View } from 'react-native'
import { useNavigation } from '@react-navigation/native'
@ -12,7 +13,7 @@ import Media from './Toot/Media'
import Card from './Toot/Card'
import Actions from './Toot/Actions'
export default function Toot ({ toot }) {
export default function TootNotification ({ toot }) {
const navigation = useNavigation()
let actualContent
@ -107,18 +108,6 @@ const styles = StyleSheet.create({
}
})
Toot.propTypes = {
toot: 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
TootNotification.propTypes = {
toot: propTypesNotification
}

View File

@ -0,0 +1,112 @@
import React, { useMemo } from 'react'
import propTypesStatus from 'src/prop-types/status'
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'
export default function TootTimeline ({ toot }) {
const navigation = useNavigation()
let actualContent
if (toot.reblog) {
actualContent = toot.reblog
} else {
actualContent = toot
}
const tootView = useMemo(() => {
return (
<View style={styles.tootTimeline}>
{toot.reblog && (
<Reblog
name={toot.account.display_name || toot.account.username}
emojis={toot.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={toot.created_at}
application={toot.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 tootView
}
const styles = StyleSheet.create({
tootTimeline: {
flex: 1,
flexDirection: 'column',
padding: 12
},
toot: {
flex: 1,
flexDirection: 'row'
},
details: {
flex: 1,
flexGrow: 1
}
})
TootTimeline.propTypes = {
toot: propTypesStatus
}

37
src/prop-types/account.js Normal file
View File

@ -0,0 +1,37 @@
import PropTypes from 'prop-types'
import propTypesEmoji from './emoji'
import propTypesStatus from './status'
const propTypesAccount = PropTypes.shape({
// Base
id: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
acct: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
// Attributes
display_name: PropTypes.string.isRequired,
note: PropTypes.string,
avatar: PropTypes.string.isRequired,
avatar_static: PropTypes.string.isRequired,
header: PropTypes.string.isRequired,
header_static: PropTypes.string.isRequired,
locked: PropTypes.bool.isRequired,
emojis: PropTypes.arrayOf(propTypesEmoji),
discoverable: PropTypes.bool.isRequired,
// Statistics
created_at: PropTypes.string.isRequired,
last_status_at: PropTypes.string.isRequired,
statuses_count: PropTypes.number.isRequired,
followers_count: PropTypes.number.isRequired,
following_count: PropTypes.number.isRequired,
// Others
moved: propTypesStatus,
// fields prop-types
bot: PropTypes.bool.isRequired
// source prop-types
})
export default propTypesAccount

View File

@ -0,0 +1,10 @@
import PropTypes from 'prop-types'
const propTypesApplication = PropTypes.shape({
// Base
name: PropTypes.string.isRequired,
website: PropTypes.string,
vapid_key: PropTypes.string
})
export default propTypesApplication

View File

@ -0,0 +1,19 @@
import PropTypes from 'prop-types'
const propTypesAttachment = PropTypes.shape({
// Base
id: PropTypes.string.isRequired,
type: PropTypes.oneOf(['unknown', 'image', 'gifv', 'video', 'audio'])
.isRequired,
url: PropTypes.string.isRequired,
preview_url: PropTypes.string.isRequired,
// Others
remote_url: PropTypes.string,
text_url: PropTypes.string,
meta: PropTypes.object,
description: PropTypes.string,
blurhash: PropTypes.string
})
export default propTypesAttachment

23
src/prop-types/card.js Normal file
View File

@ -0,0 +1,23 @@
import PropTypes from 'prop-types'
const propTypesCard = PropTypes.shape({
// Base
url: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
type: PropTypes.oneOf(['link', 'photo', 'video', 'rich']).isRequired,
// Attributes
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
})
export default propTypesCard

12
src/prop-types/emoji.js Normal file
View File

@ -0,0 +1,12 @@
import PropTypes from 'prop-types'
const propTypesEmoji = PropTypes.shape({
// Base
shortcode: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
static_url: PropTypes.string.isRequired,
visible_in_picker: PropTypes.bool.isRequired,
category: PropTypes.string
})
export default propTypesEmoji

11
src/prop-types/mention.js Normal file
View File

@ -0,0 +1,11 @@
import PropTypes from 'prop-types'
const propTypesMention = PropTypes.shape({
// Base
id: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
acct: PropTypes.string.isRequired,
url: PropTypes.string.isRequired
})
export default propTypesMention

View File

@ -0,0 +1,17 @@
import PropTypes from 'prop-types'
import propTypesAccount from './account'
import propTypesStatus from './status'
const propTypesNotification = PropTypes.shape({
// Base
id: PropTypes.string.isRequired,
type: PropTypes.oneOf(['follow', 'mention', 'reblog', 'favourite', 'poll'])
.isRequired,
created_at: PropTypes.string.isRequired,
account: propTypesAccount,
// Others
status: propTypesStatus
})
export default propTypesNotification

23
src/prop-types/poll.js Normal file
View File

@ -0,0 +1,23 @@
import PropTypes from 'prop-types'
import propTypesEmoji from './emoji'
const propTypesPoll = PropTypes.shape({
// Base
id: PropTypes.string.isRequired,
expires_at: PropTypes.string.isRequired,
expired: PropTypes.bool.isRequired,
multiple: PropTypes.bool.isRequired,
votes_count: PropTypes.number.isRequired,
voters_count: PropTypes.number.isRequired,
voted: PropTypes.bool,
own_votes: PropTypes.arrayOf(PropTypes.number),
options: PropTypes.arrayOf(
PropTypes.exact({
title: PropTypes.string.isRequired,
votes_count: PropTypes.number.isRequired
})
).isRequired,
emojis: PropTypes.arrayOf(propTypesEmoji)
})
export default propTypesPoll

51
src/prop-types/status.js Normal file
View File

@ -0,0 +1,51 @@
import PropTypes from 'prop-types'
import propTypesAccount from './account'
import propTypesAttachment from './attachment'
import propTypesApplication from './application'
import propTypesMention from './mention'
import propTypesTag from './tag'
import propTypesEmoji from './emoji'
import propTypesPoll from './poll'
import propTypesCard from './card'
const propTypesStatus = PropTypes.shape({
// Base
id: PropTypes.string.isRequired,
uri: PropTypes.string.isRequired,
created_at: PropTypes.string.isRequired,
account: propTypesAccount,
content: PropTypes.string.isRequired, // Might not be required
visibility: PropTypes.oneOf(['public', 'unlisted', 'private', 'direct'])
.isRequired,
sensitive: PropTypes.bool.isRequired,
spoiler_text: PropTypes.string,
media_attachments: PropTypes.arrayOf(propTypesAttachment),
application: propTypesApplication,
// Attributes
mentions: PropTypes.arrayOf(propTypesMention),
tags: PropTypes.arrayOf(propTypesTag),
emojis: PropTypes.arrayOf(propTypesEmoji),
// Interaction
reblogs_count: PropTypes.number.isRequired,
favourites_count: PropTypes.number.isRequired,
replies_count: PropTypes.number.isRequired,
favourited: PropTypes.bool,
reblogged: PropTypes.bool,
muted: PropTypes.bool,
bookmarked: PropTypes.bool,
pinned: PropTypes.bool,
// Others
url: PropTypes.string,
in_reply_to_id: PropTypes.string,
in_reply_to_account_id: PropTypes.string,
reblog: propTypesStatus,
poll: propTypesPoll,
card: propTypesCard,
language: PropTypes.string,
text: PropTypes.string
})
export default propTypesStatus

10
src/prop-types/tag.js Normal file
View File

@ -0,0 +1,10 @@
import PropTypes from 'prop-types'
const propTypesTag = PropTypes.shape({
// Base
name: PropTypes.string.isRequired,
url: PropTypes.string.isRequired
// history prop-types
})
export default propTypesTag

View File

@ -26,7 +26,7 @@ export default function Notifications () {
}}
>
<Stack.Screen name='Notifications'>
{props => <Timeline page='Notifications' {...props} />}
{() => <Timeline page='Notifications' />}
</Stack.Screen>
</Stack.Navigator>
)

View File

@ -3,7 +3,8 @@ import PropTypes from 'prop-types'
import { ActivityIndicator, FlatList, Text, View } from 'react-native'
import { useSelector, useDispatch } from 'react-redux'
import Toot from 'src/components/Toot'
import TootNotification from 'src/components/TootNotification'
import TootTimeline from 'src/components/TootTimeline'
import { fetch } from './timelineSlice'
// Opening nesting hashtag pages
@ -37,9 +38,13 @@ export default function Timeline ({
style={{ minHeight: '100%' }}
data={state.toots}
keyExtractor={({ id }) => id}
renderItem={({ item, index, separators }) => (
<Toot key={item.key} toot={item} />
)}
renderItem={({ item, index, separators }) =>
page === 'Notifications' ? (
<TootNotification key={item.key} toot={item} />
) : (
<TootTimeline key={item.key} toot={item} />
)
}
{...(state.pointer && { initialScrollIndex: state.pointer })}
{...(!disableRefresh && {
onRefresh: () =>