From 3d2339c2b56ceab77e791165c91f5cff1f9d2764 Mon Sep 17 00:00:00 2001 From: xmflsct Date: Thu, 15 Dec 2022 14:28:36 +0100 Subject: [PATCH] Fix searching for remote accounts --- src/api/general.ts | 4 +- src/api/helpers/index.ts | 6 ++ src/api/instance.ts | 10 +--- src/screens/Compose.tsx | 2 +- src/screens/Compose/utils/post.ts | 6 +- src/screens/Tabs/Shared/Users.tsx | 61 +++++++++++++++++-- src/utils/queryHooks/lists.ts | 5 +- src/utils/queryHooks/search.ts | 11 ++-- src/utils/queryHooks/tags.ts | 5 +- src/utils/queryHooks/timeline.ts | 5 +- src/utils/queryHooks/users.ts | 97 ++++++++++++++----------------- src/utils/queryHooks/utils.ts | 6 +- 12 files changed, 134 insertions(+), 84 deletions(-) diff --git a/src/api/general.ts b/src/api/general.ts index e29637d3..f13e698f 100644 --- a/src/api/general.ts +++ b/src/api/general.ts @@ -1,5 +1,5 @@ import axios from 'axios' -import { ctx, handleError, userAgent } from './helpers' +import { ctx, handleError, PagedResponse, userAgent } from './helpers' export type Params = { method: 'get' | 'post' | 'put' | 'delete' @@ -19,7 +19,7 @@ const apiGeneral = async ({ params, headers, body -}: Params): Promise<{ body: T }> => { +}: Params): Promise> => { console.log( ctx.bgGreen.bold(' API general ') + ' ' + diff --git a/src/api/helpers/index.ts b/src/api/helpers/index.ts index 55871b18..65013f79 100644 --- a/src/api/helpers/index.ts +++ b/src/api/helpers/index.ts @@ -63,4 +63,10 @@ const handleError = } } +type LinkFormat = { id: string; isOffset: boolean } +export type PagedResponse = { + body: T + links: { prev?: LinkFormat; next?: LinkFormat } +} + export { ctx, handleError, userAgent } diff --git a/src/api/instance.ts b/src/api/instance.ts index ca094b4d..d6446e8f 100644 --- a/src/api/instance.ts +++ b/src/api/instance.ts @@ -1,6 +1,6 @@ import { RootState } from '@root/store' import axios, { AxiosRequestConfig } from 'axios' -import { ctx, handleError, userAgent } from './helpers' +import { ctx, handleError, PagedResponse, userAgent } from './helpers' export type Params = { method: 'get' | 'post' | 'put' | 'delete' | 'patch' @@ -14,12 +14,6 @@ export type Params = { extras?: Omit } -type LinkFormat = { id: string; isOffset: boolean } -export type InstanceResponse = { - body: T - links: { prev?: LinkFormat; next?: LinkFormat } -} - const apiInstance = async ({ method, version = 'v1', @@ -28,7 +22,7 @@ const apiInstance = async ({ headers, body, extras -}: Params): Promise> => { +}: Params): Promise> => { const { store } = require('@root/store') const state = store.getState() as RootState const instanceActive = state.instances.instances.findIndex(instance => instance.active) diff --git a/src/screens/Compose.tsx b/src/screens/Compose.tsx index 81334a8e..8b4c5a9e 100644 --- a/src/screens/Compose.tsx +++ b/src/screens/Compose.tsx @@ -286,7 +286,7 @@ const ScreenCompose: React.FC> = ({ type: 'editItem', queryKey: params.queryKey, rootQueryKey: params.rootQueryKey, - status: res.body + status: res }) break case 'deleteEdit': diff --git a/src/screens/Compose/utils/post.ts b/src/screens/Compose/utils/post.ts index c9e4f116..98ceec8b 100644 --- a/src/screens/Compose/utils/post.ts +++ b/src/screens/Compose/utils/post.ts @@ -1,4 +1,4 @@ -import apiInstance, { InstanceResponse } from '@api/instance' +import apiInstance from '@api/instance' import detectLanguage from '@helpers/detectLanguage' import { ComposeState } from '@screens/Compose/utils/types' import { RootStackParamList } from '@utils/navigation/navigators' @@ -8,7 +8,7 @@ import { getPureContent } from './processText' const composePost = async ( params: RootStackParamList['Screen-Compose'], composeState: ComposeState -): Promise> => { +): Promise => { const formData = new FormData() const detectedLanguage = await detectLanguage( @@ -74,7 +74,7 @@ const composePost = async ( ) }, body: formData - }) + }).then(res => res.body) } export default composePost diff --git a/src/screens/Tabs/Shared/Users.tsx b/src/screens/Tabs/Shared/Users.tsx index fff09d70..5183c3c6 100644 --- a/src/screens/Tabs/Shared/Users.tsx +++ b/src/screens/Tabs/Shared/Users.tsx @@ -1,15 +1,18 @@ +import apiInstance from '@api/instance' import ComponentAccount from '@components/Account' import { HeaderLeft } from '@components/Header' import Icon from '@components/Icon' import ComponentSeparator from '@components/Separator' import CustomText from '@components/Text' import { TabSharedStackScreenProps } from '@utils/navigation/navigators' +import { SearchResult } from '@utils/queryHooks/search' 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, useState } from 'react' import { useTranslation } from 'react-i18next' import { View } from 'react-native' +import { Circle, Flow } from 'react-native-animated-spinkit' import { FlatList } from 'react-native-gesture-handler' const TabSharedUsers: React.FC> = ({ @@ -26,7 +29,7 @@ const TabSharedUsers: React.FC> = }, []) const queryKey: QueryKeyUsers = ['Users', params] - const { data, hasNextPage, fetchNextPage, isFetchingNextPage } = useUsersQuery({ + const { data, isFetching, hasNextPage, fetchNextPage, isFetchingNextPage } = useUsersQuery({ ...queryKey[1], options: { getPreviousPageParam: firstPage => @@ -41,17 +44,67 @@ const TabSharedUsers: React.FC> = [hasNextPage, isFetchingNextPage] ) + const [isSearching, setIsSearching] = useState(false) + return ( } + renderItem={({ item }) => ( + { + if (data?.pages[0]?.remoteData) { + setIsSearching(true) + apiInstance({ + version: 'v2', + method: 'get', + url: 'search', + params: { + q: `@${item.acct}`, + type: 'accounts', + limit: 1, + resolve: true + } + }) + .then(res => { + setIsSearching(false) + if (res.body.accounts[0]) { + navigation.push('Tab-Shared-Account', { account: res.body.accounts[0] }) + } + }) + .catch(() => setIsSearching(false)) + } else { + navigation.push('Tab-Shared-Account', { account: item }) + } + } + }} + children={} + /> + )} onEndReached={onEndReached} onEndReachedThreshold={0.75} ItemSeparatorComponent={ComponentSeparator} + ListEmptyComponent={ + isFetching ? ( + + + + ) : null + } maintainVisibleContentPosition={{ minIndexForVisible: 0, autoscrollToTopThreshold: 2 diff --git a/src/utils/queryHooks/lists.ts b/src/utils/queryHooks/lists.ts index 0f7f0b81..b0e11989 100644 --- a/src/utils/queryHooks/lists.ts +++ b/src/utils/queryHooks/lists.ts @@ -1,4 +1,4 @@ -import apiInstance, { InstanceResponse } from '@api/instance' +import apiInstance from '@api/instance' import { AxiosError } from 'axios' import { QueryFunctionContext, @@ -9,6 +9,7 @@ import { useQuery, UseQueryOptions } from '@tanstack/react-query' +import { PagedResponse } from '@api/helpers' export type QueryKeyLists = ['Lists'] @@ -97,7 +98,7 @@ const useListAccountsQuery = ({ options, ...queryKeyParams }: QueryKeyListAccounts[1] & { - options?: UseInfiniteQueryOptions, AxiosError> + options?: UseInfiniteQueryOptions, AxiosError> }) => { const queryKey: QueryKeyListAccounts = ['ListAccounts', queryKeyParams] return useInfiniteQuery(queryKey, accountsQueryFunction, options) diff --git a/src/utils/queryHooks/search.ts b/src/utils/queryHooks/search.ts index 82d9fbb8..313a7885 100644 --- a/src/utils/queryHooks/search.ts +++ b/src/utils/queryHooks/search.ts @@ -17,17 +17,18 @@ export type SearchResult = { statuses: Mastodon.Status[] } -const queryFunction = async ({ - queryKey -}: QueryFunctionContext) => { +const queryFunction = async ({ queryKey }: QueryFunctionContext) => { const { type, term, limit = 20 } = queryKey[1] + if (!term?.length) { + return Promise.reject() + } const res = await apiInstance({ version: 'v2', method: 'get', url: 'search', params: { + q: term, ...(type && { type }), - ...(term && { q: term }), limit, resolve: true } @@ -35,7 +36,7 @@ const queryFunction = async ({ return res.body } -const useSearchQuery = ({ +const useSearchQuery = ({ options, ...queryKeyParams }: QueryKeySearch[1] & { diff --git a/src/utils/queryHooks/tags.ts b/src/utils/queryHooks/tags.ts index e350124d..28e61831 100644 --- a/src/utils/queryHooks/tags.ts +++ b/src/utils/queryHooks/tags.ts @@ -1,4 +1,4 @@ -import apiInstance, { InstanceResponse } from '@api/instance' +import apiInstance from '@api/instance' import { AxiosError } from 'axios' import { QueryFunctionContext, @@ -10,12 +10,13 @@ import { UseQueryOptions } from '@tanstack/react-query' import { infinitePageParams } from './utils' +import { PagedResponse } from '@api/helpers' export type QueryKeyFollowedTags = ['FollowedTags'] const useFollowedTagsQuery = ( params: { options?: Omit< - UseInfiniteQueryOptions, AxiosError>, + UseInfiniteQueryOptions, AxiosError>, 'getPreviousPageParam' | 'getNextPageParam' > } | void diff --git a/src/utils/queryHooks/timeline.ts b/src/utils/queryHooks/timeline.ts index ea7c0fe6..5417acc9 100644 --- a/src/utils/queryHooks/timeline.ts +++ b/src/utils/queryHooks/timeline.ts @@ -1,4 +1,4 @@ -import apiInstance, { InstanceResponse } from '@api/instance' +import apiInstance from '@api/instance' import haptics from '@components/haptics' import queryClient from '@helpers/queryClient' import { store } from '@root/store' @@ -15,6 +15,7 @@ import { import deleteItem from './timeline/deleteItem' import editItem from './timeline/editItem' import updateStatusProperty from './timeline/updateStatusProperty' +import { PagedResponse } from '@api/helpers' export type QueryKeyTimeline = [ 'Timeline', @@ -240,7 +241,7 @@ const useTimelineQuery = ({ options, ...queryKeyParams }: QueryKeyTimeline[1] & { - options?: UseInfiniteQueryOptions, AxiosError> + options?: UseInfiniteQueryOptions, AxiosError> }) => { const queryKey: QueryKeyTimeline = ['Timeline', { ...queryKeyParams }] return useInfiniteQuery(queryKey, queryFunction, { diff --git a/src/utils/queryHooks/users.ts b/src/utils/queryHooks/users.ts index 810ffa03..0e8ecbdc 100644 --- a/src/utils/queryHooks/users.ts +++ b/src/utils/queryHooks/users.ts @@ -1,4 +1,4 @@ -import apiInstance, { InstanceResponse } from '@api/instance' +import apiInstance from '@api/instance' import { TabSharedStackParamList } from '@utils/navigation/navigators' import { AxiosError } from 'axios' import { @@ -7,10 +7,15 @@ import { UseInfiniteQueryOptions } from '@tanstack/react-query' import apiGeneral from '@api/general' +import { PagedResponse } from '@api/helpers' export type QueryKeyUsers = ['Users', TabSharedStackParamList['Tab-Shared-Users']] -const queryFunction = ({ queryKey, pageParam }: QueryFunctionContext) => { +const queryFunction = async ({ + queryKey, + pageParam, + meta +}: QueryFunctionContext) => { const page = queryKey[1] let params: { [key: string]: string } = { ...pageParam } @@ -20,7 +25,7 @@ const queryFunction = ({ queryKey, pageParam }: QueryFunctionContext ({ ...res, warnIncomplete: false })) + }) case 'accounts': const localInstance = page.account.username === page.account.acct if (localInstance) { @@ -28,59 +33,47 @@ const queryFunction = ({ queryKey, pageParam }: QueryFunctionContext ({ ...res, warnIncomplete: false })) + }) } else { - const domain = page.account.url.match( - /^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/\n]+)/i - )?.[1] - if (!domain) { - return apiInstance({ + let res: PagedResponse + + try { + const domain = page.account.url.match( + /^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/\n]+)/i + )?.[1] + if (!domain?.length) { + throw new Error() + } + + const resSearch = await apiGeneral<{ accounts: Mastodon.Account[] }>({ + method: 'get', + domain, + url: 'api/v2/search', + params: { + q: `@${page.account.acct}`, + type: 'accounts', + limit: '1' + } + }) + if (resSearch?.body?.accounts?.length === 1) { + res = await apiGeneral({ + method: 'get', + domain, + url: `api/v1/${page.reference}/${resSearch.body.accounts[0].id}/${page.type}`, + params + }) + return { ...res, remoteData: true } + } else { + throw new Error() + } + } catch { + res = await apiInstance({ method: 'get', url: `${page.reference}/${page.account.id}/${page.type}`, params - }).then(res => ({ ...res, warnIncomplete: true })) + }) + return { ...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({ - method: 'get', - domain, - url: `api/v1/${page.reference}/${res.body.accounts[0].id}/${page.type}`, - params - }) - .catch(() => { - return apiInstance({ - method: 'get', - url: `${page.reference}/${page.account.id}/${page.type}`, - params - }).then(res => ({ ...res, warnIncomplete: true })) - }) - .then(res => ({ ...res, warnIncomplete: false })) - } else { - return apiInstance({ - method: 'get', - url: `${page.reference}/${page.account.id}/${page.type}`, - params - }).then(res => ({ ...res, warnIncomplete: true })) - } - }) - .catch(() => { - return apiInstance({ - method: 'get', - url: `${page.reference}/${page.account.id}/${page.type}`, - params - }).then(res => ({ ...res, warnIncomplete: true })) - }) } } } @@ -90,7 +83,7 @@ const useUsersQuery = ({ ...queryKeyParams }: QueryKeyUsers[1] & { options?: UseInfiniteQueryOptions< - InstanceResponse & { warnIncomplete: boolean }, + PagedResponse & { warnIncomplete: boolean; remoteData: boolean }, AxiosError > }) => { diff --git a/src/utils/queryHooks/utils.ts b/src/utils/queryHooks/utils.ts index 5b57931b..15f39096 100644 --- a/src/utils/queryHooks/utils.ts +++ b/src/utils/queryHooks/utils.ts @@ -1,8 +1,8 @@ -import { InstanceResponse } from '@api/instance' +import { PagedResponse } from '@api/helpers' export const infinitePageParams = { - getPreviousPageParam: (firstPage: InstanceResponse) => + getPreviousPageParam: (firstPage: PagedResponse) => firstPage.links?.prev && { min_id: firstPage.links.next }, - getNextPageParam: (lastPage: InstanceResponse) => + getNextPageParam: (lastPage: PagedResponse) => lastPage.links?.next && { max_id: lastPage.links.next } }