mirror of
https://github.com/tooot-app/app
synced 2025-04-23 22:57:23 +02:00
Fixed #457
This commit is contained in:
parent
c4316804fc
commit
a4cd24f313
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
})
|
})
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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({
|
||||||
|
@ -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}
|
||||||
|
@ -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
|
||||||
})
|
})
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
>
|
>
|
||||||
}) => {
|
}) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user