1
0
mirror of https://github.com/tooot-app/app synced 2025-04-23 22:57:23 +02:00
This commit is contained in:
xmflsct 2022-12-15 01:41:34 +01:00
parent c4316804fc
commit a4cd24f313
9 changed files with 141 additions and 31 deletions

View File

@ -47,9 +47,27 @@ const apiGeneral = async <T = unknown>({
...(body && { data: body }) ...(body && { data: body })
}) })
.then(response => { .then(response => {
return Promise.resolve({ let links: {
body: response.data prev?: { id: string; isOffset: boolean }
}) next?: { id: string; isOffset: boolean }
} = {}
if (response.headers?.link) {
const linksParsed = response.headers.link.matchAll(
new RegExp('[?&](.*?_id|offset)=(.*?)>; *rel="(.*?)"', 'gi')
)
for (const link of linksParsed) {
switch (link[3]) {
case 'prev':
links.prev = { id: link[2], isOffset: link[1].includes('offset') }
break
case 'next':
links.next = { id: link[2], isOffset: link[1].includes('offset') }
break
}
}
}
return Promise.resolve({ body: response.data, links })
}) })
.catch(handleError()) .catch(handleError())
} }

View File

@ -37,7 +37,7 @@ const TimelineFeedback = () => {
onPress={() => onPress={() =>
navigation.push('Tab-Shared-Users', { navigation.push('Tab-Shared-Users', {
reference: 'statuses', reference: 'statuses',
id: status.id, status,
type: 'reblogged_by', type: 'reblogged_by',
count: status.reblogs_count count: status.reblogs_count
}) })
@ -59,7 +59,7 @@ const TimelineFeedback = () => {
onPress={() => onPress={() =>
navigation.push('Tab-Shared-Users', { navigation.push('Tab-Shared-Users', {
reference: 'statuses', reference: 'statuses',
id: status.id, status,
type: 'favourited_by', type: 'favourited_by',
count: status.favourites_count count: status.favourites_count
}) })

View File

@ -395,7 +395,8 @@
"statuses": { "statuses": {
"reblogged_by": "{{count}} boosted", "reblogged_by": "{{count}} boosted",
"favourited_by": "{{count}} favourited" "favourited_by": "{{count}} favourited"
} },
"resultIncomplete": "Results from a remote instance is incomplete"
} }
} }
} }

View File

@ -28,7 +28,7 @@ const AccountAttachments: React.FC<Props> = ({ account }) => {
const queryKeyParams: QueryKeyTimeline[1] = { const queryKeyParams: QueryKeyTimeline[1] = {
page: 'Account', page: 'Account',
account: account.id, account: account.id,
exclude_reblogs: true, exclude_reblogs: false,
only_media: true only_media: true
} }
const { data, refetch } = useTimelineQuery({ const { data, refetch } = useTimelineQuery({

View File

@ -25,8 +25,8 @@ const AccountInformationAccount: React.FC<Props> = ({ account }) => {
options: { enabled: account !== undefined } options: { enabled: account !== undefined }
}) })
const localInstance = instanceAccount.acct.includes('@') const localInstance = account?.acct.includes('@')
? instanceAccount.acct.includes(`@${instanceUri}`) ? account?.acct.includes(`@${instanceUri}`)
: true : true
if (account || (localInstance && instanceAccount)) { if (account || (localInstance && instanceAccount)) {
@ -52,7 +52,7 @@ const AccountInformationAccount: React.FC<Props> = ({ account }) => {
}} }}
selectable selectable
> >
@{localInstance ? instanceAccount?.acct : account?.acct} @{account?.acct}
{localInstance ? `@${instanceUri}` : null} {localInstance ? `@${instanceUri}` : null}
</CustomText> </CustomText>
{relationship?.followed_by ? t('shared.account.followed_by') : null} {relationship?.followed_by ? t('shared.account.followed_by') : null}

View File

@ -53,7 +53,7 @@ const AccountInformationStats: React.FC<Props> = ({ account, myInfo }) => {
onPress={() => onPress={() =>
navigation.push('Tab-Shared-Users', { navigation.push('Tab-Shared-Users', {
reference: 'accounts', reference: 'accounts',
id: account.id, account,
type: 'following', type: 'following',
count: account.following_count count: account.following_count
}) })
@ -77,7 +77,7 @@ const AccountInformationStats: React.FC<Props> = ({ account, myInfo }) => {
onPress={() => onPress={() =>
navigation.push('Tab-Shared-Users', { navigation.push('Tab-Shared-Users', {
reference: 'accounts', reference: 'accounts',
id: account.id, account,
type: 'followers', type: 'followers',
count: account.followers_count count: account.followers_count
}) })

View File

@ -1,16 +1,22 @@
import ComponentAccount from '@components/Account' import ComponentAccount from '@components/Account'
import { HeaderLeft } from '@components/Header' import { HeaderLeft } from '@components/Header'
import Icon from '@components/Icon'
import ComponentSeparator from '@components/Separator' import ComponentSeparator from '@components/Separator'
import CustomText from '@components/Text'
import { TabSharedStackScreenProps } from '@utils/navigation/navigators' import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
import { QueryKeyUsers, useUsersQuery } from '@utils/queryHooks/users' import { QueryKeyUsers, useUsersQuery } from '@utils/queryHooks/users'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback, useEffect } from 'react' import React, { useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { View } from 'react-native'
import { FlatList } from 'react-native-gesture-handler' import { FlatList } from 'react-native-gesture-handler'
const TabSharedUsers: React.FC<TabSharedStackScreenProps<'Tab-Shared-Users'>> = ({ const TabSharedUsers: React.FC<TabSharedStackScreenProps<'Tab-Shared-Users'>> = ({
navigation, navigation,
route: { params } route: { params }
}) => { }) => {
const { colors } = useTheme()
const { t } = useTranslation('screenTabs') const { t } = useTranslation('screenTabs')
useEffect(() => { useEffect(() => {
navigation.setOptions({ navigation.setOptions({
@ -23,8 +29,9 @@ const TabSharedUsers: React.FC<TabSharedStackScreenProps<'Tab-Shared-Users'>> =
const { data, hasNextPage, fetchNextPage, isFetchingNextPage } = useUsersQuery({ const { data, hasNextPage, fetchNextPage, isFetchingNextPage } = useUsersQuery({
...queryKey[1], ...queryKey[1],
options: { options: {
getPreviousPageParam: firstPage => firstPage.links?.prev && { min_id: firstPage.links.next }, getPreviousPageParam: firstPage =>
getNextPageParam: lastPage => lastPage.links?.next && { max_id: lastPage.links.next } firstPage.links?.prev?.id && { min_id: firstPage.links.prev.id },
getNextPageParam: lastPage => lastPage.links?.next?.id && { max_id: lastPage.links.next.id }
} }
}) })
const flattenData = data?.pages ? data.pages.flatMap(page => [...page.body]) : [] const flattenData = data?.pages ? data.pages.flatMap(page => [...page.body]) : []
@ -49,6 +56,31 @@ const TabSharedUsers: React.FC<TabSharedStackScreenProps<'Tab-Shared-Users'>> =
minIndexForVisible: 0, minIndexForVisible: 0,
autoscrollToTopThreshold: 2 autoscrollToTopThreshold: 2
}} }}
ListHeaderComponent={
data?.pages[0]?.warnIncomplete === true ? (
<View
style={{
flexDirection: 'row',
alignItems: 'center',
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
padding: StyleConstants.Spacing.S,
borderColor: colors.border,
borderWidth: 1,
borderRadius: StyleConstants.Spacing.S
}}
>
<Icon
name='AlertCircle'
color={colors.secondary}
size={StyleConstants.Font.Size.M}
style={{ marginRight: StyleConstants.Spacing.S }}
/>
<CustomText fontStyle='S' style={{ flexShrink: 1, color: colors.secondary }}>
{t('shared.users.resultIncomplete')}
</CustomText>
</View>
) : null
}
/> />
) )
} }

View File

@ -112,13 +112,13 @@ export type TabSharedStackParamList = {
'Tab-Shared-Users': 'Tab-Shared-Users':
| { | {
reference: 'accounts' reference: 'accounts'
id: Mastodon.Account['id'] account: Pick<Mastodon.Account, 'id' | 'username' | 'acct' | 'url'>
type: 'following' | 'followers' type: 'following' | 'followers'
count: number count: number
} }
| { | {
reference: 'statuses' reference: 'statuses'
id: Mastodon.Status['id'] status: Pick<Mastodon.Status, 'id'>
type: 'reblogged_by' | 'favourited_by' type: 'reblogged_by' | 'favourited_by'
count: number count: number
} }

View File

@ -6,24 +6,83 @@ import {
useInfiniteQuery, useInfiniteQuery,
UseInfiniteQueryOptions UseInfiniteQueryOptions
} from '@tanstack/react-query' } from '@tanstack/react-query'
import apiGeneral from '@api/general'
export type QueryKeyUsers = [ export type QueryKeyUsers = ['Users', TabSharedStackParamList['Tab-Shared-Users']]
'Users',
TabSharedStackParamList['Tab-Shared-Users']
]
const queryFunction = ({ const queryFunction = ({ queryKey, pageParam }: QueryFunctionContext<QueryKeyUsers>) => {
queryKey, const page = queryKey[1]
pageParam
}: QueryFunctionContext<QueryKeyUsers>) => {
const { reference, id, type } = queryKey[1]
let params: { [key: string]: string } = { ...pageParam } let params: { [key: string]: string } = { ...pageParam }
switch (page.reference) {
case 'statuses':
return apiInstance<Mastodon.Account[]>({ return apiInstance<Mastodon.Account[]>({
method: 'get', method: 'get',
url: `${reference}/${id}/${type}`, url: `${page.reference}/${page.status.id}/${page.type}`,
params
}).then(res => ({ ...res, warnIncomplete: false }))
case 'accounts':
const localInstance = page.account.username === page.account.acct
if (localInstance) {
return apiInstance<Mastodon.Account[]>({
method: 'get',
url: `${page.reference}/${page.account.id}/${page.type}`,
params
}).then(res => ({ ...res, warnIncomplete: false }))
} else {
const domain = page.account.url.match(
/^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/\n]+)/i
)?.[1]
if (!domain) {
return apiInstance<Mastodon.Account[]>({
method: 'get',
url: `${page.reference}/${page.account.id}/${page.type}`,
params
}).then(res => ({ ...res, warnIncomplete: true }))
}
return apiGeneral<{ accounts: Mastodon.Account[] }>({
method: 'get',
domain,
url: 'api/v2/search',
params: {
q: `@${page.account.acct}`,
type: 'accounts',
limit: '1'
}
})
.then(res => {
if (res?.body?.accounts?.length === 1) {
return apiGeneral<Mastodon.Account[]>({
method: 'get',
domain,
url: `api/v1/${page.reference}/${res.body.accounts[0].id}/${page.type}`,
params params
}) })
.catch(() => {
return apiInstance<Mastodon.Account[]>({
method: 'get',
url: `${page.reference}/${page.account.id}/${page.type}`,
params
}).then(res => ({ ...res, warnIncomplete: true }))
})
.then(res => ({ ...res, warnIncomplete: false }))
} else {
return apiInstance<Mastodon.Account[]>({
method: 'get',
url: `${page.reference}/${page.account.id}/${page.type}`,
params
}).then(res => ({ ...res, warnIncomplete: true }))
}
})
.catch(() => {
return apiInstance<Mastodon.Account[]>({
method: 'get',
url: `${page.reference}/${page.account.id}/${page.type}`,
params
}).then(res => ({ ...res, warnIncomplete: true }))
})
}
}
} }
const useUsersQuery = ({ const useUsersQuery = ({
@ -31,7 +90,7 @@ const useUsersQuery = ({
...queryKeyParams ...queryKeyParams
}: QueryKeyUsers[1] & { }: QueryKeyUsers[1] & {
options?: UseInfiniteQueryOptions< options?: UseInfiniteQueryOptions<
InstanceResponse<Mastodon.Account[]>, InstanceResponse<Mastodon.Account[]> & { warnIncomplete: boolean },
AxiosError AxiosError
> >
}) => { }) => {