Improve account page

This commit is contained in:
xmflsct 2023-02-26 21:51:31 +01:00
parent 71e0c88cc2
commit 120641b95e
10 changed files with 233 additions and 254 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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