mirror of https://github.com/tooot-app/app
Improve account page
This commit is contained in:
parent
71e0c88cc2
commit
120641b95e
|
@ -173,6 +173,10 @@ const ComponentInstance: React.FC<Props> = ({
|
||||||
lists: { shown: false },
|
lists: { shown: false },
|
||||||
announcements: { shown: false, unread: 0 }
|
announcements: { shown: false, unread: 0 }
|
||||||
},
|
},
|
||||||
|
page_account_timeline: {
|
||||||
|
excludeBoosts: true,
|
||||||
|
excludeReplies: true
|
||||||
|
},
|
||||||
drafts: [],
|
drafts: [],
|
||||||
emojis_frequent: []
|
emojis_frequent: []
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,8 @@ const AccountAttachments: React.FC<Props> = ({ remote_id, remote_domain }) => {
|
||||||
|
|
||||||
const { data } = useTimelineQuery({
|
const { data } = useTimelineQuery({
|
||||||
page: 'Account',
|
page: 'Account',
|
||||||
|
type: 'attachments',
|
||||||
id: account?.id,
|
id: account?.id,
|
||||||
exclude_reblogs: false,
|
|
||||||
only_media: true,
|
|
||||||
...(remote_id && remote_domain && { remote_id, remote_domain }),
|
...(remote_id && remote_domain && { remote_id, remote_domain }),
|
||||||
options: { enabled: !!account?.id || (!!remote_id && !!remote_domain) }
|
options: { enabled: !!account?.id || (!!remote_id && !!remote_domain) }
|
||||||
})
|
})
|
||||||
|
@ -53,6 +52,7 @@ const AccountAttachments: React.FC<Props> = ({ remote_id, remote_domain }) => {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
height: width + StyleConstants.Spacing.Global.PagePadding * 2,
|
height: width + StyleConstants.Spacing.Global.PagePadding * 2,
|
||||||
paddingVertical: StyleConstants.Spacing.Global.PagePadding,
|
paddingVertical: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
paddingRight: StyleConstants.Spacing.Global.PagePadding,
|
||||||
borderTopWidth: 1,
|
borderTopWidth: 1,
|
||||||
borderTopColor: colors.border
|
borderTopColor: colors.border
|
||||||
}}
|
}}
|
||||||
|
@ -70,7 +70,7 @@ const AccountAttachments: React.FC<Props> = ({ remote_id, remote_domain }) => {
|
||||||
children={
|
children={
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
marginLeft: StyleConstants.Spacing.Global.PagePadding,
|
||||||
backgroundColor: colors.backgroundOverlayInvert,
|
backgroundColor: colors.backgroundOverlayInvert,
|
||||||
width: width,
|
width: width,
|
||||||
height: width,
|
height: width,
|
||||||
|
@ -110,7 +110,11 @@ const AccountAttachments: React.FC<Props> = ({ remote_id, remote_domain }) => {
|
||||||
blurhash: item.media_attachments[0]?.blurhash
|
blurhash: item.media_attachments[0]?.blurhash
|
||||||
}}
|
}}
|
||||||
dimension={{ width, height: width }}
|
dimension={{ width, height: width }}
|
||||||
style={{ marginLeft: StyleConstants.Spacing.Global.PagePadding }}
|
style={{
|
||||||
|
marginLeft: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
borderRadius: StyleConstants.BorderRadius / 2,
|
||||||
|
overflow: 'hidden'
|
||||||
|
}}
|
||||||
onPress={() => navigation.push('Tab-Shared-Toot', { toot: item })}
|
onPress={() => navigation.push('Tab-Shared-Toot', { toot: item })}
|
||||||
dim
|
dim
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -9,11 +9,12 @@ const AccountHeader: React.FC = () => {
|
||||||
const { account } = useContext(AccountContext)
|
const { account } = useContext(AccountContext)
|
||||||
|
|
||||||
const topInset = useSafeAreaInsets().top
|
const topInset = useSafeAreaInsets().top
|
||||||
|
const height = Dimensions.get('window').width / 3 + topInset
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GracefullyImage
|
<GracefullyImage
|
||||||
sources={{ default: { uri: account?.header }, static: { uri: account?.header_static } }}
|
sources={{ default: { uri: account?.header }, static: { uri: account?.header_static } }}
|
||||||
style={{ height: Dimensions.get('window').width / 3 + topInset }}
|
style={{ height }}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (account) {
|
if (account) {
|
||||||
Image.getSize(account.header, (width, height) =>
|
Image.getSize(account.header, (width, height) =>
|
||||||
|
|
|
@ -19,7 +19,8 @@ const AccountInformationFields: React.FC = () => {
|
||||||
style={{
|
style={{
|
||||||
borderTopWidth: StyleSheet.hairlineWidth,
|
borderTopWidth: StyleSheet.hairlineWidth,
|
||||||
marginBottom: StyleConstants.Spacing.M,
|
marginBottom: StyleConstants.Spacing.M,
|
||||||
borderTopColor: colors.border
|
borderTopColor: colors.border,
|
||||||
|
marginHorizontal: -StyleConstants.Spacing.Global.PagePadding
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{account.fields.map((field, index) => (
|
{account.fields.map((field, index) => (
|
||||||
|
|
|
@ -23,25 +23,6 @@ const AccountInformationStats: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.stats, { flexDirection: 'row' }]}>
|
<View style={[styles.stats, { flexDirection: 'row' }]}>
|
||||||
{account ? (
|
|
||||||
<CustomText
|
|
||||||
style={[styles.stat, { color: colors.primaryDefault }]}
|
|
||||||
children={t('shared.account.summary.statuses_count', {
|
|
||||||
count: account.statuses_count || 0
|
|
||||||
})}
|
|
||||||
onPress={() => {
|
|
||||||
pageMe && account && navigation.push('Tab-Shared-Account', { account })
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<PlaceholderLine
|
|
||||||
width={StyleConstants.Font.Size.S * 1.25}
|
|
||||||
height={StyleConstants.Font.LineHeight.S}
|
|
||||||
color={colors.shimmerDefault}
|
|
||||||
noMargin
|
|
||||||
style={{ borderRadius: 0 }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{account ? (
|
{account ? (
|
||||||
<CustomText
|
<CustomText
|
||||||
style={[styles.stat, { color: colors.primaryDefault, textAlign: 'right' }]}
|
style={[styles.stat, { color: colors.primaryDefault, textAlign: 'right' }]}
|
||||||
|
@ -95,13 +76,8 @@ const AccountInformationStats: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
stats: {
|
stats: { flex: 1, gap: StyleConstants.Spacing.L },
|
||||||
flex: 1,
|
stat: { ...StyleConstants.FontStyle.S }
|
||||||
justifyContent: 'space-between'
|
|
||||||
},
|
|
||||||
stat: {
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default AccountInformationStats
|
export default AccountInformationStats
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
import GracefullyImage from '@components/GracefullyImage'
|
||||||
import { ParseEmojis } from '@components/Parse'
|
import { ParseEmojis } from '@components/Parse'
|
||||||
import CustomText from '@components/Text'
|
import CustomText from '@components/Text'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import { Dimensions, StyleSheet, View } from 'react-native'
|
import { StyleSheet, View } from 'react-native'
|
||||||
import Animated, { Extrapolate, interpolate, useAnimatedStyle } from 'react-native-reanimated'
|
import Animated, { Extrapolate, interpolate, useAnimatedStyle } from 'react-native-reanimated'
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
import AccountContext from './Context'
|
import AccountContext from './Context'
|
||||||
|
@ -18,23 +19,11 @@ const AccountNav: React.FC<Props> = ({ scrollY }) => {
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const headerHeight = useSafeAreaInsets().top + 44
|
const headerHeight = useSafeAreaInsets().top + 44
|
||||||
|
|
||||||
const nameY =
|
|
||||||
Dimensions.get('window').width / 3 +
|
|
||||||
StyleConstants.Avatar.L -
|
|
||||||
StyleConstants.Spacing.Global.PagePadding * 2 +
|
|
||||||
StyleConstants.Spacing.M -
|
|
||||||
headerHeight
|
|
||||||
|
|
||||||
const styleOpacity = useAnimatedStyle(() => {
|
const styleOpacity = useAnimatedStyle(() => {
|
||||||
return {
|
return {
|
||||||
opacity: interpolate(scrollY.value, [0, 200], [0, 1], Extrapolate.CLAMP)
|
opacity: interpolate(scrollY.value, [0, 200], [0, 1], Extrapolate.CLAMP)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const styleMarginTop = useAnimatedStyle(() => {
|
|
||||||
return {
|
|
||||||
marginTop: interpolate(scrollY.value, [nameY, nameY + 20], [50, 0], Extrapolate.CLAMP)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
|
@ -53,20 +42,32 @@ const AccountNav: React.FC<Props> = ({ scrollY }) => {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
marginTop: useSafeAreaInsets().top + (44 - StyleConstants.Font.Size.L) / 2
|
marginTop: useSafeAreaInsets().top + StyleConstants.Font.Size.L / 2
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Animated.View style={[{ flexDirection: 'row' }, styleMarginTop]}>
|
<View
|
||||||
|
style={{ flexDirection: 'row', alignItems: 'center', gap: StyleConstants.Spacing.XS }}
|
||||||
|
>
|
||||||
{account ? (
|
{account ? (
|
||||||
<CustomText numberOfLines={1}>
|
<>
|
||||||
<ParseEmojis
|
<GracefullyImage
|
||||||
content={account.display_name || account.username}
|
sources={{ default: { uri: account.avatar_static } }}
|
||||||
emojis={account.emojis}
|
dimension={{
|
||||||
fontBold
|
width: StyleConstants.Font.Size.L,
|
||||||
|
height: StyleConstants.Font.Size.L
|
||||||
|
}}
|
||||||
|
style={{ borderRadius: 99, overflow: 'hidden' }}
|
||||||
/>
|
/>
|
||||||
</CustomText>
|
<CustomText numberOfLines={1}>
|
||||||
|
<ParseEmojis
|
||||||
|
content={account.display_name || account.username}
|
||||||
|
emojis={account.emojis}
|
||||||
|
fontBold
|
||||||
|
/>
|
||||||
|
</CustomText>
|
||||||
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</Animated.View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import menuAccount from '@components/contextMenu/account'
|
import menuAccount from '@components/contextMenu/account'
|
||||||
import menuShare from '@components/contextMenu/share'
|
import menuShare from '@components/contextMenu/share'
|
||||||
import { HeaderLeft, HeaderRight } from '@components/Header'
|
import { HeaderLeft, HeaderRight } from '@components/Header'
|
||||||
|
import Icon from '@components/Icon'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
|
||||||
import SegmentedControl from '@react-native-segmented-control/segmented-control'
|
|
||||||
import { useQueryClient } from '@tanstack/react-query'
|
|
||||||
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
||||||
|
import { queryClient } from '@utils/queryHooks'
|
||||||
import { useAccountQuery } from '@utils/queryHooks/account'
|
import { useAccountQuery } from '@utils/queryHooks/account'
|
||||||
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
|
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { useAccountStorage } from '@utils/storage/actions'
|
import { useAccountStorage } from '@utils/storage/actions'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { Fragment, useEffect, useMemo, useState } from 'react'
|
import React, { Fragment, useEffect, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Platform, Text, View } from 'react-native'
|
import { Platform, Pressable, Text, View } from 'react-native'
|
||||||
import { useSharedValue } from 'react-native-reanimated'
|
import { useSharedValue } from 'react-native-reanimated'
|
||||||
import * as DropdownMenu from 'zeego/dropdown-menu'
|
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||||
import AccountAttachments from './Attachments'
|
import AccountAttachments from './Attachments'
|
||||||
|
@ -29,7 +29,7 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||||
params: { account }
|
params: { account }
|
||||||
}
|
}
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation(['common', 'screenTabs'])
|
||||||
const { colors, mode } = useTheme()
|
const { colors, mode } = useTheme()
|
||||||
|
|
||||||
const { data, dataUpdatedAt, isFetched } = useAccountQuery({
|
const { data, dataUpdatedAt, isFetched } = useAccountQuery({
|
||||||
|
@ -46,18 +46,15 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||||
id: data?.id,
|
id: data?.id,
|
||||||
options: { enabled: account._remote ? isFetched : true }
|
options: { enabled: account._remote ? isFetched : true }
|
||||||
})
|
})
|
||||||
|
const queryKeyDefault: QueryKeyTimeline = [
|
||||||
const queryClient = useQueryClient()
|
|
||||||
const [queryKey, setQueryKey] = useState<QueryKeyTimeline>([
|
|
||||||
'Timeline',
|
'Timeline',
|
||||||
{
|
{
|
||||||
page: 'Account',
|
page: 'Account',
|
||||||
|
type: 'default',
|
||||||
id: data?.id,
|
id: data?.id,
|
||||||
exclude_reblogs: true,
|
|
||||||
only_media: false,
|
|
||||||
...(account._remote && { remote_id: account.id, remote_domain: account._remote })
|
...(account._remote && { remote_id: account.id, remote_domain: account._remote })
|
||||||
}
|
}
|
||||||
])
|
]
|
||||||
|
|
||||||
const mShare = menuShare({ type: 'account', url: data?.url })
|
const mShare = menuShare({ type: 'account', url: data?.url })
|
||||||
const mAccount = menuAccount({ type: 'account', openChange: true, account: data })
|
const mAccount = menuAccount({ type: 'account', openChange: true, account: data })
|
||||||
|
@ -72,10 +69,10 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger>
|
<DropdownMenu.Trigger>
|
||||||
<HeaderRight
|
<HeaderRight
|
||||||
accessibilityLabel={t('shared.account.actions.accessibilityLabel', {
|
accessibilityLabel={t('screenTabs:shared.account.actions.accessibilityLabel', {
|
||||||
user: account.acct
|
user: account.acct
|
||||||
})}
|
})}
|
||||||
accessibilityHint={t('shared.account.actions.accessibilityHint')}
|
accessibilityHint={t('screenTabs:shared.account.actions.accessibilityHint')}
|
||||||
content='more-horizontal'
|
content='more-horizontal'
|
||||||
onPress={() => {}}
|
onPress={() => {}}
|
||||||
background
|
background
|
||||||
|
@ -150,15 +147,10 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [mAccount])
|
}, [mAccount])
|
||||||
useEffect(() => {
|
|
||||||
navigation.setParams({ queryKey })
|
|
||||||
}, [queryKey[1]])
|
|
||||||
|
|
||||||
const scrollY = useSharedValue(0)
|
const scrollY = useSharedValue(0)
|
||||||
|
|
||||||
const page = queryKey[1]
|
const [timelineSettings, setTimelineSettings] = useAccountStorage.object('page_account_timeline')
|
||||||
|
|
||||||
const [segment, setSegment] = useState<number>(0)
|
|
||||||
const ListHeaderComponent = useMemo(() => {
|
const ListHeaderComponent = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -170,45 +162,97 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{!data?.suspended ? (
|
{!data?.suspended ? (
|
||||||
// @ts-ignore
|
<DropdownMenu.Root>
|
||||||
<SegmentedControl
|
<DropdownMenu.Trigger>
|
||||||
appearance={mode}
|
<Pressable
|
||||||
values={[t('shared.account.toots.default'), t('shared.account.toots.all')]}
|
style={{
|
||||||
selectedIndex={segment}
|
flex: 1,
|
||||||
onChange={({ nativeEvent }: any) => {
|
flexDirection: 'row',
|
||||||
setSegment(nativeEvent.selectedSegmentIndex)
|
alignItems: 'center',
|
||||||
switch (nativeEvent.selectedSegmentIndex) {
|
justifyContent: 'space-between',
|
||||||
case 0:
|
gap: StyleConstants.Spacing.XS,
|
||||||
setQueryKey([
|
paddingVertical: StyleConstants.Spacing.S,
|
||||||
queryKey[0],
|
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding
|
||||||
{
|
}}
|
||||||
...page,
|
>
|
||||||
page: 'Account',
|
<View style={{ flex: 1 }} />
|
||||||
id: data?.id,
|
<View
|
||||||
exclude_reblogs: true,
|
style={{ flex: 1 }}
|
||||||
only_media: false
|
children={
|
||||||
}
|
<CustomText
|
||||||
])
|
style={{ color: colors.secondary, alignSelf: 'center' }}
|
||||||
break
|
children={t('screenTabs:shared.account.summary.statuses_count', {
|
||||||
case 1:
|
count: data?.statuses_count || 0
|
||||||
setQueryKey([
|
})}
|
||||||
queryKey[0],
|
/>
|
||||||
{
|
}
|
||||||
...page,
|
/>
|
||||||
page: 'Account',
|
<View
|
||||||
id: data?.id,
|
style={{
|
||||||
exclude_reblogs: false,
|
flex: 1,
|
||||||
only_media: false
|
flexDirection: 'row',
|
||||||
}
|
alignItems: 'center',
|
||||||
])
|
justifyContent: 'flex-end'
|
||||||
break
|
}}
|
||||||
}
|
>
|
||||||
}}
|
<Icon name='filter' color={colors.secondary} size={StyleConstants.Font.Size.M} />
|
||||||
style={{
|
</View>
|
||||||
marginTop: StyleConstants.Spacing.M,
|
</Pressable>
|
||||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
|
</DropdownMenu.Trigger>
|
||||||
}}
|
|
||||||
/>
|
<DropdownMenu.Content>
|
||||||
|
<DropdownMenu.Group>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
key='showBoosts'
|
||||||
|
value={
|
||||||
|
(
|
||||||
|
typeof timelineSettings?.excludeBoosts === 'boolean'
|
||||||
|
? timelineSettings.excludeBoosts
|
||||||
|
: true
|
||||||
|
)
|
||||||
|
? 'off'
|
||||||
|
: 'on'
|
||||||
|
}
|
||||||
|
onValueChange={() => {
|
||||||
|
setTimelineSettings({
|
||||||
|
...timelineSettings,
|
||||||
|
excludeBoosts: !timelineSettings?.excludeBoosts
|
||||||
|
})
|
||||||
|
queryClient.refetchQueries(queryKeyDefault)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemIndicator />
|
||||||
|
<DropdownMenu.ItemTitle
|
||||||
|
children={t('screenTabs:tabs.local.options.showBoosts')}
|
||||||
|
/>
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
key='showReplies'
|
||||||
|
value={
|
||||||
|
(
|
||||||
|
typeof timelineSettings?.excludeReplies === 'boolean'
|
||||||
|
? timelineSettings.excludeReplies
|
||||||
|
: true
|
||||||
|
)
|
||||||
|
? 'off'
|
||||||
|
: 'on'
|
||||||
|
}
|
||||||
|
onValueChange={() => {
|
||||||
|
setTimelineSettings({
|
||||||
|
...timelineSettings,
|
||||||
|
excludeReplies: !timelineSettings?.excludeReplies
|
||||||
|
})
|
||||||
|
queryClient.refetchQueries(queryKeyDefault)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemTitle
|
||||||
|
children={t('screenTabs:tabs.local.options.showReplies')}
|
||||||
|
/>
|
||||||
|
<DropdownMenu.ItemIndicator />
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Root>
|
||||||
) : null}
|
) : null}
|
||||||
{data?.suspended ? (
|
{data?.suspended ? (
|
||||||
<View
|
<View
|
||||||
|
@ -226,13 +270,13 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||||
textAlign: 'center'
|
textAlign: 'center'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('shared.account.suspended')}
|
{t('screenTabs:shared.account.suspended')}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}, [segment, dataUpdatedAt, mode])
|
}, [timelineSettings, dataUpdatedAt, mode])
|
||||||
|
|
||||||
const [domain] = useAccountStorage.string('auth.account.domain')
|
const [domain] = useAccountStorage.string('auth.account.domain')
|
||||||
|
|
||||||
|
@ -252,16 +296,14 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||||
ListHeaderComponent
|
ListHeaderComponent
|
||||||
) : (
|
) : (
|
||||||
<Timeline
|
<Timeline
|
||||||
queryKey={queryKey}
|
queryKey={queryKeyDefault}
|
||||||
disableRefresh
|
disableRefresh
|
||||||
customProps={{
|
customProps={{
|
||||||
keyboardShouldPersistTaps: 'always',
|
keyboardShouldPersistTaps: 'always',
|
||||||
renderItem: ({ item }) => <TimelineDefault item={item} queryKey={queryKey} />,
|
|
||||||
onScroll: ({ nativeEvent }) => (scrollY.value = nativeEvent.contentOffset.y),
|
onScroll: ({ nativeEvent }) => (scrollY.value = nativeEvent.contentOffset.y),
|
||||||
ListHeaderComponent,
|
ListHeaderComponent,
|
||||||
maintainVisibleContentPosition: undefined,
|
refreshing: false,
|
||||||
onRefresh: () => queryClient.refetchQueries(queryKey),
|
onRefresh: () => queryClient.refetchQueries(queryKeyDefault)
|
||||||
refreshing: false
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -46,7 +46,12 @@ const TabSharedAttachments: React.FC<TabSharedStackScreenProps<'Tab-Shared-Attac
|
||||||
|
|
||||||
const queryKey: QueryKeyTimeline = [
|
const queryKey: QueryKeyTimeline = [
|
||||||
'Timeline',
|
'Timeline',
|
||||||
{ page: 'Account', id: account.id, exclude_reblogs: true, only_media: true }
|
{
|
||||||
|
page: 'Account',
|
||||||
|
type: 'attachments',
|
||||||
|
id: account.id,
|
||||||
|
...(account._remote && { remote_id: account.id, remote_domain: account._remote })
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return <Timeline queryKey={queryKey} />
|
return <Timeline queryKey={queryKey} />
|
||||||
|
|
|
@ -15,7 +15,6 @@ import { useNavState } from '@utils/navigation/navigators'
|
||||||
import { queryClient } from '@utils/queryHooks'
|
import { queryClient } from '@utils/queryHooks'
|
||||||
import { getAccountStorage, setAccountStorage } from '@utils/storage/actions'
|
import { getAccountStorage, setAccountStorage } from '@utils/storage/actions'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import { uniqBy } from 'lodash'
|
|
||||||
import { searchLocalStatus } from './search'
|
import { searchLocalStatus } from './search'
|
||||||
import deleteItem from './timeline/deleteItem'
|
import deleteItem from './timeline/deleteItem'
|
||||||
import editItem from './timeline/editItem'
|
import editItem from './timeline/editItem'
|
||||||
|
@ -43,10 +42,9 @@ export type QueryKeyTimeline = [
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
page: 'Account'
|
page: 'Account'
|
||||||
|
type: 'default' | 'attachments'
|
||||||
id?: Mastodon.Account['id']
|
id?: Mastodon.Account['id']
|
||||||
exclude_reblogs: boolean
|
// remote
|
||||||
only_media: boolean
|
|
||||||
// remote info
|
|
||||||
remote_id?: Mastodon.Account['id']
|
remote_id?: Mastodon.Account['id']
|
||||||
remote_domain?: string
|
remote_domain?: string
|
||||||
}
|
}
|
||||||
|
@ -163,139 +161,82 @@ export const queryFunctionTimeline = async ({
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Account':
|
case 'Account':
|
||||||
const reject = Promise.reject('Timeline query account id not provided')
|
if (!page.id) return Promise.reject('Timeline account missing id')
|
||||||
|
|
||||||
if (page.only_media) {
|
let typeParams
|
||||||
let res
|
switch (page.type) {
|
||||||
if (page.remote_domain && page.remote_id) {
|
case 'default':
|
||||||
res = await apiGeneral<Mastodon.Status[]>({
|
const filters = getAccountStorage.object('page_account_timeline')
|
||||||
method: 'get',
|
typeParams = {
|
||||||
domain: page.remote_domain,
|
exclude_reblogs:
|
||||||
url: `api/v1/accounts/${page.remote_id}/statuses`,
|
typeof filters?.excludeBoosts === 'boolean' ? filters.excludeBoosts : true,
|
||||||
params: {
|
exclude_replies:
|
||||||
only_media: true,
|
typeof filters?.excludeReplies === 'boolean' ? filters.excludeReplies : true
|
||||||
...params
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(res => ({
|
|
||||||
...res,
|
|
||||||
body: res.body.map(status => appendRemote.status(status, page.remote_domain!))
|
|
||||||
}))
|
|
||||||
.catch(() => {})
|
|
||||||
}
|
|
||||||
if (!res && page.id) {
|
|
||||||
res = await apiInstance<Mastodon.Status[]>({
|
|
||||||
method: 'get',
|
|
||||||
url: `accounts/${page.id}/statuses`,
|
|
||||||
params: {
|
|
||||||
only_media: true,
|
|
||||||
...params
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return res || reject
|
|
||||||
} else if (page.exclude_reblogs) {
|
|
||||||
if (pageParam && pageParam.hasOwnProperty('max_id')) {
|
|
||||||
let res
|
|
||||||
if (page.remote_domain && page.remote_id) {
|
|
||||||
res = await apiGeneral<Mastodon.Status[]>({
|
|
||||||
method: 'get',
|
|
||||||
domain: page.remote_domain,
|
|
||||||
url: `api/v1/accounts/${page.remote_id}/statuses`,
|
|
||||||
params: {
|
|
||||||
exclude_replies: true,
|
|
||||||
...params
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(res => ({
|
|
||||||
...res,
|
|
||||||
body: res.body.map(status => appendRemote.status(status, page.remote_domain!))
|
|
||||||
}))
|
|
||||||
.catch(() => {})
|
|
||||||
}
|
}
|
||||||
if (!res && page.id) {
|
break
|
||||||
res = await apiInstance<Mastodon.Status[]>({
|
case 'attachments':
|
||||||
method: 'get',
|
typeParams = { only_media: true, exclude_reblogs: true }
|
||||||
url: `accounts/${page.id}/statuses`,
|
break
|
||||||
params: {
|
|
||||||
exclude_replies: true,
|
|
||||||
...params
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return res || reject
|
|
||||||
} else {
|
|
||||||
let res
|
|
||||||
if (page.remote_domain && page.remote_id) {
|
|
||||||
res = await apiGeneral<Mastodon.Status[]>({
|
|
||||||
method: 'get',
|
|
||||||
domain: page.remote_domain,
|
|
||||||
url: `api/v1/accounts/${page.remote_id}/statuses`,
|
|
||||||
params: { exclude_replies: true }
|
|
||||||
})
|
|
||||||
.then(res => ({
|
|
||||||
...res,
|
|
||||||
body: res.body.map(status => appendRemote.status(status, page.remote_domain!))
|
|
||||||
}))
|
|
||||||
.catch(() => {})
|
|
||||||
}
|
|
||||||
if (!res && page.id) {
|
|
||||||
const resPinned = await apiInstance<(Mastodon.Status & { _pinned: boolean })[]>({
|
|
||||||
method: 'get',
|
|
||||||
url: `accounts/${page.id}/statuses`,
|
|
||||||
params: { pinned: true }
|
|
||||||
}).then(res => ({
|
|
||||||
...res,
|
|
||||||
body: res.body.map(status => {
|
|
||||||
status._pinned = true
|
|
||||||
return status
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
const resDefault = await apiInstance<Mastodon.Status[]>({
|
|
||||||
method: 'get',
|
|
||||||
url: `accounts/${page.id}/statuses`,
|
|
||||||
params: { exclude_replies: true }
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
body: uniqBy([...resPinned.body, ...resDefault.body], 'id'),
|
|
||||||
links: resDefault.links
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res || reject
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let res
|
|
||||||
if (page.remote_domain && page.remote_id) {
|
|
||||||
res = await apiGeneral<Mastodon.Status[]>({
|
|
||||||
method: 'get',
|
|
||||||
domain: page.remote_domain,
|
|
||||||
url: `api/v1/accounts/${page.remote_id}/statuses`,
|
|
||||||
params: {
|
|
||||||
...params,
|
|
||||||
exclude_replies: false,
|
|
||||||
only_media: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(res => ({
|
|
||||||
...res,
|
|
||||||
body: res.body.map(status => appendRemote.status(status, page.remote_domain!))
|
|
||||||
}))
|
|
||||||
.catch(() => {})
|
|
||||||
}
|
|
||||||
if (!res && page.id) {
|
|
||||||
res = await apiInstance<Mastodon.Status[]>({
|
|
||||||
method: 'get',
|
|
||||||
url: `accounts/${page.id}/statuses`,
|
|
||||||
params: {
|
|
||||||
...params,
|
|
||||||
exclude_replies: false,
|
|
||||||
only_media: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return res || reject
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pinned
|
||||||
|
if (page.type === 'default' && !params.hasOwnProperty('max_id')) {
|
||||||
|
if (page.remote_domain && page.remote_id) {
|
||||||
|
pinned = await apiGeneral<Mastodon.Status[]>({
|
||||||
|
method: 'get',
|
||||||
|
domain: page.remote_domain,
|
||||||
|
url: `api/v1/accounts/${page.remote_id}/statuses`,
|
||||||
|
params: { pinned: true }
|
||||||
|
})
|
||||||
|
.then(res => ({
|
||||||
|
...res,
|
||||||
|
body: res.body.map(status => appendRemote.status(status, page.remote_domain!))
|
||||||
|
}))
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
if (!pinned) {
|
||||||
|
pinned = await apiInstance<Mastodon.Status[]>({
|
||||||
|
method: 'get',
|
||||||
|
url: `accounts/${page.id}/statuses`,
|
||||||
|
params: { pinned: true }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let res
|
||||||
|
if (page.remote_domain && page.remote_id) {
|
||||||
|
res = await apiGeneral<Mastodon.Status[]>({
|
||||||
|
method: 'get',
|
||||||
|
domain: page.remote_domain,
|
||||||
|
url: `api/v1/accounts/${page.remote_id}/statuses`,
|
||||||
|
params: {
|
||||||
|
...typeParams,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(res => ({
|
||||||
|
...res,
|
||||||
|
body: res.body.map(status => appendRemote.status(status, page.remote_domain!))
|
||||||
|
}))
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
if (!res) {
|
||||||
|
res = await apiInstance<Mastodon.Status[]>({
|
||||||
|
method: 'get',
|
||||||
|
url: `accounts/${page.id}/statuses`,
|
||||||
|
params: {
|
||||||
|
...typeParams,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return pinned
|
||||||
|
? {
|
||||||
|
body: [...pinned.body.map(status => ({ ...status, _pinned: true })), ...res.body],
|
||||||
|
links: res.links
|
||||||
|
}
|
||||||
|
: res
|
||||||
|
|
||||||
case 'Hashtag':
|
case 'Hashtag':
|
||||||
return apiInstance<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
|
|
@ -53,6 +53,10 @@ export type AccountV0 = {
|
||||||
unread: number
|
unread: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
page_account_timeline: {
|
||||||
|
excludeBoosts: boolean
|
||||||
|
excludeReplies: boolean
|
||||||
|
}
|
||||||
drafts: ComposeStateDraft[]
|
drafts: ComposeStateDraft[]
|
||||||
emojis_frequent: {
|
emojis_frequent: {
|
||||||
emoji: Pick<Mastodon.Emoji, 'url' | 'shortcode' | 'static_url'>
|
emoji: Pick<Mastodon.Emoji, 'url' | 'shortcode' | 'static_url'>
|
||||||
|
|
Loading…
Reference in New Issue