mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Notification working and end of list working
This commit is contained in:
15
src/@types/mastodon.d.ts
vendored
15
src/@types/mastodon.d.ts
vendored
@ -267,6 +267,21 @@ declare namespace Mastodon {
|
|||||||
'reading:expand:spoilers'?: boolean
|
'reading:expand:spoilers'?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Relationship = {
|
||||||
|
id: string
|
||||||
|
following: boolean
|
||||||
|
showing_reblogs: boolean
|
||||||
|
followed_by: boolean
|
||||||
|
blocking: boolean
|
||||||
|
blocked_by: boolean
|
||||||
|
muting: boolean
|
||||||
|
muting_notifications: boolean
|
||||||
|
requested: boolean
|
||||||
|
domain_blocking: boolean
|
||||||
|
endorsed: boolean
|
||||||
|
note: string
|
||||||
|
}
|
||||||
|
|
||||||
type Results = {
|
type Results = {
|
||||||
accounts?: Account[]
|
accounts?: Account[]
|
||||||
statuses?: Status[]
|
statuses?: Status[]
|
||||||
|
@ -8,6 +8,7 @@ import TimelineConversation from 'src/components/Timelines/Timeline/Conversation
|
|||||||
import { timelineFetch } from 'src/utils/fetches/timelineFetch'
|
import { timelineFetch } from 'src/utils/fetches/timelineFetch'
|
||||||
import TimelineSeparator from './Timeline/Separator'
|
import TimelineSeparator from './Timeline/Separator'
|
||||||
import TimelineEmpty from './Timeline/Empty'
|
import TimelineEmpty from './Timeline/Empty'
|
||||||
|
import TimelineEnd from './Timeline/Shared/End'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
page: App.Pages
|
page: App.Pages
|
||||||
@ -44,10 +45,13 @@ const Timeline: React.FC<Props> = ({
|
|||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
isFetchingMore,
|
isFetchingMore,
|
||||||
|
canFetchMore,
|
||||||
data,
|
data,
|
||||||
fetchMore,
|
fetchMore,
|
||||||
refetch
|
refetch
|
||||||
} = useInfiniteQuery(queryKey, timelineFetch)
|
} = useInfiniteQuery(queryKey, timelineFetch, {
|
||||||
|
getFetchMore: last => last?.toots.length > 0
|
||||||
|
})
|
||||||
const flattenData = data ? data.flatMap(d => [...d?.toots]) : []
|
const flattenData = data ? data.flatMap(d => [...d?.toots]) : []
|
||||||
const flattenPointer = data ? data.flatMap(d => [d?.pointer]) : []
|
const flattenPointer = data ? data.flatMap(d => [d?.pointer]) : []
|
||||||
|
|
||||||
@ -90,6 +94,7 @@ const Timeline: React.FC<Props> = ({
|
|||||||
const flOnEndReach = useCallback(
|
const flOnEndReach = useCallback(
|
||||||
() =>
|
() =>
|
||||||
!disableRefresh &&
|
!disableRefresh &&
|
||||||
|
canFetchMore &&
|
||||||
fetchMore({
|
fetchMore({
|
||||||
direction: 'next',
|
direction: 'next',
|
||||||
id: flattenData[flattenData.length - 1].id
|
id: flattenData[flattenData.length - 1].id
|
||||||
@ -100,7 +105,7 @@ const Timeline: React.FC<Props> = ({
|
|||||||
if (isFetchingMore) {
|
if (isFetchingMore) {
|
||||||
return <ActivityIndicator />
|
return <ActivityIndicator />
|
||||||
} else {
|
} else {
|
||||||
return null
|
return <TimelineEnd />
|
||||||
}
|
}
|
||||||
}, [isFetchingMore])
|
}, [isFetchingMore])
|
||||||
const onScrollToIndexFailed = useCallback(error => {
|
const onScrollToIndexFailed = useCallback(error => {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React, { useMemo } from 'react'
|
import React from 'react'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, StyleSheet, View } from 'react-native'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
|
||||||
import TimelineAvatar from './Shared/Avatar'
|
import TimelineAvatar from './Shared/Avatar'
|
||||||
import HeaderConversation from './Shared/HeaderConversation'
|
import TimelineHeaderConversation from './Shared/HeaderConversation'
|
||||||
import TimelineContent from './Shared/Content'
|
import TimelineContent from './Shared/Content'
|
||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
|
|
||||||
@ -14,13 +14,12 @@ export interface Props {
|
|||||||
const TimelineConversation: React.FC<Props> = ({ item }) => {
|
const TimelineConversation: React.FC<Props> = ({ item }) => {
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
|
|
||||||
const statusView = useMemo(() => {
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.statusView}>
|
<View style={styles.statusView}>
|
||||||
<View style={styles.status}>
|
<View style={styles.status}>
|
||||||
<TimelineAvatar uri={item.accounts[0].avatar} id={item.accounts[0].id} />
|
<TimelineAvatar account={item.accounts[0]} />
|
||||||
<View style={styles.details}>
|
<View style={styles.details}>
|
||||||
<HeaderConversation
|
<TimelineHeaderConversation
|
||||||
account={item.accounts[0]}
|
account={item.accounts[0]}
|
||||||
created_at={item.last_status?.created_at}
|
created_at={item.last_status?.created_at}
|
||||||
/>
|
/>
|
||||||
@ -34,14 +33,7 @@ const TimelineConversation: React.FC<Props> = ({ item }) => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{item.last_status ? (
|
{item.last_status ? (
|
||||||
<TimelineContent
|
<TimelineContent status={item.last_status} />
|
||||||
content={item.last_status.content}
|
|
||||||
emojis={item.last_status.emojis}
|
|
||||||
mentions={item.last_status.mentions}
|
|
||||||
spoiler_text={item.last_status.spoiler_text}
|
|
||||||
// tags={actualStatus.tags}
|
|
||||||
// style={{ flex: 1 }}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
)}
|
)}
|
||||||
@ -50,9 +42,6 @@ const TimelineConversation: React.FC<Props> = ({ item }) => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}, [item])
|
|
||||||
|
|
||||||
return statusView
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -29,14 +29,14 @@ const TimelineDefault: React.FC<Props> = ({ item, queryKey }) => {
|
|||||||
StyleConstants.Avatar.S - // Avatar width
|
StyleConstants.Avatar.S - // Avatar width
|
||||||
StyleConstants.Spacing.S // Avatar margin to the right
|
StyleConstants.Spacing.S // Avatar margin to the right
|
||||||
|
|
||||||
const pressableToot = useCallback(
|
const tootOnPress = useCallback(
|
||||||
() =>
|
() =>
|
||||||
navigation.navigate('Screen-Shared-Toot', {
|
navigation.navigate('Screen-Shared-Toot', {
|
||||||
toot: actualStatus
|
toot: actualStatus
|
||||||
}),
|
}),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
const childrenToot = useMemo(
|
const tootChildren = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<>
|
<>
|
||||||
{actualStatus.content.length > 0 && (
|
{actualStatus.content.length > 0 && (
|
||||||
@ -63,8 +63,7 @@ const TimelineDefault: React.FC<Props> = ({ item, queryKey }) => {
|
|||||||
<TimelineAvatar account={actualStatus.account} />
|
<TimelineAvatar account={actualStatus.account} />
|
||||||
<View style={styles.details}>
|
<View style={styles.details}>
|
||||||
<TimelineHeaderDefault queryKey={queryKey} status={actualStatus} />
|
<TimelineHeaderDefault queryKey={queryKey} status={actualStatus} />
|
||||||
{/* Can pass toot info to next page to speed up performance */}
|
<Pressable onPress={tootOnPress} children={tootChildren} />
|
||||||
<Pressable onPress={pressableToot} children={childrenToot} />
|
|
||||||
<TimelineActions queryKey={queryKey} status={actualStatus} />
|
<TimelineActions queryKey={queryKey} status={actualStatus} />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import React, { useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
import { Dimensions, Pressable, StyleSheet, View } from 'react-native'
|
import { Dimensions, Pressable, StyleSheet, View } from 'react-native'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
|
||||||
import Actioned from './Shared/Actioned'
|
import TimelineActioned from './Shared/Actioned'
|
||||||
import Avatar from './Shared/Avatar'
|
import TimelineActions from './Shared/Actions'
|
||||||
import HeaderDefault from './Shared/HeaderDefault'
|
import TimelineAttachment from './Shared/Attachment'
|
||||||
import Content from './Shared/Content'
|
import TimelineAvatar from './Shared/Avatar'
|
||||||
import Poll from './Shared/Poll'
|
import TimelineCard from './Shared/Card'
|
||||||
import Attachment from './Shared/Attachment'
|
import TimelineContent from './Shared/Content'
|
||||||
import Card from './Shared/Card'
|
import TimelineHeaderNotification from './Shared/HeaderNotification'
|
||||||
import ActionsStatus from './Shared/Actions'
|
import TimelinePoll from './Shared/Poll'
|
||||||
|
|
||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -22,75 +23,63 @@ const TimelineNotifications: React.FC<Props> = ({ notification, queryKey }) => {
|
|||||||
const actualAccount = notification.status
|
const actualAccount = notification.status
|
||||||
? notification.status.account
|
? notification.status.account
|
||||||
: notification.account
|
: notification.account
|
||||||
|
const contentWidth =
|
||||||
|
Dimensions.get('window').width -
|
||||||
|
StyleConstants.Spacing.Global.PagePadding * 2 - // Global page padding on both sides
|
||||||
|
StyleConstants.Avatar.S - // Avatar width
|
||||||
|
StyleConstants.Spacing.S // Avatar margin to the right
|
||||||
|
|
||||||
|
const tootOnPress = useCallback(
|
||||||
|
() =>
|
||||||
|
navigation.navigate('Screen-Shared-Toot', {
|
||||||
|
toot: notification
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
const tootChildren = useMemo(
|
||||||
|
() =>
|
||||||
|
notification.status ? (
|
||||||
|
<>
|
||||||
|
{notification.status.content.length > 0 && (
|
||||||
|
<TimelineContent status={notification.status} />
|
||||||
|
)}
|
||||||
|
{notification.status.poll && (
|
||||||
|
<TimelinePoll queryKey={queryKey} status={notification.status} />
|
||||||
|
)}
|
||||||
|
{notification.status.media_attachments.length > 0 && (
|
||||||
|
<TimelineAttachment
|
||||||
|
status={notification.status}
|
||||||
|
width={contentWidth}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{notification.status.card && (
|
||||||
|
<TimelineCard card={notification.status.card} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : null,
|
||||||
|
[notification.status?.poll?.voted]
|
||||||
|
)
|
||||||
|
|
||||||
const statusView = useMemo(() => {
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.notificationView}>
|
<View style={styles.notificationView}>
|
||||||
<Actioned
|
<TimelineActioned
|
||||||
action={notification.type}
|
action={notification.type}
|
||||||
name={
|
account={notification.account}
|
||||||
notification.account.display_name || notification.account.username
|
|
||||||
}
|
|
||||||
emojis={notification.account.emojis}
|
|
||||||
notification
|
notification
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View style={styles.notification}>
|
<View style={styles.notification}>
|
||||||
<Avatar uri={actualAccount.avatar} id={actualAccount.id} />
|
<TimelineAvatar account={actualAccount} />
|
||||||
<View style={styles.details}>
|
<View style={styles.details}>
|
||||||
<HeaderDefault
|
<TimelineHeaderNotification notification={notification} />
|
||||||
name={actualAccount.display_name || actualAccount.username}
|
<Pressable onPress={tootOnPress} children={tootChildren} />
|
||||||
emojis={actualAccount.emojis}
|
|
||||||
account={actualAccount.acct}
|
|
||||||
created_at={notification.created_at}
|
|
||||||
/>
|
|
||||||
<Pressable
|
|
||||||
onPress={() =>
|
|
||||||
navigation.navigate('Screen-Shared-Toot', {
|
|
||||||
toot: notification.id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{notification.status ? (
|
|
||||||
<>
|
|
||||||
{notification.status.content && (
|
|
||||||
<Content
|
|
||||||
content={notification.status.content}
|
|
||||||
emojis={notification.status.emojis}
|
|
||||||
mentions={notification.status.mentions}
|
|
||||||
spoiler_text={notification.status.spoiler_text}
|
|
||||||
// tags={notification.notification.tags}
|
|
||||||
// style={{ flex: 1 }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{notification.status.poll && (
|
|
||||||
<Poll poll={notification.status.poll} />
|
|
||||||
)}
|
|
||||||
{notification.status.media_attachments.length > 0 && (
|
|
||||||
<Attachment
|
|
||||||
media_attachments={notification.status.media_attachments}
|
|
||||||
sensitive={notification.status.sensitive}
|
|
||||||
width={Dimensions.get('window').width - 24 - 50 - 8}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{notification.status.card && (
|
|
||||||
<Card card={notification.status.card} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
</Pressable>
|
|
||||||
{notification.status && (
|
{notification.status && (
|
||||||
<ActionsStatus queryKey={queryKey} status={notification.status} />
|
<TimelineActions queryKey={queryKey} status={notification.status} />
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}, [notification])
|
|
||||||
|
|
||||||
return statusView
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
38
src/components/Timelines/Timeline/Shared/End.tsx
Normal file
38
src/components/Timelines/Timeline/Shared/End.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { Feather } from '@expo/vector-icons'
|
||||||
|
import React from 'react'
|
||||||
|
import { StyleSheet, Text, View } from 'react-native'
|
||||||
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
|
|
||||||
|
const TimelineEnd: React.FC = () => {
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.base}>
|
||||||
|
<Text style={[styles.text, { color: theme.secondary }]}>
|
||||||
|
居然刷到底了,喝杯{' '}
|
||||||
|
<Feather
|
||||||
|
name='coffee'
|
||||||
|
size={StyleConstants.Font.Size.S}
|
||||||
|
color={theme.secondary}
|
||||||
|
/>{' '}
|
||||||
|
吧
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
base: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: StyleConstants.Spacing.M
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
fontSize: StyleConstants.Font.Size.S,
|
||||||
|
marginLeft: StyleConstants.Spacing.S
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default TimelineEnd
|
@ -20,7 +20,9 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => {
|
const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => {
|
||||||
const domain = status.uri.split(new RegExp(/\/\/(.*?)\//))[1]
|
const domain = status.uri
|
||||||
|
? status.uri.split(new RegExp(/\/\/(.*?)\//))[1]
|
||||||
|
: ''
|
||||||
const name = status.account.display_name || status.account.username
|
const name = status.account.display_name || status.account.username
|
||||||
const emojis = status.account.emojis
|
const emojis = status.account.emojis
|
||||||
const account = status.account.acct
|
const account = status.account.acct
|
||||||
@ -173,7 +175,6 @@ const styles = StyleSheet.create({
|
|||||||
account: {
|
account: {
|
||||||
flexShrink: 1,
|
flexShrink: 1,
|
||||||
marginLeft: StyleConstants.Spacing.XS
|
marginLeft: StyleConstants.Spacing.XS
|
||||||
// lineHeight: StyleConstants.Font.LineHeight.M
|
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
238
src/components/Timelines/Timeline/Shared/HeaderNotification.tsx
Normal file
238
src/components/Timelines/Timeline/Shared/HeaderNotification.tsx
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Pressable,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View
|
||||||
|
} from 'react-native'
|
||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { Feather } from '@expo/vector-icons'
|
||||||
|
|
||||||
|
import Emojis from './Emojis'
|
||||||
|
import relativeTime from 'src/utils/relativeTime'
|
||||||
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
|
import { useQuery } from 'react-query'
|
||||||
|
import { relationshipFetch } from 'src/utils/fetches/relationshipFetch'
|
||||||
|
import client from 'src/api/client'
|
||||||
|
import { toast } from 'src/components/toast'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
notification: Mastodon.Notification
|
||||||
|
}
|
||||||
|
|
||||||
|
const TimelineHeaderNotification: React.FC<Props> = ({ notification }) => {
|
||||||
|
const name =
|
||||||
|
notification.account.display_name || notification.account.username
|
||||||
|
const emojis = notification.account.emojis
|
||||||
|
const account = notification.account.acct
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
|
const navigation = useNavigation()
|
||||||
|
const [since, setSince] = useState(relativeTime(notification.created_at))
|
||||||
|
|
||||||
|
const { status, data, refetch } = useQuery(
|
||||||
|
['Relationship', { id: notification.account.id }],
|
||||||
|
relationshipFetch,
|
||||||
|
{
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const [updateData, setUpdateData] = useState<
|
||||||
|
Mastodon.Relationship | undefined
|
||||||
|
>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
setSince(relativeTime(notification.created_at))
|
||||||
|
}, 1000)
|
||||||
|
}, [since])
|
||||||
|
|
||||||
|
const applicationOnPress = useCallback(() => {
|
||||||
|
navigation.navigate('Screen-Shared-Webview', {
|
||||||
|
uri: notification.status?.application!.website
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const relationshipOnPress = useCallback(() => {
|
||||||
|
client({
|
||||||
|
method: 'post',
|
||||||
|
instance: 'local',
|
||||||
|
url: `accounts/${notification.account.id}/${
|
||||||
|
updateData
|
||||||
|
? updateData.following || updateData.requested
|
||||||
|
? 'un'
|
||||||
|
: ''
|
||||||
|
: data.following || data.requested
|
||||||
|
? 'un'
|
||||||
|
: ''
|
||||||
|
}follow`
|
||||||
|
}).then(res => {
|
||||||
|
if (res.body.id === (updateData && updateData.id) || data.id) {
|
||||||
|
setUpdateData(res.body)
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
toast({ type: 'error', content: '请重试', autoHide: false })
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [data, updateData])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (notification.type === 'follow') {
|
||||||
|
refetch()
|
||||||
|
}
|
||||||
|
}, [notification.type])
|
||||||
|
const relationshipIcon = useMemo(() => {
|
||||||
|
switch (status) {
|
||||||
|
case 'idle':
|
||||||
|
case 'loading':
|
||||||
|
return <ActivityIndicator />
|
||||||
|
case 'success':
|
||||||
|
return (
|
||||||
|
<Pressable onPress={relationshipOnPress}>
|
||||||
|
<Feather
|
||||||
|
name={
|
||||||
|
updateData
|
||||||
|
? updateData.following
|
||||||
|
? 'user-check'
|
||||||
|
: updateData.requested
|
||||||
|
? 'loader'
|
||||||
|
: 'user-plus'
|
||||||
|
: data.following
|
||||||
|
? 'user-check'
|
||||||
|
: data.requested
|
||||||
|
? 'loader'
|
||||||
|
: 'user-plus'
|
||||||
|
}
|
||||||
|
color={
|
||||||
|
updateData
|
||||||
|
? updateData.following
|
||||||
|
? theme.primary
|
||||||
|
: theme.secondary
|
||||||
|
: data.following
|
||||||
|
? theme.primary
|
||||||
|
: theme.secondary
|
||||||
|
}
|
||||||
|
size={StyleConstants.Font.Size.M + 2}
|
||||||
|
/>
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}, [status, data, updateData])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.base}>
|
||||||
|
<View style={{ flexBasis: '80%' }}>
|
||||||
|
<View style={styles.nameAndAction}>
|
||||||
|
<View style={styles.name}>
|
||||||
|
{emojis?.length ? (
|
||||||
|
<Emojis
|
||||||
|
content={name}
|
||||||
|
emojis={emojis}
|
||||||
|
size={StyleConstants.Font.Size.M}
|
||||||
|
fontBold={true}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Text
|
||||||
|
numberOfLines={1}
|
||||||
|
style={[styles.nameWithoutEmoji, { color: theme.primary }]}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<Text
|
||||||
|
style={[styles.account, { color: theme.secondary }]}
|
||||||
|
numberOfLines={1}
|
||||||
|
>
|
||||||
|
@{account}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.meta}>
|
||||||
|
<View>
|
||||||
|
<Text style={[styles.created_at, { color: theme.secondary }]}>
|
||||||
|
{since}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
{notification.status?.visibility === 'private' && (
|
||||||
|
<Feather
|
||||||
|
name='lock'
|
||||||
|
size={StyleConstants.Font.Size.S}
|
||||||
|
color={theme.secondary}
|
||||||
|
style={styles.visibility}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{notification.status?.application &&
|
||||||
|
notification.status?.application.name !== 'Web' && (
|
||||||
|
<View>
|
||||||
|
<Text
|
||||||
|
onPress={applicationOnPress}
|
||||||
|
style={[styles.application, { color: theme.secondary }]}
|
||||||
|
>
|
||||||
|
发自于 - {notification.status?.application.name}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{notification.type === 'follow' && (
|
||||||
|
<View style={styles.relationship}>{relationshipIcon}</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
base: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row'
|
||||||
|
},
|
||||||
|
nameAndAction: {
|
||||||
|
width: '100%',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
flexBasis: '90%',
|
||||||
|
flexDirection: 'row'
|
||||||
|
},
|
||||||
|
nameWithoutEmoji: {
|
||||||
|
fontSize: StyleConstants.Font.Size.M,
|
||||||
|
fontWeight: StyleConstants.Font.Weight.Bold
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
alignItems: 'flex-end'
|
||||||
|
},
|
||||||
|
account: {
|
||||||
|
flexShrink: 1,
|
||||||
|
marginLeft: StyleConstants.Spacing.XS
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: StyleConstants.Spacing.XS,
|
||||||
|
marginBottom: StyleConstants.Spacing.S
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
fontSize: StyleConstants.Font.Size.S
|
||||||
|
},
|
||||||
|
visibility: {
|
||||||
|
marginLeft: StyleConstants.Spacing.S
|
||||||
|
},
|
||||||
|
application: {
|
||||||
|
fontSize: StyleConstants.Font.Size.S,
|
||||||
|
marginLeft: StyleConstants.Spacing.S
|
||||||
|
},
|
||||||
|
relationship: {
|
||||||
|
flexBasis: '20%',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default React.memo(TimelineHeaderNotification, () => true)
|
16
src/utils/fetches/relationshipFetch.ts
Normal file
16
src/utils/fetches/relationshipFetch.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import client from 'src/api/client'
|
||||||
|
|
||||||
|
export const relationshipFetch = async (
|
||||||
|
key: string,
|
||||||
|
{ id }: { id: string }
|
||||||
|
) => {
|
||||||
|
const res = await client({
|
||||||
|
method: 'get',
|
||||||
|
instance: 'local',
|
||||||
|
url: `accounts/relationships`,
|
||||||
|
params: {
|
||||||
|
'id[]': id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return Promise.resolve(res.body[0])
|
||||||
|
}
|
Reference in New Issue
Block a user