1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

Refine structure

This commit is contained in:
Zhiyuan Zheng
2021-01-04 18:29:02 +01:00
parent 811964e10f
commit dcb36a682d
21 changed files with 561 additions and 382 deletions

View File

@ -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

View File

@ -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
}),
[]
)

View File

@ -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(

View File

@ -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()

View File

@ -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

View File

@ -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
})}
/>
),
[]

View File

@ -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

View File

@ -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}

View File

@ -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

View File

@ -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}
/>
)}
</>

View File

@ -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
},