mirror of https://github.com/tooot-app/app
Refine structure
This commit is contained in:
parent
811964e10f
commit
dcb36a682d
|
@ -17,39 +17,20 @@ declare namespace App {
|
|||
}
|
||||
|
||||
declare namespace QueryKey {
|
||||
type Account = [
|
||||
'Account',
|
||||
{
|
||||
id: Mastodon.Account['id']
|
||||
}
|
||||
]
|
||||
type Account = ['Account', { id: Mastodon.Account['id'] }]
|
||||
|
||||
type Announcements = [
|
||||
'Announcements',
|
||||
{
|
||||
showAll?: boolean
|
||||
}
|
||||
]
|
||||
type Announcements = ['Announcements', { showAll?: boolean }]
|
||||
|
||||
type Application = [
|
||||
'Application',
|
||||
{
|
||||
instanceDomain: string
|
||||
}
|
||||
]
|
||||
type Application = ['Application', { instanceDomain: string }]
|
||||
|
||||
type Instance = [
|
||||
'Instance',
|
||||
{
|
||||
instanceDomain: string
|
||||
}
|
||||
]
|
||||
type Instance = ['Instance', { instanceDomain: string }]
|
||||
|
||||
type Relationship = [
|
||||
'Relationship',
|
||||
{
|
||||
id: Mastodon.Account['id']
|
||||
}
|
||||
type Relationship = ['Relationship', { id: Mastodon.Account['id'] }]
|
||||
|
||||
type Relationships = [
|
||||
'Relationships',
|
||||
'following' | 'followers',
|
||||
{ id: Mastodon.Account['id'] }
|
||||
]
|
||||
|
||||
type Search = [
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import { ParseEmojis } from '@components/Parse'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { Image, Pressable, StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
account: Mastodon.Account
|
||||
}
|
||||
|
||||
const ComponentAccount: React.FC<Props> = ({ account }) => {
|
||||
const navigation = useNavigation()
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
style={[styles.itemDefault, styles.itemAccount]}
|
||||
onPress={() => {
|
||||
navigation.push('Screen-Shared-Account', { account })
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: account.avatar_static }}
|
||||
style={styles.itemAccountAvatar}
|
||||
/>
|
||||
<View>
|
||||
<Text numberOfLines={1}>
|
||||
<ParseEmojis
|
||||
content={account.display_name || account.username}
|
||||
emojis={account.emojis}
|
||||
size='S'
|
||||
fontBold
|
||||
/>
|
||||
</Text>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={[styles.itemAccountAcct, { color: theme.secondary }]}
|
||||
>
|
||||
@{account.acct}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
itemDefault: {
|
||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingVertical: StyleConstants.Spacing.M
|
||||
},
|
||||
itemAccount: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
itemAccountAvatar: {
|
||||
alignSelf: 'flex-start',
|
||||
width: StyleConstants.Avatar.S,
|
||||
height: StyleConstants.Avatar.S,
|
||||
borderRadius: 6,
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
},
|
||||
itemAccountAcct: { marginTop: StyleConstants.Spacing.XS }
|
||||
})
|
||||
|
||||
export default ComponentAccount
|
|
@ -27,7 +27,8 @@ const renderNode = ({
|
|||
navigation,
|
||||
mentions,
|
||||
tags,
|
||||
showFullLink
|
||||
showFullLink,
|
||||
disableDetails
|
||||
}: {
|
||||
theme: any
|
||||
node: any
|
||||
|
@ -37,6 +38,7 @@ const renderNode = ({
|
|||
mentions?: Mastodon.Mention[]
|
||||
tags?: Mastodon.Tag[]
|
||||
showFullLink: boolean
|
||||
disableDetails: boolean
|
||||
}) => {
|
||||
if (node.name == 'a') {
|
||||
const classes = node.attribs.class
|
||||
|
@ -52,9 +54,10 @@ const renderNode = ({
|
|||
}}
|
||||
onPress={() => {
|
||||
const tag = href.split(new RegExp(/\/tag\/(.*)|\/tags\/(.*)/))
|
||||
navigation.push('Screen-Shared-Hashtag', {
|
||||
hashtag: tag[1] || tag[2]
|
||||
})
|
||||
!disableDetails &&
|
||||
navigation.push('Screen-Shared-Hashtag', {
|
||||
hashtag: tag[1] || tag[2]
|
||||
})
|
||||
}}
|
||||
>
|
||||
{node.children[0].data}
|
||||
|
@ -72,6 +75,7 @@ const renderNode = ({
|
|||
}}
|
||||
onPress={() => {
|
||||
accountIndex !== -1 &&
|
||||
!disableDetails &&
|
||||
navigation.push('Screen-Shared-Account', {
|
||||
account: mentions[accountIndex]
|
||||
})
|
||||
|
@ -96,7 +100,7 @@ const renderNode = ({
|
|||
...StyleConstants.FontStyle[size]
|
||||
}}
|
||||
onPress={async () =>
|
||||
!shouldBeTag
|
||||
!disableDetails && !shouldBeTag
|
||||
? await openLink(href)
|
||||
: navigation.push('Screen-Shared-Hashtag', {
|
||||
hashtag: content.substring(1)
|
||||
|
@ -132,6 +136,7 @@ export interface Props {
|
|||
showFullLink?: boolean
|
||||
numberOfLines?: number
|
||||
expandHint?: string
|
||||
disableDetails?: boolean
|
||||
}
|
||||
|
||||
const ParseHTML: React.FC<Props> = ({
|
||||
|
@ -142,7 +147,8 @@ const ParseHTML: React.FC<Props> = ({
|
|||
tags,
|
||||
showFullLink = false,
|
||||
numberOfLines = 10,
|
||||
expandHint = '全文'
|
||||
expandHint = '全文',
|
||||
disableDetails = false
|
||||
}) => {
|
||||
const navigation = useNavigation()
|
||||
const { theme } = useTheme()
|
||||
|
@ -157,7 +163,8 @@ const ParseHTML: React.FC<Props> = ({
|
|||
navigation,
|
||||
mentions,
|
||||
tags,
|
||||
showFullLink
|
||||
showFullLink,
|
||||
disableDetails
|
||||
}),
|
||||
[]
|
||||
)
|
||||
|
|
|
@ -15,7 +15,7 @@ export interface Props {
|
|||
const RelationshipIncoming: React.FC<Props> = ({ id }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const relationshipQueryKey = ['Relationship', { id }]
|
||||
const relationshipQueryKey: QueryKey.Relationship = ['Relationship', { id }]
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const fireMutation = useCallback(
|
||||
|
|
|
@ -14,7 +14,7 @@ export interface Props {
|
|||
const RelationshipOutgoing: React.FC<Props> = ({ id }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const relationshipQueryKey = ['Relationship', { id }]
|
||||
const relationshipQueryKey: QueryKey.Relationship = ['Relationship', { id }]
|
||||
const query = useQuery(relationshipQueryKey, relationshipFetch)
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
extraMarginLeft?: number
|
||||
extraMarginRight?: number
|
||||
}
|
||||
|
||||
const ComponentSeparator = React.memo(
|
||||
({ extraMarginLeft = 0, extraMarginRight = 0 }: Props) => {
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
borderTopColor: theme.border,
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
marginLeft:
|
||||
StyleConstants.Spacing.Global.PagePadding + extraMarginLeft,
|
||||
marginRight:
|
||||
StyleConstants.Spacing.Global.PagePadding + extraMarginRight
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
() => true
|
||||
)
|
||||
|
||||
export default ComponentSeparator
|
|
@ -1,18 +1,18 @@
|
|||
import ComponentSeparator from '@components/Separator'
|
||||
import TimelineConversation from '@components/Timelines/Timeline/Conversation'
|
||||
import TimelineDefault from '@components/Timelines/Timeline/Default'
|
||||
import TimelineEmpty from '@components/Timelines/Timeline/Empty'
|
||||
import TimelineEnd from '@root/components/Timelines/Timeline/End'
|
||||
import TimelineNotifications from '@components/Timelines/Timeline/Notifications'
|
||||
import { useScrollToTop } from '@react-navigation/native'
|
||||
import { timelineFetch } from '@utils/fetches/timelineFetch'
|
||||
import { updateNotification } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import React, { useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import { RefreshControl, StyleSheet } from 'react-native'
|
||||
import { InfiniteData, useInfiniteQuery } from 'react-query'
|
||||
|
||||
import TimelineNotifications from '@components/Timelines/Timeline/Notifications'
|
||||
import TimelineDefault from '@components/Timelines/Timeline/Default'
|
||||
import TimelineConversation from '@components/Timelines/Timeline/Conversation'
|
||||
import { timelineFetch } from '@utils/fetches/timelineFetch'
|
||||
import TimelineSeparator from '@components/Timelines/Timeline/Separator'
|
||||
import TimelineEmpty from '@components/Timelines/Timeline/Empty'
|
||||
import TimelineEnd from '@components/Timelines/Timeline/Shared/End'
|
||||
import { useScrollToTop } from '@react-navigation/native'
|
||||
import { FlatList } from 'react-native-gesture-handler'
|
||||
import { InfiniteData, useInfiniteQuery } from 'react-query'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { updateNotification } from '@root/utils/slices/instancesSlice'
|
||||
|
||||
export type TimelineData =
|
||||
| InfiniteData<{
|
||||
|
@ -130,6 +130,10 @@ const Timeline: React.FC<Props> = ({
|
|||
item={item}
|
||||
queryKey={queryKey}
|
||||
index={index}
|
||||
{...(queryKey[0] === 'RemotePublic' && {
|
||||
disableDetails: true,
|
||||
disableOnPress: true
|
||||
})}
|
||||
{...(flattenPinnedLength &&
|
||||
flattenPinnedLength[0] && {
|
||||
pinnedLength: flattenPinnedLength[0]
|
||||
|
@ -143,8 +147,13 @@ const Timeline: React.FC<Props> = ({
|
|||
)
|
||||
const ItemSeparatorComponent = useCallback(
|
||||
({ leadingItem }) => (
|
||||
<TimelineSeparator
|
||||
{...(toot === leadingItem.id && { highlighted: true })}
|
||||
<ComponentSeparator
|
||||
{...(toot === leadingItem.id
|
||||
? { extraMarginLeft: 0 }
|
||||
: {
|
||||
extraMarginLeft:
|
||||
StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||
})}
|
||||
/>
|
||||
),
|
||||
[]
|
||||
|
|
|
@ -15,10 +15,12 @@ import { useSelector } from 'react-redux'
|
|||
|
||||
export interface Props {
|
||||
item: Mastodon.Status
|
||||
queryKey: QueryKey.Timeline
|
||||
queryKey?: QueryKey.Timeline
|
||||
index: number
|
||||
pinnedLength?: number
|
||||
highlighted?: boolean
|
||||
disableDetails?: boolean
|
||||
disableOnPress?: boolean
|
||||
}
|
||||
|
||||
// When the poll is long
|
||||
|
@ -27,17 +29,18 @@ const TimelineDefault: React.FC<Props> = ({
|
|||
queryKey,
|
||||
index,
|
||||
pinnedLength,
|
||||
highlighted = false
|
||||
highlighted = false,
|
||||
disableDetails = false,
|
||||
disableOnPress = false
|
||||
}) => {
|
||||
const localAccountId = useSelector(getLocalAccountId)
|
||||
const isRemotePublic = queryKey[0] === 'RemotePublic'
|
||||
const navigation = useNavigation()
|
||||
|
||||
let actualStatus = item.reblog ? item.reblog : item
|
||||
|
||||
const onPress = useCallback(
|
||||
() =>
|
||||
!isRemotePublic &&
|
||||
!disableOnPress &&
|
||||
!highlighted &&
|
||||
navigation.push('Screen-Shared-Toot', {
|
||||
toot: actualStatus
|
||||
|
@ -55,11 +58,11 @@ const TimelineDefault: React.FC<Props> = ({
|
|||
|
||||
<View style={styles.header}>
|
||||
<TimelineAvatar
|
||||
{...(!isRemotePublic && { queryKey })}
|
||||
queryKey={disableOnPress ? undefined : queryKey}
|
||||
account={actualStatus.account}
|
||||
/>
|
||||
<TimelineHeaderDefault
|
||||
{...(!isRemotePublic && { queryKey })}
|
||||
queryKey={disableOnPress ? undefined : queryKey}
|
||||
status={actualStatus}
|
||||
sameAccount={actualStatus.account.id === localAccountId}
|
||||
/>
|
||||
|
@ -74,9 +77,13 @@ const TimelineDefault: React.FC<Props> = ({
|
|||
}}
|
||||
>
|
||||
{actualStatus.content.length > 0 && (
|
||||
<TimelineContent status={actualStatus} highlighted={highlighted} />
|
||||
<TimelineContent
|
||||
status={actualStatus}
|
||||
highlighted={highlighted}
|
||||
disableDetails={disableDetails}
|
||||
/>
|
||||
)}
|
||||
{actualStatus.poll && (
|
||||
{queryKey && actualStatus.poll && (
|
||||
<TimelinePoll
|
||||
queryKey={queryKey}
|
||||
poll={actualStatus.poll}
|
||||
|
@ -84,13 +91,15 @@ const TimelineDefault: React.FC<Props> = ({
|
|||
sameAccount={actualStatus.account.id === localAccountId}
|
||||
/>
|
||||
)}
|
||||
{actualStatus.media_attachments.length > 0 && (
|
||||
{!disableDetails && actualStatus.media_attachments.length > 0 && (
|
||||
<TimelineAttachment status={actualStatus} />
|
||||
)}
|
||||
{actualStatus.card && <TimelineCard card={actualStatus.card} />}
|
||||
{!disableDetails && actualStatus.card && (
|
||||
<TimelineCard card={actualStatus.card} />
|
||||
)}
|
||||
</View>
|
||||
|
||||
{!isRemotePublic && (
|
||||
{queryKey && !disableDetails && (
|
||||
<View
|
||||
style={{
|
||||
paddingLeft: highlighted
|
||||
|
|
|
@ -23,7 +23,6 @@ const TimelineEnd: React.FC<Props> = ({ hasNextPage }) => {
|
|||
i18nKey='timeline:shared.end.message' // optional -> fallbacks to defaults if not provided
|
||||
components={[
|
||||
<Icon
|
||||
inline
|
||||
name='Coffee'
|
||||
size={StyleConstants.Font.Size.S}
|
||||
color={theme.secondary}
|
|
@ -1,38 +0,0 @@
|
|||
import React from 'react'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
|
||||
export interface Props {
|
||||
highlighted?: boolean
|
||||
}
|
||||
|
||||
const TimelineSeparator: React.FC<Props> = ({ highlighted = false }) => {
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.base,
|
||||
{
|
||||
borderTopColor: theme.border,
|
||||
marginLeft: highlighted
|
||||
? StyleConstants.Spacing.Global.PagePadding
|
||||
: StyleConstants.Spacing.Global.PagePadding +
|
||||
StyleConstants.Avatar.M +
|
||||
StyleConstants.Spacing.S
|
||||
}
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
marginRight: StyleConstants.Spacing.Global.PagePadding
|
||||
}
|
||||
})
|
||||
|
||||
export default TimelineSeparator
|
|
@ -8,12 +8,14 @@ export interface Props {
|
|||
status: Mastodon.Status
|
||||
numberOfLines?: number
|
||||
highlighted?: boolean
|
||||
disableDetails?: boolean
|
||||
}
|
||||
|
||||
const TimelineContent: React.FC<Props> = ({
|
||||
status,
|
||||
numberOfLines,
|
||||
highlighted = false
|
||||
highlighted = false,
|
||||
disableDetails = false
|
||||
}) => {
|
||||
const { t } = useTranslation('timeline')
|
||||
|
||||
|
@ -29,6 +31,7 @@ const TimelineContent: React.FC<Props> = ({
|
|||
mentions={status.mentions}
|
||||
tags={status.tags}
|
||||
numberOfLines={999}
|
||||
disableDetails={disableDetails}
|
||||
/>
|
||||
</View>
|
||||
<ParseHTML
|
||||
|
@ -39,6 +42,7 @@ const TimelineContent: React.FC<Props> = ({
|
|||
tags={status.tags}
|
||||
numberOfLines={0}
|
||||
expandHint={t('shared.content.expandHint')}
|
||||
disableDetails={disableDetails}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
|
@ -49,6 +53,7 @@ const TimelineContent: React.FC<Props> = ({
|
|||
mentions={status.mentions}
|
||||
tags={status.tags}
|
||||
numberOfLines={numberOfLines}
|
||||
disableDetails={disableDetails}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import client from '@api/client'
|
||||
import haptics from '@components/haptics'
|
||||
import Icon from '@components/Icon'
|
||||
import { TimelineData } from '@components/Timelines/Timeline'
|
||||
import { toast } from '@components/toast'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { findIndex } from 'lodash'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Pressable, StyleSheet, View } from 'react-native'
|
||||
|
@ -33,14 +35,24 @@ const HeaderConversation: React.FC<Props> = ({ queryKey, conversation }) => {
|
|||
const oldData = queryClient.getQueryData(queryKey)
|
||||
|
||||
haptics('Success')
|
||||
queryClient.setQueryData(queryKey, (old: any) =>
|
||||
old.pages.map((paging: any) => ({
|
||||
toots: paging.toots.filter(
|
||||
(toot: Mastodon.Conversation) => toot.id !== conversation.id
|
||||
),
|
||||
pointer: paging.pointer
|
||||
}))
|
||||
)
|
||||
queryClient.setQueryData<TimelineData>(queryKey, old => {
|
||||
let tootIndex = -1
|
||||
const pageIndex = findIndex(old?.pages, page => {
|
||||
const tempIndex = findIndex(page.toots, ['id', conversation.id])
|
||||
if (tempIndex >= 0) {
|
||||
tootIndex = tempIndex
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
if (pageIndex >= 0 && tootIndex >= 0) {
|
||||
old!.pages[pageIndex].toots.splice(tootIndex, 1)
|
||||
}
|
||||
|
||||
return old
|
||||
})
|
||||
|
||||
return oldData
|
||||
},
|
||||
|
|
|
@ -7,8 +7,8 @@ export default {
|
|||
created_at: '加入时间:{{date}}',
|
||||
summary: {
|
||||
statuses_count: '{{count}} 条嘟文',
|
||||
followers_count: '关注 {{count}} 人',
|
||||
following_count: '被 {{count}} 人关注'
|
||||
following_count: '关注 {{count}} 人',
|
||||
followers_count: '被 {{count}} 人关注'
|
||||
},
|
||||
segments: {
|
||||
left: '所有嘟嘟',
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useNavigation } from '@react-navigation/native'
|
||||
import { StyleConstants } from '@root/utils/styles/constants'
|
||||
import { useTheme } from '@root/utils/styles/ThemeManager'
|
||||
import { LinearGradient } from 'expo-linear-gradient'
|
||||
|
@ -13,6 +14,7 @@ export interface Props {
|
|||
}
|
||||
|
||||
const AccountInformationStats = forwardRef<any, Props>(({ account }, ref) => {
|
||||
const navigation = useNavigation()
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation('sharedAccount')
|
||||
const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient)
|
||||
|
@ -55,10 +57,17 @@ const AccountInformationStats = forwardRef<any, Props>(({ account }, ref) => {
|
|||
shimmerColors={theme.shimmer}
|
||||
>
|
||||
<Text
|
||||
style={[styles.stat, { color: theme.primary, textAlign: 'center' }]}
|
||||
style={[styles.stat, { color: theme.primary, textAlign: 'right' }]}
|
||||
onPress={() =>
|
||||
account &&
|
||||
navigation.push('Screen-Shared-Relationships', {
|
||||
account,
|
||||
initialType: 'following'
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('content.summary.followers_count', {
|
||||
count: account?.followers_count || 0
|
||||
{t('content.summary.following_count', {
|
||||
count: account?.following_count || 0
|
||||
})}
|
||||
</Text>
|
||||
</ShimmerPlaceholder>
|
||||
|
@ -70,10 +79,17 @@ const AccountInformationStats = forwardRef<any, Props>(({ account }, ref) => {
|
|||
shimmerColors={theme.shimmer}
|
||||
>
|
||||
<Text
|
||||
style={[styles.stat, { color: theme.primary, textAlign: 'right' }]}
|
||||
style={[styles.stat, { color: theme.primary, textAlign: 'center' }]}
|
||||
onPress={() =>
|
||||
account &&
|
||||
navigation.push('Screen-Shared-Relationships', {
|
||||
account,
|
||||
initialType: 'followers'
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('content.summary.following_count', {
|
||||
count: account?.following_count || 0
|
||||
{t('content.summary.followers_count', {
|
||||
count: account?.followers_count || 0
|
||||
})}
|
||||
</Text>
|
||||
</ShimmerPlaceholder>
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
import TimelineAttachment from '@components/Timelines/Timeline/Shared/Attachment'
|
||||
import TimelineAvatar from '@components/Timelines/Timeline/Shared/Avatar'
|
||||
import TimelineCard from '@components/Timelines/Timeline/Shared/Card'
|
||||
import TimelineContent from '@components/Timelines/Timeline/Shared/Content'
|
||||
import TimelineHeaderDefault from '@components/Timelines/Timeline/Shared/HeaderDefault'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import ComposeContext from './utils/createContext'
|
||||
import React, { useContext } from 'react'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
import TimelineDefault from '@root/components/Timelines/Timeline/Default'
|
||||
|
||||
const ComposeReply: React.FC = () => {
|
||||
const {
|
||||
|
@ -16,32 +11,20 @@ const ComposeReply: React.FC = () => {
|
|||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
<View style={[styles.status, { borderTopColor: theme.border }]}>
|
||||
<TimelineAvatar account={replyToStatus!.account} />
|
||||
<View style={styles.details}>
|
||||
<TimelineHeaderDefault status={replyToStatus!} sameAccount={false} />
|
||||
{replyToStatus!.content.length > 0 && (
|
||||
<TimelineContent status={replyToStatus!} />
|
||||
)}
|
||||
{replyToStatus!.media_attachments.length > 0 && (
|
||||
<TimelineAttachment status={replyToStatus!} />
|
||||
)}
|
||||
{replyToStatus!.card && <TimelineCard card={replyToStatus!.card} />}
|
||||
</View>
|
||||
<View style={[styles.base, { borderTopColor: theme.border }]}>
|
||||
<TimelineDefault
|
||||
item={replyToStatus!}
|
||||
index={0}
|
||||
disableDetails
|
||||
disableOnPress
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
status: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
paddingTop: StyleConstants.Spacing.Global.PagePadding,
|
||||
margin: StyleConstants.Spacing.Global.PagePadding
|
||||
},
|
||||
details: {
|
||||
flex: 1
|
||||
base: {
|
||||
borderTopWidth: StyleSheet.hairlineWidth
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import SegmentedControl from '@react-native-community/segmented-control'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Dimensions, StyleSheet, View } from 'react-native'
|
||||
import { TabView } from 'react-native-tab-view'
|
||||
import RelationshipsList from './Relationships/List'
|
||||
|
||||
export interface Props {
|
||||
route: {
|
||||
params: {
|
||||
account: Mastodon.Account
|
||||
initialType: 'following' | 'followers'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ScreenSharedRelationships: React.FC<Props> = ({
|
||||
route: {
|
||||
params: { account, initialType }
|
||||
}
|
||||
}) => {
|
||||
console.log(account.id)
|
||||
const { mode } = useTheme()
|
||||
const navigation = useNavigation()
|
||||
|
||||
const [segment, setSegment] = useState(initialType === 'following' ? 0 : 1)
|
||||
useEffect(() => {
|
||||
const updateHeaderRight = () =>
|
||||
navigation.setOptions({
|
||||
headerCenter: () => (
|
||||
<View style={styles.segmentsContainer}>
|
||||
<SegmentedControl
|
||||
appearance={mode}
|
||||
values={['关注中', '关注者']}
|
||||
selectedIndex={segment}
|
||||
onChange={({ nativeEvent }) =>
|
||||
setSegment(nativeEvent.selectedSegmentIndex)
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
})
|
||||
return updateHeaderRight()
|
||||
}, [])
|
||||
|
||||
const routes: { key: Props['route']['params']['initialType'] }[] = [
|
||||
{ key: 'following' },
|
||||
{ key: 'followers' }
|
||||
]
|
||||
|
||||
const renderScene = ({
|
||||
route
|
||||
}: {
|
||||
route: {
|
||||
key: Props['route']['params']['initialType']
|
||||
}
|
||||
}) => {
|
||||
return <RelationshipsList id={account.id} type={route.key} />
|
||||
}
|
||||
|
||||
return (
|
||||
<TabView
|
||||
lazy
|
||||
swipeEnabled
|
||||
renderScene={renderScene}
|
||||
renderTabBar={() => null}
|
||||
initialLayout={{ width: Dimensions.get('window').width }}
|
||||
navigationState={{ index: segment, routes }}
|
||||
onIndexChange={index => setSegment(index)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
segmentsContainer: {
|
||||
flexBasis: '60%'
|
||||
}
|
||||
})
|
||||
|
||||
export default React.memo(ScreenSharedRelationships, () => true)
|
|
@ -0,0 +1,98 @@
|
|||
import ComponentAccount from '@components/Account'
|
||||
import ComponentSeparator from '@components/Separator'
|
||||
import TimelineEmpty from '@components/Timelines/Timeline/Empty'
|
||||
import TimelineEnd from '@root/components/Timelines/Timeline/End'
|
||||
import { useScrollToTop } from '@react-navigation/native'
|
||||
import { relationshipsFetch } from '@utils/fetches/relationshipsFetch'
|
||||
import React, { useCallback, useMemo, useRef } from 'react'
|
||||
import { RefreshControl, StyleSheet } from 'react-native'
|
||||
import { FlatList } from 'react-native-gesture-handler'
|
||||
import { useInfiniteQuery } from 'react-query'
|
||||
|
||||
export interface Props {
|
||||
id: Mastodon.Account['id']
|
||||
type: 'following' | 'followers'
|
||||
}
|
||||
|
||||
const RelationshipsList: React.FC<Props> = ({ id, type }) => {
|
||||
const queryKey: QueryKey.Relationships = ['Relationships', type, { id }]
|
||||
|
||||
const {
|
||||
status,
|
||||
data,
|
||||
isFetching,
|
||||
refetch,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
isFetchingNextPage
|
||||
} = useInfiniteQuery(queryKey, relationshipsFetch, {
|
||||
getNextPageParam: lastPage => {
|
||||
return lastPage.length
|
||||
? {
|
||||
direction: 'next',
|
||||
id: lastPage[lastPage.length - 1].id
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
})
|
||||
const flattenData = data?.pages ? data.pages.flatMap(d => [...d]) : []
|
||||
|
||||
const flRef = useRef<FlatList<any>>(null)
|
||||
|
||||
const keyExtractor = useCallback(({ id }) => id, [])
|
||||
const renderItem = useCallback(
|
||||
({ item }) => <ComponentAccount account={item} />,
|
||||
[]
|
||||
)
|
||||
const flItemEmptyComponent = useMemo(
|
||||
() => <TimelineEmpty status={status} refetch={refetch} />,
|
||||
[status]
|
||||
)
|
||||
const onEndReached = useCallback(
|
||||
() => !isFetchingNextPage && fetchNextPage(),
|
||||
[isFetchingNextPage]
|
||||
)
|
||||
const ListFooterComponent = useCallback(
|
||||
() => <TimelineEnd hasNextPage={hasNextPage} />,
|
||||
[hasNextPage]
|
||||
)
|
||||
const refreshControl = useMemo(
|
||||
() => (
|
||||
<RefreshControl refreshing={isFetching} onRefresh={() => refetch()} />
|
||||
),
|
||||
[isFetching]
|
||||
)
|
||||
|
||||
useScrollToTop(flRef)
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
ref={flRef}
|
||||
windowSize={11}
|
||||
data={flattenData}
|
||||
initialNumToRender={5}
|
||||
maxToRenderPerBatch={5}
|
||||
style={styles.flatList}
|
||||
renderItem={renderItem}
|
||||
onEndReached={onEndReached}
|
||||
keyExtractor={keyExtractor}
|
||||
onEndReachedThreshold={0.75}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
ListEmptyComponent={flItemEmptyComponent}
|
||||
refreshControl={refreshControl}
|
||||
ItemSeparatorComponent={ComponentSeparator}
|
||||
maintainVisibleContentPosition={{
|
||||
minIndexForVisible: 0,
|
||||
autoscrollToTopThreshold: 2
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flatList: {
|
||||
minHeight: '100%'
|
||||
}
|
||||
})
|
||||
|
||||
export default RelationshipsList
|
|
@ -1,24 +1,22 @@
|
|||
import { HeaderRight } from '@components/Header'
|
||||
import Icon from '@components/Icon'
|
||||
import { ParseEmojis, ParseHTML } from '@components/Parse'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import ComponentAccount from '@root/components/Account'
|
||||
import ComponentSeparator from '@root/components/Separator'
|
||||
import TimelineDefault from '@root/components/Timelines/Timeline/Default'
|
||||
import { searchFetch } from '@utils/fetches/searchFetch'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { debounce } from 'lodash'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import {
|
||||
Image,
|
||||
KeyboardAvoidingView,
|
||||
Pressable,
|
||||
SectionList,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
View
|
||||
} from 'react-native'
|
||||
import { Chase } from 'react-native-animated-spinkit'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
import { TextInput } from 'react-native-gesture-handler'
|
||||
import { useQuery } from 'react-query'
|
||||
|
||||
const ScreenSharedSearch: React.FC = () => {
|
||||
|
@ -31,6 +29,42 @@ const ScreenSharedSearch: React.FC = () => {
|
|||
{ enabled: false }
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const updateHeaderRight = () =>
|
||||
navigation.setOptions({
|
||||
headerCenter: () => (
|
||||
<View style={styles.searchBar}>
|
||||
<Text
|
||||
style={{ ...StyleConstants.FontStyle.M, color: theme.primary }}
|
||||
>
|
||||
搜索
|
||||
</Text>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.textInput,
|
||||
{
|
||||
color: theme.primary
|
||||
}
|
||||
]}
|
||||
autoFocus
|
||||
onChangeText={onChangeText}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
clearButtonMode='never'
|
||||
keyboardType='web-search'
|
||||
onSubmitEditing={({ nativeEvent: { text } }) =>
|
||||
setSearchTerm(text)
|
||||
}
|
||||
placeholder={'些什么'}
|
||||
placeholderTextColor={theme.secondary}
|
||||
returnKeyType='go'
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
})
|
||||
return updateHeaderRight()
|
||||
}, [])
|
||||
|
||||
const [setctionData, setSectionData] = useState<
|
||||
{ title: string; data: any }[]
|
||||
>([])
|
||||
|
@ -74,48 +108,50 @@ const ScreenSharedSearch: React.FC = () => {
|
|||
const listEmpty = useMemo(
|
||||
() => (
|
||||
<View style={styles.emptyBase}>
|
||||
{status === 'loading' ? (
|
||||
<View style={styles.loading}>
|
||||
<Chase
|
||||
size={StyleConstants.Font.Size.M * 1.25}
|
||||
color={theme.secondary}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<Text
|
||||
style={[
|
||||
styles.emptyDefault,
|
||||
styles.emptyFontSize,
|
||||
{ color: theme.primary }
|
||||
]}
|
||||
>
|
||||
输入关键词搜索<Text style={styles.emptyFontBold}>用户</Text>、
|
||||
<Text style={styles.emptyFontBold}>话题标签</Text>或者
|
||||
<Text style={styles.emptyFontBold}>嘟文</Text>
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
高级搜索格式
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>@username@domain</Text>
|
||||
{' '}
|
||||
搜索用户
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>#example</Text>
|
||||
{' '}搜索话题标签
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>URL</Text>
|
||||
{' '}搜索指定嘟文
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>URL</Text>
|
||||
{' '}搜索指定用户
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
<View>
|
||||
{status === 'loading' ? (
|
||||
<View style={styles.loading}>
|
||||
<Chase
|
||||
size={StyleConstants.Font.Size.M * 1.25}
|
||||
color={theme.secondary}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<Text
|
||||
style={[
|
||||
styles.emptyDefault,
|
||||
styles.emptyFontSize,
|
||||
{ color: theme.primary }
|
||||
]}
|
||||
>
|
||||
输入关键词搜索<Text style={styles.emptyFontBold}>用户</Text>、
|
||||
<Text style={styles.emptyFontBold}>话题标签</Text>或者
|
||||
<Text style={styles.emptyFontBold}>嘟文</Text>
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
高级搜索格式
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>@username@domain</Text>
|
||||
{' '}
|
||||
搜索用户
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>#example</Text>
|
||||
{' '}搜索话题标签
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>URL</Text>
|
||||
{' '}搜索指定嘟文
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>URL</Text>
|
||||
{' '}搜索指定用户
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
),
|
||||
[status]
|
||||
|
@ -123,10 +159,7 @@ const ScreenSharedSearch: React.FC = () => {
|
|||
const sectionHeader = useCallback(
|
||||
({ section: { title } }) => (
|
||||
<View
|
||||
style={[
|
||||
styles.sectionHeader,
|
||||
{ borderBottomColor: theme.border, backgroundColor: theme.background }
|
||||
]}
|
||||
style={[styles.sectionHeader, { backgroundColor: theme.background }]}
|
||||
>
|
||||
<Text style={[styles.sectionHeaderText, { color: theme.primary }]}>
|
||||
{title}
|
||||
|
@ -152,43 +185,10 @@ const ScreenSharedSearch: React.FC = () => {
|
|||
) : null,
|
||||
[searchTerm]
|
||||
)
|
||||
const listItem = useCallback(({ item, section }) => {
|
||||
const listItem = useCallback(({ item, section, index }) => {
|
||||
switch (section.title) {
|
||||
case 'accounts':
|
||||
return (
|
||||
<Pressable
|
||||
style={[
|
||||
styles.itemDefault,
|
||||
styles.itemAccount,
|
||||
{ borderBottomColor: theme.border }
|
||||
]}
|
||||
onPress={() => {
|
||||
navigation.goBack()
|
||||
navigation.push('Screen-Shared-Account', { account: item })
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: item.avatar_static }}
|
||||
style={styles.itemAccountAvatar}
|
||||
/>
|
||||
<View>
|
||||
<Text numberOfLines={1}>
|
||||
<ParseEmojis
|
||||
content={item.display_name || item.username}
|
||||
emojis={item.emojis}
|
||||
size='S'
|
||||
fontBold
|
||||
/>
|
||||
</Text>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={[styles.itemAccountAcct, { color: theme.secondary }]}
|
||||
>
|
||||
@{item.acct}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
)
|
||||
return <ComponentAccount account={item} />
|
||||
case 'hashtags':
|
||||
return (
|
||||
<Pressable
|
||||
|
@ -206,50 +206,7 @@ const ScreenSharedSearch: React.FC = () => {
|
|||
</Pressable>
|
||||
)
|
||||
case 'statuses':
|
||||
return (
|
||||
<Pressable
|
||||
style={[
|
||||
styles.itemDefault,
|
||||
styles.itemAccount,
|
||||
{ borderBottomColor: theme.border }
|
||||
]}
|
||||
onPress={() => {
|
||||
navigation.goBack()
|
||||
navigation.push('Screen-Shared-Toot', { toot: item })
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: item.account.avatar_static }}
|
||||
style={styles.itemAccountAvatar}
|
||||
/>
|
||||
<View>
|
||||
<Text numberOfLines={1}>
|
||||
<ParseEmojis
|
||||
content={item.account.display_name || item.account.username}
|
||||
emojis={item.account.emojis}
|
||||
size='S'
|
||||
fontBold
|
||||
/>
|
||||
</Text>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={[styles.itemAccountAcct, { color: theme.secondary }]}
|
||||
>
|
||||
@{item.account.acct}
|
||||
</Text>
|
||||
{item.content && (
|
||||
<View style={styles.itemStatus}>
|
||||
<ParseHTML
|
||||
content={item.content}
|
||||
size='M'
|
||||
emojis={item.emojis}
|
||||
numberOfLines={2}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Pressable>
|
||||
)
|
||||
return <TimelineDefault item={item} index={index} disableDetails />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
@ -257,100 +214,42 @@ const ScreenSharedSearch: React.FC = () => {
|
|||
|
||||
return (
|
||||
<KeyboardAvoidingView behavior='padding' style={{ flex: 1 }}>
|
||||
<SafeAreaView style={{ flex: 1 }} edges={['bottom']}>
|
||||
<View style={styles.searchBar}>
|
||||
<View
|
||||
style={[styles.searchField, { borderBottomColor: theme.secondary }]}
|
||||
>
|
||||
<Icon
|
||||
name='Search'
|
||||
color={theme.primary}
|
||||
size={StyleConstants.Font.Size.M}
|
||||
style={styles.searchIcon}
|
||||
/>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.textInput,
|
||||
{
|
||||
color: theme.primary
|
||||
}
|
||||
]}
|
||||
autoFocus
|
||||
onChangeText={onChangeText}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
clearButtonMode='never'
|
||||
keyboardType='web-search'
|
||||
onSubmitEditing={({ nativeEvent: { text } }) =>
|
||||
setSearchTerm(text)
|
||||
}
|
||||
placeholder={'搜索些什么'}
|
||||
placeholderTextColor={theme.secondary}
|
||||
returnKeyType='go'
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.searchCancel}>
|
||||
<HeaderRight
|
||||
type='text'
|
||||
content='取消'
|
||||
onPress={() => navigation.goBack()}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<SectionList
|
||||
style={styles.base}
|
||||
renderItem={listItem}
|
||||
stickySectionHeadersEnabled
|
||||
sections={setctionData}
|
||||
ListEmptyComponent={listEmpty}
|
||||
keyboardShouldPersistTaps='always'
|
||||
renderSectionHeader={sectionHeader}
|
||||
renderSectionFooter={sectionFooter}
|
||||
keyExtractor={(item, index) => item + index}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
<SectionList
|
||||
style={styles.base}
|
||||
renderItem={listItem}
|
||||
stickySectionHeadersEnabled
|
||||
sections={setctionData}
|
||||
ListEmptyComponent={listEmpty}
|
||||
keyboardShouldPersistTaps='always'
|
||||
renderSectionHeader={sectionHeader}
|
||||
renderSectionFooter={sectionFooter}
|
||||
keyExtractor={(item, index) => item + index}
|
||||
SectionSeparatorComponent={ComponentSeparator}
|
||||
ItemSeparatorComponent={ComponentSeparator}
|
||||
/>
|
||||
</KeyboardAvoidingView>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
flex: 1,
|
||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingTop: 0
|
||||
minHeight: '100%'
|
||||
},
|
||||
searchBar: {
|
||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingBottom: 0,
|
||||
flexBasis: '80%',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
searchField: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
borderBottomWidth: 1.25
|
||||
},
|
||||
searchIcon: {
|
||||
marginLeft: StyleConstants.Spacing.S
|
||||
},
|
||||
searchCancel: {
|
||||
paddingHorizontal: StyleConstants.Spacing.S,
|
||||
marginLeft: StyleConstants.Spacing.S
|
||||
},
|
||||
textInput: {
|
||||
flex: 1,
|
||||
padding: StyleConstants.Spacing.S,
|
||||
...StyleConstants.FontStyle.M,
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
paddingLeft: StyleConstants.Spacing.XS,
|
||||
marginBottom:
|
||||
(StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M) / 2
|
||||
},
|
||||
|
||||
emptyBase: {
|
||||
marginTop: StyleConstants.Spacing.M,
|
||||
marginLeft:
|
||||
StyleConstants.Spacing.S +
|
||||
StyleConstants.Spacing.M +
|
||||
StyleConstants.Spacing.S
|
||||
marginVertical: StyleConstants.Spacing.Global.PagePadding,
|
||||
// paddingHorizontal: StyleConstants.Spacing.Global.PagePadding
|
||||
alignItems: 'center'
|
||||
},
|
||||
loading: { flex: 1, alignItems: 'center' },
|
||||
emptyFontSize: { ...StyleConstants.FontStyle.S },
|
||||
|
@ -364,8 +263,7 @@ const styles = StyleSheet.create({
|
|||
marginBottom: StyleConstants.Spacing.S
|
||||
},
|
||||
sectionHeader: {
|
||||
padding: StyleConstants.Spacing.M,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth
|
||||
padding: StyleConstants.Spacing.M
|
||||
},
|
||||
sectionHeaderText: {
|
||||
...StyleConstants.FontStyle.M,
|
||||
|
@ -383,23 +281,8 @@ const styles = StyleSheet.create({
|
|||
padding: StyleConstants.Spacing.S * 1.5,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth
|
||||
},
|
||||
itemAccount: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
itemAccountAvatar: {
|
||||
alignSelf: 'flex-start',
|
||||
width: StyleConstants.Avatar.S,
|
||||
height: StyleConstants.Avatar.S,
|
||||
borderRadius: 6,
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
},
|
||||
itemAccountAcct: { marginTop: StyleConstants.Spacing.XS },
|
||||
itemHashtag: {
|
||||
...StyleConstants.FontStyle.M
|
||||
},
|
||||
itemStatus: {
|
||||
marginTop: StyleConstants.Spacing.S
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { HeaderLeft } from '@root/components/Header'
|
||||
import { HeaderLeft, HeaderRight } from '@components/Header'
|
||||
import ScreenSharedAccount from '@screens/Shared/Account'
|
||||
import ScreenSharedAnnouncements from '@root/screens/Shared/Announcements'
|
||||
import ScreenSharedAnnouncements from '@screens/Shared/Announcements'
|
||||
import ScreenSharedHashtag from '@screens/Shared/Hashtag'
|
||||
import ScreenSharedImagesViewer from '@screens/Shared/ImagesViewer'
|
||||
import ScreenSharedRelationships from '@screens/Shared/Relationships'
|
||||
import ScreenSharedToot from '@screens/Shared/Toot'
|
||||
import Compose from '@screens/Shared/Compose'
|
||||
import ComposeEditAttachment from '@screens/Shared/Compose/EditAttachment'
|
||||
import ScreenSharedSearch from '@screens/Shared/Search'
|
||||
import React from 'react'
|
||||
import { Text } from 'react-native'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ScreenSharedImagesViewer from './ImagesViewer'
|
||||
import { View } from 'react-native'
|
||||
|
||||
const sharedScreens = (Stack: any) => {
|
||||
const { t } = useTranslation()
|
||||
|
@ -60,9 +60,9 @@ const sharedScreens = (Stack: any) => {
|
|||
key='Screen-Shared-Search'
|
||||
name='Screen-Shared-Search'
|
||||
component={ScreenSharedSearch}
|
||||
options={{
|
||||
stackPresentation: 'modal'
|
||||
}}
|
||||
options={({ navigation }: any) => ({
|
||||
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
|
||||
})}
|
||||
/>,
|
||||
<Stack.Screen
|
||||
key='Screen-Shared-Announcements'
|
||||
|
@ -81,6 +81,15 @@ const sharedScreens = (Stack: any) => {
|
|||
stackPresentation: 'transparentModal',
|
||||
stackAnimation: 'none'
|
||||
}}
|
||||
/>,
|
||||
<Stack.Screen
|
||||
key='Screen-Shared-Relationships'
|
||||
name='Screen-Shared-Relationships'
|
||||
component={ScreenSharedRelationships}
|
||||
options={({ route, navigation }: any) => ({
|
||||
title: route.params.account.display_name || route.params.account.name,
|
||||
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
|
||||
})}
|
||||
/>
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import client from '@api/client'
|
||||
|
||||
export const relationshipsFetch = async ({
|
||||
queryKey,
|
||||
pageParam
|
||||
}: {
|
||||
queryKey: QueryKey.Relationships
|
||||
pageParam?: { direction: 'next'; id: Mastodon.Status['id'] }
|
||||
}): Promise<Mastodon.Account[]> => {
|
||||
const [_, type, { id }] = queryKey
|
||||
let params: { [key: string]: string } = {}
|
||||
|
||||
if (pageParam) {
|
||||
switch (pageParam.direction) {
|
||||
case 'next':
|
||||
params.max_id = pageParam.id
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const res = await client({
|
||||
method: 'get',
|
||||
instance: 'local',
|
||||
url: `accounts/${id}/${type}`,
|
||||
params
|
||||
})
|
||||
return Promise.resolve(res.body)
|
||||
}
|
|
@ -3,7 +3,7 @@ const Base = 4
|
|||
export const StyleConstants = {
|
||||
Font: {
|
||||
Size: { S: 14, M: 16, L: 18 },
|
||||
LineHeight: { S: 18, M: 22, L: 30 },
|
||||
LineHeight: { S: 18, M: 22, L: 26 },
|
||||
Weight: { Bold: '600' as '600' }
|
||||
},
|
||||
FontStyle: {
|
||||
|
@ -21,5 +21,5 @@ export const StyleConstants = {
|
|||
Global: { PagePadding: Base * 4 }
|
||||
},
|
||||
|
||||
Avatar: { S: 36, M: 52, L: 104 }
|
||||
Avatar: { S: 40, M: 52, L: 104 }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue