From e5e74410d0d838d2cc0700ced70c565110946b3a Mon Sep 17 00:00:00 2001 From: xmflsct Date: Sat, 14 Jan 2023 15:21:31 +0100 Subject: [PATCH 01/10] Fix #659 --- package.json | 2 +- src/utils/queryHooks/timeline.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 418088f3..4be79694 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tooot", - "version": "4.8.2", + "version": "4.8.3", "description": "tooot for Mastodon", "author": "xmflsct ", "license": "GPL-3.0-or-later", diff --git a/src/utils/queryHooks/timeline.ts b/src/utils/queryHooks/timeline.ts index 315cadf0..ed9d7bf2 100644 --- a/src/utils/queryHooks/timeline.ts +++ b/src/utils/queryHooks/timeline.ts @@ -11,7 +11,7 @@ import apiInstance from '@utils/api/instance' import { featureCheck } from '@utils/helpers/featureCheck' import { useNavState } from '@utils/navigation/navigators' import { queryClient } from '@utils/queryHooks' -import { getAccountStorage } from '@utils/storage/actions' +import { getAccountStorage, setAccountStorage } from '@utils/storage/actions' import { AxiosError } from 'axios' import { uniqBy } from 'lodash' import { searchLocalStatus } from './search' @@ -85,6 +85,11 @@ export const queryFunctionTimeline = async ({ url: 'timelines/home', params }).then(res => { + if (marker && !res.body.length) { + setAccountStorage([{ key: 'read_marker_following', value: undefined }]) + return Promise.reject() + } + if (!page.showBoosts || !page.showReplies) { return { ...res, From 26d2c7851727af16ed645e86930239a5e6578b4e Mon Sep 17 00:00:00 2001 From: xmflsct Date: Sat, 14 Jan 2023 15:26:11 +0100 Subject: [PATCH 02/10] Fix #653 --- src/components/Timeline/Shared/Card.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/Timeline/Shared/Card.tsx b/src/components/Timeline/Shared/Card.tsx index 4579a88a..0ce1df8f 100644 --- a/src/components/Timeline/Shared/Card.tsx +++ b/src/components/Timeline/Shared/Card.tsx @@ -64,6 +64,9 @@ const TimelineCard: React.FC = () => { if (loading) { return null } + if (status.media_attachments.length) { + return null + } if ((!status.card?.image || !status.card.title) && !status.card?.description) { return null } From 57f1ed62b50c32f0f4c80d4de027ff16a2519d66 Mon Sep 17 00:00:00 2001 From: xmflsct Date: Sat, 14 Jan 2023 16:20:37 +0100 Subject: [PATCH 03/10] Fix #657 No need for Android, as if permission was denied last time, the next time when requesting, permission would be checked again. --- src/screens/ImageViewer/save.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/screens/ImageViewer/save.ts b/src/screens/ImageViewer/save.ts index 4fbeffef..8936ebf8 100644 --- a/src/screens/ImageViewer/save.ts +++ b/src/screens/ImageViewer/save.ts @@ -4,7 +4,7 @@ import { CameraRoll } from '@react-native-camera-roll/camera-roll' import { RootStackParamList } from '@utils/navigation/navigators' import * as FileSystem from 'expo-file-system' import i18next from 'i18next' -import { PermissionsAndroid, Platform } from 'react-native' +import { Linking, PermissionsAndroid, Platform } from 'react-native' type CommonProps = { image: RootStackParamList['Screen-ImagesViewer']['imageUrls'][0] @@ -19,7 +19,11 @@ const saveIos = async ({ image }: CommonProps) => { message: i18next.t('screenImageViewer:content.save.succeed') }) }) - .catch(() => { + .catch(err => { + if (err?.code === 'E_PHOTO_LIBRARY_AUTH_DENIED') { + Linking.openSettings() + return + } if (image.remote_url) { CameraRoll.save(image.remote_url) .then(() => { From e7ca5ba63dc58cef0df3e0d4d8e20d81f0df116a Mon Sep 17 00:00:00 2001 From: xmflsct Date: Sat, 14 Jan 2023 16:29:28 +0100 Subject: [PATCH 04/10] Fix #655 --- src/App.tsx | 2 -- src/i18n/index.ts | 9 +++++++++ src/utils/startup/timezone.ts | 14 -------------- 3 files changed, 9 insertions(+), 16 deletions(-) delete mode 100644 src/utils/startup/timezone.ts diff --git a/src/App.tsx b/src/App.tsx index cf102047..427e18ad 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,7 +10,6 @@ import log from '@utils/startup/log' import netInfo from '@utils/startup/netInfo' import push from '@utils/startup/push' import sentry from '@utils/startup/sentry' -import timezone from '@utils/startup/timezone' import { storage } from '@utils/storage' import { getGlobalStorage, @@ -39,7 +38,6 @@ dev() sentry() audio() push() -timezone() enableFreeze(true) log('log', 'App', 'delay splash') diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 32083f91..3499410d 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -1,3 +1,4 @@ +import * as Localization from 'expo-localization' import i18n from 'i18next' import { initReactI18next } from 'react-i18next' @@ -128,4 +129,12 @@ i18n.use(initReactI18next).init({ } }) +const timezone = Localization.getCalendars()[0].timeZone +if (timezone && '__setDefaultTimeZone' in Intl.DateTimeFormat) { + try { + // @ts-ignore + Intl.DateTimeFormat.__setDefaultTimeZone(timezone) + } catch {} +} + export default i18n diff --git a/src/utils/startup/timezone.ts b/src/utils/startup/timezone.ts deleted file mode 100644 index b1f0115f..00000000 --- a/src/utils/startup/timezone.ts +++ /dev/null @@ -1,14 +0,0 @@ -import * as Localization from 'expo-localization' -import log from './log' - -const timezone = () => { - log('log', 'Timezone', Localization.getCalendars()[0].timeZone || 'unknown') - if ('__setDefaultTimeZone' in Intl.DateTimeFormat) { - try { - // @ts-ignore - Intl.DateTimeFormat.__setDefaultTimeZone(Intl.DateTimeFormat.__setDefaultTimeZone('xxx')) - } catch {} - } -} - -export default timezone From e5744aba061ef0ce7e9403875af10e78e52d7a88 Mon Sep 17 00:00:00 2001 From: xmflsct Date: Sat, 14 Jan 2023 16:58:01 +0100 Subject: [PATCH 05/10] Try to fix #648 Also reported by other users --- src/components/openLink.ts | 13 ------------- src/screens/Compose/Root/Suggestions.tsx | 1 - src/utils/api/instance.ts | 2 +- src/utils/queryHooks/search.ts | 21 ++++++++++++++++----- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/components/openLink.ts b/src/components/openLink.ts index e096e313..3cda22bf 100644 --- a/src/components/openLink.ts +++ b/src/components/openLink.ts @@ -9,13 +9,7 @@ import { getGlobalStorage } from '@utils/storage/actions' import * as Linking from 'expo-linking' import * as WebBrowser from 'expo-web-browser' -export let loadingLink = false - const openLink = async (url: string, navigation?: any) => { - if (loadingLink) { - return - } - const handleNavigation = (page: 'Tab-Shared-Toot' | 'Tab-Shared-Account', options: any) => { if (navigation) { navigation.push(page, options) @@ -28,7 +22,6 @@ const openLink = async (url: string, navigation?: any) => { const match = urlMatcher(url) // If a tooot can be found if (match?.status?.id) { - loadingLink = true let response: Mastodon.Status | undefined = undefined const queryKey: QueryKeyStatus = [ @@ -39,7 +32,6 @@ const openLink = async (url: string, navigation?: any) => { if (cache) { handleNavigation('Tab-Shared-Toot', { toot: cache }) - loadingLink = false return } else { try { @@ -47,7 +39,6 @@ const openLink = async (url: string, navigation?: any) => { } catch {} if (response) { handleNavigation('Tab-Shared-Toot', { toot: response }) - loadingLink = false return } } @@ -60,7 +51,6 @@ const openLink = async (url: string, navigation?: any) => { return } - loadingLink = true let response: Mastodon.Account | undefined = undefined const queryKey: QueryKeyAccount = [ @@ -71,7 +61,6 @@ const openLink = async (url: string, navigation?: any) => { if (cache) { handleNavigation('Tab-Shared-Account', { account: cache }) - loadingLink = false return } else { try { @@ -79,13 +68,11 @@ const openLink = async (url: string, navigation?: any) => { } catch {} if (response) { handleNavigation('Tab-Shared-Account', { account: response }) - loadingLink = false return } } } - loadingLink = false switch (getGlobalStorage.string('app.browser')) { // Some links might end with an empty space at the end that triggers an error case 'internal': diff --git a/src/screens/Compose/Root/Suggestions.tsx b/src/screens/Compose/Root/Suggestions.tsx index b1ae64e9..89439541 100644 --- a/src/screens/Compose/Root/Suggestions.tsx +++ b/src/screens/Compose/Root/Suggestions.tsx @@ -38,7 +38,6 @@ const ComposeRootSuggestions: React.FC = () => { const { isFetching, data, refetch } = useSearchQuery({ type: mapSchemaToType(), term: composeState.tag?.raw.substring(1), - limit: 10, options: { enabled: false } }) useEffect(() => { diff --git a/src/utils/api/instance.ts b/src/utils/api/instance.ts index 9c71dadd..64f5b318 100644 --- a/src/utils/api/instance.ts +++ b/src/utils/api/instance.ts @@ -13,7 +13,7 @@ export type Params = { } headers?: { [key: string]: string } body?: FormData - extras?: Omit + extras?: Omit } const apiInstance = async ({ diff --git a/src/utils/queryHooks/search.ts b/src/utils/queryHooks/search.ts index 0f75cdc9..aafceff8 100644 --- a/src/utils/queryHooks/search.ts +++ b/src/utils/queryHooks/search.ts @@ -18,8 +18,8 @@ export type SearchResult = { statuses: Mastodon.Status[] } -const queryFunction = async ({ queryKey }: QueryFunctionContext) => { - const { type, term, limit = 20 } = queryKey[1] +const queryFunction = async ({ queryKey, meta }: QueryFunctionContext) => { + const { type, term, limit = 10 } = queryKey[1] if (!term?.length) { return Promise.reject('Empty search term') } @@ -32,7 +32,8 @@ const queryFunction = async ({ queryKey }: QueryFunctionContext) ...(type && { type }), limit, resolve: true - } + }, + ...(meta && { extras: meta }) }) return res.body } @@ -50,7 +51,12 @@ const useSearchQuery = ({ export const searchLocalStatus = async (uri: Mastodon.Status['uri']): Promise => { const queryKey: QueryKeySearch = ['Search', { type: 'statuses', term: uri, limit: 1 }] return await queryClient - .fetchQuery(queryKey, queryFunction, { staleTime: 3600, cacheTime: 3600 }) + .fetchQuery(queryKey, queryFunction, { + staleTime: 3600, + cacheTime: 3600, + retry: false, + meta: { timeout: 1000 } + }) .then(res => res.statuses[0]?.uri === uri || res.statuses[0]?.url === uri ? res.statuses[0] @@ -63,7 +69,12 @@ export const searchLocalAccount = async ( ): Promise => { const queryKey: QueryKeySearch = ['Search', { type: 'accounts', term: url, limit: 1 }] return await queryClient - .fetchQuery(queryKey, queryFunction, { staleTime: 3600, cacheTime: 3600 }) + .fetchQuery(queryKey, queryFunction, { + staleTime: 3600, + cacheTime: 3600, + retry: false, + meta: { timeout: 1000 } + }) .then(res => (res.accounts[0].url === url ? res.accounts[0] : Promise.reject())) } From 0efb7e5b70052deab35f786054ab0fae30e002f0 Mon Sep 17 00:00:00 2001 From: xmflsct Date: Sun, 15 Jan 2023 13:40:12 +0100 Subject: [PATCH 06/10] Fix #615 --- src/utils/push/updateExpoToken.ts | 11 ++++-- src/utils/push/useConnect.ts | 63 +++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/utils/push/updateExpoToken.ts b/src/utils/push/updateExpoToken.ts index 543e8c6e..fe2cbc67 100644 --- a/src/utils/push/updateExpoToken.ts +++ b/src/utils/push/updateExpoToken.ts @@ -4,7 +4,7 @@ import { getGlobalStorage, setGlobalStorage } from '@utils/storage/actions' import * as Notifications from 'expo-notifications' import { Platform } from 'react-native' -export const updateExpoToken = async () => { +export const updateExpoToken = async (): Promise => { const expoToken = getGlobalStorage.string('app.expo_token') if (Platform.OS === 'android') { @@ -12,16 +12,19 @@ export const updateExpoToken = async () => { } if (expoToken?.length) { - return Promise.resolve() + return Promise.resolve(expoToken) } else { if (isDevelopment) { setGlobalStorage('app.expo_token', 'ExponentPushToken[DEVELOPMENT_1]') - return Promise.resolve() + return Promise.resolve('ExponentPushToken[DEVELOPMENT_1]') } return await Notifications.getExpoPushTokenAsync({ experienceId: '@xmflsct/tooot', applicationId: 'com.xmflsct.app.tooot' - }).then(({ data }) => setGlobalStorage('app.expo_token', data)) + }).then(({ data }) => { + setGlobalStorage('app.expo_token', data) + return data + }) } } diff --git a/src/utils/push/useConnect.ts b/src/utils/push/useConnect.ts index d583c165..e77aa0d4 100644 --- a/src/utils/push/useConnect.ts +++ b/src/utils/push/useConnect.ts @@ -5,6 +5,7 @@ import apiGeneral from '@utils/api/general' import apiTooot from '@utils/api/tooot' import navigationRef from '@utils/navigation/navigationRef' import { + generateAccountKey, getAccountDetails, getGlobalStorage, setAccountStorage, @@ -12,7 +13,7 @@ import { } from '@utils/storage/actions' import { AxiosError } from 'axios' import * as Notifications from 'expo-notifications' -import { useEffect } from 'react' +import { useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import { AppState } from 'react-native' import { updateExpoToken } from './updateExpoToken' @@ -20,22 +21,32 @@ import { updateExpoToken } from './updateExpoToken' const pushUseConnect = () => { const { t } = useTranslation('screens') - useEffect(() => { - updateExpoToken() - }, []) + const startupChecked = useRef(false) const [expoToken] = useGlobalStorage.string('app.expo_token') - const pushEnabledCount = getGlobalStorage.object('accounts')?.filter(account => { - return getAccountDetails(['push'], account)?.push?.global - }).length + const accounts = getGlobalStorage.object('accounts') + const pushEnabled = accounts + ?.map(account => { + const details = getAccountDetails(['push', 'auth.domain', 'auth.account.id'], account) + if (details?.push?.global) { + return { + accountKey: generateAccountKey({ + domain: details['auth.domain'], + id: details['auth.account.id'] + }), + push: details.push + } + } + }) + .filter(d => !!d) - const connectQuery = useQuery( + const connectQuery = useQuery<{ accounts: string[] } | undefined, AxiosError>( ['tooot', { endpoint: 'push/connect' }], () => - apiTooot({ + apiTooot<{ accounts: string[] } | undefined>({ method: 'get', url: `push/connect/${expoToken}` - }), + }).then(res => res.body), { enabled: false, retry: (failureCount, error) => { @@ -48,6 +59,17 @@ const pushUseConnect = () => { refetchOnWindowFocus: false, refetchOnReconnect: false, onSettled: () => Notifications.setBadgeCountAsync(0), + onSuccess: data => { + if (!startupChecked.current && data?.accounts) { + startupChecked.current = true + for (const acct of data.accounts) { + const matchedAcct = pushEnabled?.find(p => p?.accountKey === acct) + if (matchedAcct && !matchedAcct.push.global) { + setAccountStorage([{ key: 'push', value: { ...matchedAcct.push, global: true } }]) + } + } + } + }, onError: error => { if (error?.status == 404) { displayMessage({ @@ -96,18 +118,21 @@ const pushUseConnect = () => { ) useEffect(() => { - Sentry.setTags({ expoToken, pushEnabledCount }) + updateExpoToken().then(async token => { + const badgeCount = await Notifications.getBadgeCountAsync() + if (token && (pushEnabled?.length || badgeCount)) { + connectQuery.refetch() + } + }) + }, []) - if (expoToken && pushEnabledCount) { - connectQuery.refetch() - } + useEffect(() => { + Sentry.setTags({ expoToken, pushEnabledCount: pushEnabled?.length }) const appStateListener = AppState.addEventListener('change', state => { - if (expoToken && pushEnabledCount && state === 'active') { + if (expoToken && pushEnabled?.length && state === 'active') { Notifications.getBadgeCountAsync().then(count => { - if (count > 0) { - connectQuery.refetch() - } + if (count > 0) connectQuery.refetch() }) } }) @@ -115,7 +140,7 @@ const pushUseConnect = () => { return () => { appStateListener.remove() } - }, [expoToken, pushEnabledCount]) + }, [expoToken, pushEnabled?.length]) } export default pushUseConnect From 8814161e0e71aaa569b3692e2483fada9721b86a Mon Sep 17 00:00:00 2001 From: xmflsct Date: Sun, 15 Jan 2023 18:00:58 +0100 Subject: [PATCH 07/10] Improve toot page loading --- src/components/openLink.ts | 4 +- src/screens/Tabs/Shared/Toot.tsx | 251 +++++++++++++++++-------------- src/utils/queryHooks/search.ts | 12 +- 3 files changed, 150 insertions(+), 117 deletions(-) diff --git a/src/components/openLink.ts b/src/components/openLink.ts index 3cda22bf..fbe4eabe 100644 --- a/src/components/openLink.ts +++ b/src/components/openLink.ts @@ -35,7 +35,7 @@ const openLink = async (url: string, navigation?: any) => { return } else { try { - response = await searchLocalStatus(url) + response = await searchLocalStatus(url, true) } catch {} if (response) { handleNavigation('Tab-Shared-Toot', { toot: response }) @@ -64,7 +64,7 @@ const openLink = async (url: string, navigation?: any) => { return } else { try { - response = await searchLocalAccount(url) + response = await searchLocalAccount(url, true) } catch {} if (response) { handleNavigation('Tab-Shared-Account', { account: response }) diff --git a/src/screens/Tabs/Shared/Toot.tsx b/src/screens/Tabs/Shared/Toot.tsx index a48e9202..7c791e72 100644 --- a/src/screens/Tabs/Shared/Toot.tsx +++ b/src/screens/Tabs/Shared/Toot.tsx @@ -15,7 +15,7 @@ import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Alert, FlatList, Pressable, View } from 'react-native' +import { Alert, FlatList, Platform, Pressable, View } from 'react-native' import { Circle } from 'react-native-animated-spinkit' import { Path, Svg } from 'react-native-svg' @@ -34,6 +34,8 @@ const TabSharedToot: React.FC> = ({ remote: ['Timeline', { page: 'Toot', toot: toot.id, remote: true }] } + const flRef = useRef>(null) + useEffect(() => { navigation.setOptions({ headerTitle: () => ( @@ -69,12 +71,12 @@ const TabSharedToot: React.FC> = ({ navigation.setParams({ toot, queryKey: queryKey.local }) }, [hasRemoteContent]) - const flRef = useRef(null) - const scrolled = useRef(false) + const PREV_PER_BATCH = 1 + const ancestorsCache = useRef<(Mastodon.Status & { _level?: number })[]>() + const loaded = useRef(false) const match = urlMatcher(toot.url || toot.uri) - const highlightIndex = useRef(0) - const query = useQuery<{ pages: { body: Mastodon.Status[] }[] }>( + const query = useQuery<{ pages: { body: (Mastodon.Status & { _level?: number })[] }[] }>( queryKey.local, async () => { const context = await apiInstance<{ @@ -85,15 +87,14 @@ const TabSharedToot: React.FC> = ({ url: `statuses/${toot.id}/context` }).then(res => res.body) - highlightIndex.current = context.ancestors.length - - const statuses = [...context.ancestors, { ...toot }, ...context.descendants] + ancestorsCache.current = [...context.ancestors] + const statuses = [{ ...toot }, ...context.descendants] return { pages: [ { body: statuses.map((status, index) => { - if (index < highlightIndex.current || status.id === toot.id) { + if (index === 0) { status._level = 0 return status } else { @@ -117,26 +118,6 @@ const TabSharedToot: React.FC> = ({ navigation.goBack() return } - - if (!scrolled.current) { - scrolled.current = true - const pointer = data.pages[0].body.findIndex(({ id }) => id === toot.id) - if (pointer < 1) return - const length = flRef.current?.props.data?.length - if (!length) return - try { - setTimeout(() => { - try { - flRef.current?.scrollToIndex({ - index: pointer, - viewOffset: 100 - }) - } catch {} - }, 500) - } catch (error) { - return - } - } } } ) @@ -165,19 +146,45 @@ const TabSharedToot: React.FC> = ({ return Promise.resolve([{ ...toot }]) } - highlightIndex.current = context.ancestors.length - - const statuses = [...context.ancestors, { ...toot }, ...context.descendants] + ancestorsCache.current = context.ancestors.map(ancestor => { + const localMatch = ancestorsCache.current?.find(local => local.uri === ancestor.uri) + if (localMatch) { + return { ...localMatch, _level: 0 } + } else { + return { + ...ancestor, + _remote: true, + account: { ...ancestor.account, _remote: true }, + mentions: ancestor.mentions.map(mention => ({ + ...mention, + _remote: true + })), + ...(ancestor.reblog && { + reblog: { + ...ancestor.reblog, + _remote: true, + account: { ...ancestor.reblog.account, _remote: true }, + mentions: ancestor.reblog.mentions.map(mention => ({ + ...mention, + _remote: true + })) + } + }) + } + } + }) + const statuses = [{ ...toot }, ...context.descendants] return statuses.map((status, index) => { - if (index < highlightIndex.current || status.id === toot.id) { + if (index === 0) { status._level = 0 return status + } else { + const repliedLevel: number = + statuses.find(s => s.id === status.in_reply_to_id)?._level || 0 + status._level = repliedLevel + 1 + return status } - - const repliedLevel: number = statuses.find(s => s.id === status.in_reply_to_id)?._level || 0 - status._level = repliedLevel + 1 - return status }) }, { @@ -187,7 +194,7 @@ const TabSharedToot: React.FC> = ({ match?.domain !== getAccountStorage.string('auth.domain'), staleTime: 0, refetchOnMount: true, - onSuccess: data => { + onSuccess: async data => { if ((query.data?.pages[0].body.length || 0) < 1 && data.length < 1) { navigation.goBack() return @@ -195,59 +202,85 @@ const TabSharedToot: React.FC> = ({ if ((query.data?.pages[0].body.length || 0) < data.length) { queryClient.cancelQueries(queryKey.local) - queryClient.setQueryData<{ - pages: { body: Mastodon.Status[] }[] - }>(queryKey.local, old => { - setHasRemoteContent(true) - return { - pages: [ - { - body: data.map(remote => { - const localMatch = old?.pages[0].body.find(local => local.uri === remote.uri) - if (localMatch) { - return { ...localMatch, _level: remote._level } - } else { - return { - ...remote, - _remote: true, - account: { ...remote.account, _remote: true }, - mentions: remote.mentions.map(mention => ({ ...mention, _remote: true })), - ...(remote.reblog && { - reblog: { - ...remote.reblog, - _remote: true, - account: { ...remote.reblog.account, _remote: true }, - mentions: remote.reblog.mentions.map(mention => ({ - ...mention, - _remote: true - })) - } - }) + queryClient.setQueryData<{ pages: { body: Mastodon.Status[] }[] }>( + queryKey.local, + old => { + setHasRemoteContent(true) + return { + pages: [ + { + body: data.map(remote => { + const localMatch = old?.pages[0].body.find(local => local.uri === remote.uri) + if (localMatch) { + return { ...localMatch, _level: remote._level } + } else { + return { + ...remote, + _remote: true, + account: { ...remote.account, _remote: true }, + mentions: remote.mentions.map(mention => ({ ...mention, _remote: true })), + ...(remote.reblog && { + reblog: { + ...remote.reblog, + _remote: true, + account: { ...remote.reblog.account, _remote: true }, + mentions: remote.reblog.mentions.map(mention => ({ + ...mention, + _remote: true + })) + } + }) + } } - } - }) - } - ] + }) + } + ] + } } - }) + ) } - scrolled.current = true - const pointer = data.findIndex(({ id }) => id === toot.id) - if (pointer < 1) return - const length = flRef.current?.props.data?.length - if (!length) return - try { - setTimeout(() => { - try { - flRef.current?.scrollToIndex({ - index: pointer, - viewOffset: 100 - }) - } catch {} - }, 500) - } catch (error) { - return + loaded.current = true + + if (ancestorsCache.current?.length) { + switch (Platform.OS) { + case 'ios': + for (let [] of Array( + Math.ceil(ancestorsCache.current.length / PREV_PER_BATCH) + ).entries()) { + await new Promise(promise => setTimeout(promise, 64)) + queryClient.setQueryData<{ pages: { body: Mastodon.Status[] }[] }>( + queryKey.local, + old => { + const insert = ancestorsCache.current?.slice(-PREV_PER_BATCH) + ancestorsCache.current = ancestorsCache.current?.slice(0, -PREV_PER_BATCH) + if (insert) { + old?.pages[0].body.unshift(...insert) + } + + return old + } + ) + } + break + default: + queryClient.setQueryData<{ pages: { body: Mastodon.Status[] }[] }>( + queryKey.local, + old => { + ancestorsCache.current && old?.pages[0].body.unshift(...ancestorsCache.current) + + return old + } + ) + + setTimeout(() => { + flRef.current?.scrollToIndex({ + index: (ancestorsCache.current?.length || 0), + viewOffset: 50 + }) + }, 50) + break + } } } } @@ -265,17 +298,12 @@ const TabSharedToot: React.FC> = ({ data={query.data?.pages?.[0].body} renderItem={({ item, index }) => { const prev = query.data?.pages[0].body[index - 1]?._level || 0 - const curr = item._level + const curr = item._level || 0 const next = query.data?.pages[0].body[index + 1]?._level || 0 return ( highlightIndex.current - ? Math.min(item._level - 1, MAX_LEVEL) * StyleConstants.Spacing.S - : undefined - }} + style={{ paddingLeft: Math.min(curr - 1, MAX_LEVEL) * StyleConstants.Spacing.S }} onLayout={({ nativeEvent: { layout: { height } @@ -397,7 +425,7 @@ const TabSharedToot: React.FC> = ({ ? 0 : StyleConstants.Avatar.XS + StyleConstants.Spacing.S + - Math.min(Math.max(0, leadingItem._level - 1), MAX_LEVEL) * + Math.min(Math.max(0, (leadingItem._level || 0) - 1), MAX_LEVEL) * StyleConstants.Spacing.S } /> @@ -416,21 +444,6 @@ const TabSharedToot: React.FC> = ({ ) }} - onScrollToIndexFailed={error => { - const offset = error.averageItemLength * error.index - flRef.current?.scrollToOffset({ offset }) - try { - error.index < (query.data?.pages[0].body.length || 0) && - setTimeout( - () => - flRef.current?.scrollToIndex({ - index: error.index, - viewOffset: 100 - }), - 500 - ) - } catch {} - }} ListFooterComponent={ > = ({ marginHorizontal: StyleConstants.Spacing.M }} > - {query.isFetching ? ( + {query.isLoading ? ( ) : null} } + {...(loaded.current && { maintainVisibleContentPosition: { minIndexForVisible: 0 } })} + {...(Platform.OS !== 'ios' && { + onScrollToIndexFailed: error => { + const offset = error.averageItemLength * error.index + flRef.current?.scrollToOffset({ offset }) + error.index < (ancestorsCache.current?.length || 0) && + setTimeout( + () => + flRef.current?.scrollToIndex({ + index: error.index, + viewOffset: 50 + }), + 50 + ) + } + })} /> ) } diff --git a/src/utils/queryHooks/search.ts b/src/utils/queryHooks/search.ts index aafceff8..95be0875 100644 --- a/src/utils/queryHooks/search.ts +++ b/src/utils/queryHooks/search.ts @@ -48,14 +48,17 @@ const useSearchQuery = ({ return useQuery(queryKey, queryFunction, { ...options, staleTime: 3600, cacheTime: 3600 }) } -export const searchLocalStatus = async (uri: Mastodon.Status['uri']): Promise => { +export const searchLocalStatus = async ( + uri: Mastodon.Status['uri'], + timeout: boolean = false +): Promise => { const queryKey: QueryKeySearch = ['Search', { type: 'statuses', term: uri, limit: 1 }] return await queryClient .fetchQuery(queryKey, queryFunction, { staleTime: 3600, cacheTime: 3600, retry: false, - meta: { timeout: 1000 } + ...(timeout && { meta: { timeout: 1000 } }) }) .then(res => res.statuses[0]?.uri === uri || res.statuses[0]?.url === uri @@ -65,7 +68,8 @@ export const searchLocalStatus = async (uri: Mastodon.Status['uri']): Promise => { const queryKey: QueryKeySearch = ['Search', { type: 'accounts', term: url, limit: 1 }] return await queryClient @@ -73,7 +77,7 @@ export const searchLocalAccount = async ( staleTime: 3600, cacheTime: 3600, retry: false, - meta: { timeout: 1000 } + ...(timeout && { meta: { timeout: 1000 } }) }) .then(res => (res.accounts[0].url === url ? res.accounts[0] : Promise.reject())) } From 9a289489fa720ad4183be5253df721fa63c57633 Mon Sep 17 00:00:00 2001 From: xmflsct Date: Sun, 15 Jan 2023 19:38:45 +0100 Subject: [PATCH 08/10] Fix #661 The API does not support a better way to achieve this as suggested. Though the search API accepts a "following" param, and it will return data prioritising following accounts. --- src/screens/Compose/Root/Suggestions.tsx | 1 + src/screens/Tabs/Shared/Toot.tsx | 2 +- src/utils/queryHooks/search.ts | 6 ++++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/screens/Compose/Root/Suggestions.tsx b/src/screens/Compose/Root/Suggestions.tsx index 89439541..b5c8b267 100644 --- a/src/screens/Compose/Root/Suggestions.tsx +++ b/src/screens/Compose/Root/Suggestions.tsx @@ -38,6 +38,7 @@ const ComposeRootSuggestions: React.FC = () => { const { isFetching, data, refetch } = useSearchQuery({ type: mapSchemaToType(), term: composeState.tag?.raw.substring(1), + ...(mapSchemaToType() === 'accounts' && { following: true }), options: { enabled: false } }) useEffect(() => { diff --git a/src/screens/Tabs/Shared/Toot.tsx b/src/screens/Tabs/Shared/Toot.tsx index 7c791e72..2379b634 100644 --- a/src/screens/Tabs/Shared/Toot.tsx +++ b/src/screens/Tabs/Shared/Toot.tsx @@ -275,7 +275,7 @@ const TabSharedToot: React.FC> = ({ setTimeout(() => { flRef.current?.scrollToIndex({ - index: (ancestorsCache.current?.length || 0), + index: ancestorsCache.current?.length || 0, viewOffset: 50 }) }, 50) diff --git a/src/utils/queryHooks/search.ts b/src/utils/queryHooks/search.ts index 95be0875..4f0ca471 100644 --- a/src/utils/queryHooks/search.ts +++ b/src/utils/queryHooks/search.ts @@ -9,6 +9,7 @@ export type QueryKeySearch = [ type?: 'accounts' | 'hashtags' | 'statuses' term?: string limit?: number + following?: boolean } ] @@ -19,7 +20,7 @@ export type SearchResult = { } const queryFunction = async ({ queryKey, meta }: QueryFunctionContext) => { - const { type, term, limit = 10 } = queryKey[1] + const { type, term, limit = 10, following = false } = queryKey[1] if (!term?.length) { return Promise.reject('Empty search term') } @@ -31,7 +32,8 @@ const queryFunction = async ({ queryKey, meta }: QueryFunctionContext Date: Sun, 15 Jan 2023 19:59:48 +0100 Subject: [PATCH 09/10] Fixed Android's svg spans full width --- src/components/Timeline/Default.tsx | 6 ++++-- src/screens/Tabs/Shared/Toot.tsx | 13 +++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx index ccb284af..33e396da 100644 --- a/src/components/Timeline/Default.tsx +++ b/src/components/Timeline/Default.tsx @@ -36,6 +36,7 @@ export interface Props { disableDetails?: boolean disableOnPress?: boolean isConversation?: boolean + noBackground?: boolean } // When the poll is long @@ -45,7 +46,8 @@ const TimelineDefault: React.FC = ({ highlighted = false, disableDetails = false, disableOnPress = false, - isConversation = false + isConversation = false, + noBackground = false }) => { const status = item.reblog ? item.reblog : item const rawContent = useRef([]) @@ -77,7 +79,7 @@ const TimelineDefault: React.FC = ({ padding: disableDetails ? StyleConstants.Spacing.Global.PagePadding / 1.5 : StyleConstants.Spacing.Global.PagePadding, - backgroundColor: colors.backgroundDefault, + backgroundColor: noBackground ? undefined : colors.backgroundDefault, paddingBottom: disableDetails ? StyleConstants.Spacing.Global.PagePadding / 1.5 : 0 } const main = () => ( diff --git a/src/screens/Tabs/Shared/Toot.tsx b/src/screens/Tabs/Shared/Toot.tsx index 2379b634..3e7a920e 100644 --- a/src/screens/Tabs/Shared/Toot.tsx +++ b/src/screens/Tabs/Shared/Toot.tsx @@ -310,12 +310,6 @@ const TabSharedToot: React.FC> = ({ } }) => setHeights({ ...heights, [index]: height })} > - {curr > 1 || next > 1 ? [...new Array(curr)].map((_, i) => { if (i > MAX_LEVEL) return null @@ -399,6 +393,13 @@ const TabSharedToot: React.FC> = ({ } }) : null} + {/* Date: Sun, 15 Jan 2023 20:34:22 +0100 Subject: [PATCH 10/10] Use native loading spinner The lib is not being maintained and the animation quality is bad --- package.json | 1 - src/components/Button.tsx | 4 +-- src/components/Header/Right.tsx | 4 +-- src/components/Loading.tsx | 12 ++++++++ src/components/Menu/Row.tsx | 4 +-- src/components/Timeline/Empty.tsx | 4 +-- src/components/Timeline/Footer.tsx | 4 +-- src/components/Timeline/Shared/Translate.tsx | 10 ++----- src/i18n/en/screens/tabs.json | 3 +- src/screens/Announcements.tsx | 12 ++------ .../Compose/Root/Footer/Attachments.tsx | 4 +-- src/screens/Compose/Root/Suggestions.tsx | 4 +-- src/screens/Tabs/Shared/Search/Empty.tsx | 28 +++++++++++++------ src/screens/Tabs/Shared/Search/index.tsx | 5 +--- src/screens/Tabs/Shared/Toot.tsx | 21 ++++---------- src/screens/Tabs/Shared/Users.tsx | 18 ++++++------ yarn.lock | 11 -------- 17 files changed, 69 insertions(+), 80 deletions(-) create mode 100644 src/components/Loading.tsx diff --git a/package.json b/package.json index 4be79694..2dfb17f7 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,6 @@ "react-i18next": "^12.1.4", "react-intl": "^6.2.5", "react-native": "^0.70.6", - "react-native-animated-spinkit": "^1.5.2", "react-native-blurhash": "^1.1.10", "react-native-fast-image": "^8.6.3", "react-native-feather": "^1.1.2", diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 7c81f55e..bcf90e21 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -3,7 +3,7 @@ import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { useState } from 'react' import { AccessibilityProps, Pressable, StyleProp, View, ViewStyle } from 'react-native' -import { Flow } from 'react-native-animated-spinkit' +import { Loading } from './Loading' import CustomText from './Text' export interface Props { @@ -53,7 +53,7 @@ const Button: React.FC = ({ const loadingSpinkit = () => loading ? ( - + ) : null diff --git a/src/components/Header/Right.tsx b/src/components/Header/Right.tsx index 2b799d08..e0f460a0 100644 --- a/src/components/Header/Right.tsx +++ b/src/components/Header/Right.tsx @@ -1,10 +1,10 @@ import Icon from '@components/Icon' +import { Loading } from '@components/Loading' import CustomText from '@components/Text' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React from 'react' import { AccessibilityProps, Pressable, View } from 'react-native' -import { Flow } from 'react-native-animated-spinkit' export interface Props { accessibilityLabel?: string @@ -43,7 +43,7 @@ const HeaderRight: React.FC = ({ const loadingSpinkit = () => loading ? ( - + ) : null diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx new file mode 100644 index 00000000..9307f35a --- /dev/null +++ b/src/components/Loading.tsx @@ -0,0 +1,12 @@ +import { useTheme } from '@utils/styles/ThemeManager' +import { ActivityIndicator, ViewProps } from 'react-native' + +export type Props = { + size?: 'small' | 'large' +} & ViewProps + +export const Loading: React.FC = ({ size = 'small', ...rest }) => { + const { colors } = useTheme() + + return +} diff --git a/src/components/Menu/Row.tsx b/src/components/Menu/Row.tsx index c360071f..2fcc8fbc 100644 --- a/src/components/Menu/Row.tsx +++ b/src/components/Menu/Row.tsx @@ -1,4 +1,5 @@ import Icon from '@components/Icon' +import { Loading } from '@components/Loading' import CustomText from '@components/Text' import { useAccessibility } from '@utils/accessibility/AccessibilityManager' import { StyleConstants } from '@utils/styles/constants' @@ -6,7 +7,6 @@ import { useTheme } from '@utils/styles/ThemeManager' import { ColorDefinitions } from '@utils/styles/themes' import React from 'react' import { View } from 'react-native' -import { Flow } from 'react-native-animated-spinkit' import { State, Switch, TapGestureHandler } from 'react-native-gesture-handler' export interface Props { @@ -150,7 +150,7 @@ const MenuRow: React.FC = ({ ) : null} {loading ? ( - + ) : null} diff --git a/src/components/Timeline/Empty.tsx b/src/components/Timeline/Empty.tsx index e948f343..5fe79703 100644 --- a/src/components/Timeline/Empty.tsx +++ b/src/components/Timeline/Empty.tsx @@ -1,5 +1,6 @@ import Button from '@components/Button' import Icon from '@components/Icon' +import { Loading } from '@components/Loading' import CustomText from '@components/Text' import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' @@ -7,7 +8,6 @@ import { useTheme } from '@utils/styles/ThemeManager' import React from 'react' import { useTranslation } from 'react-i18next' import { View } from 'react-native' -import { Circle } from 'react-native-animated-spinkit' export interface Props { queryKey: QueryKeyTimeline @@ -25,7 +25,7 @@ const TimelineEmpty: React.FC = ({ queryKey }) => { const children = () => { switch (status) { case 'loading': - return + return case 'error': return ( <> diff --git a/src/components/Timeline/Footer.tsx b/src/components/Timeline/Footer.tsx index 391c498f..db50cc12 100644 --- a/src/components/Timeline/Footer.tsx +++ b/src/components/Timeline/Footer.tsx @@ -1,4 +1,5 @@ import Icon from '@components/Icon' +import { Loading } from '@components/Loading' import CustomText from '@components/Text' import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' @@ -6,7 +7,6 @@ import { useTheme } from '@utils/styles/ThemeManager' import React from 'react' import { Trans } from 'react-i18next' import { View } from 'react-native' -import { Circle } from 'react-native-animated-spinkit' export interface Props { queryKey: QueryKeyTimeline @@ -31,7 +31,7 @@ const TimelineFooter: React.FC = ({ queryKey, disableInfinity }) => { }} > {!disableInfinity && hasNextPage ? ( - + ) : ( { @@ -111,13 +111,7 @@ const TimelineTranslate = () => { }) : t('componentTimeline:shared.translate.default')} - {isFetching ? ( - - ) : null} + {isFetching ? : null} {devView()} {data && data.error === undefined diff --git a/src/i18n/en/screens/tabs.json b/src/i18n/en/screens/tabs.json index d4dd8466..051ab6e5 100644 --- a/src/i18n/en/screens/tabs.json +++ b/src/i18n/en/screens/tabs.json @@ -373,7 +373,8 @@ "hashtags": "Hashtag", "statuses": "Toot" }, - "notFound": "Cannot find {{searchTerm}} related {{type}}" + "notFound": "Cannot find {{searchTerm}} related {{type}}", + "noResult": "Cannot find anything, please try a different term" }, "toot": { "name": "Discussions", diff --git a/src/screens/Announcements.tsx b/src/screens/Announcements.tsx index 681feb41..f52b3019 100644 --- a/src/screens/Announcements.tsx +++ b/src/screens/Announcements.tsx @@ -1,5 +1,6 @@ import Button from '@components/Button' import haptics from '@components/haptics' +import { Loading } from '@components/Loading' import { ParseHTML } from '@components/Parse' import RelativeTime from '@components/RelativeTime' import CustomText from '@components/Text' @@ -20,7 +21,6 @@ import { StyleSheet, View } from 'react-native' -import { Circle } from 'react-native-animated-spinkit' import FastImage from 'react-native-fast-image' import { FlatList, ScrollView } from 'react-native-gesture-handler' import { SafeAreaView } from 'react-native-safe-area-context' @@ -191,14 +191,8 @@ const ScreenAnnouncements: React.FC const ListEmptyComponent = () => { return ( - - + + ) } diff --git a/src/screens/Compose/Root/Footer/Attachments.tsx b/src/screens/Compose/Root/Footer/Attachments.tsx index 611e1685..008345c6 100644 --- a/src/screens/Compose/Root/Footer/Attachments.tsx +++ b/src/screens/Compose/Root/Footer/Attachments.tsx @@ -1,6 +1,7 @@ import Button from '@components/Button' import haptics from '@components/haptics' import Icon from '@components/Icon' +import { Loading } from '@components/Loading' import { MAX_MEDIA_ATTACHMENTS } from '@components/mediaSelector' import CustomText from '@components/Text' import { useActionSheet } from '@expo/react-native-action-sheet' @@ -11,7 +12,6 @@ import { useTheme } from '@utils/styles/ThemeManager' import React, { RefObject, useContext, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import { FlatList, Pressable, StyleSheet, View } from 'react-native' -import { Circle } from 'react-native-animated-spinkit' import FastImage from 'react-native-fast-image' import ComposeContext from '../../utils/createContext' import { ExtendedAttachment } from '../../utils/types' @@ -135,7 +135,7 @@ const ComposeAttachments: React.FC = ({ accessibleRefAttachments }) => { backgroundColor: colors.backgroundOverlayInvert }} > - + ) : ( { key='listEmpty' style={{ flex: 1, alignItems: 'center', marginVertical: StyleConstants.Spacing.M }} > - + ) : ( <>{main()} diff --git a/src/screens/Tabs/Shared/Search/Empty.tsx b/src/screens/Tabs/Shared/Search/Empty.tsx index af8ff166..1d5b4b9b 100644 --- a/src/screens/Tabs/Shared/Search/Empty.tsx +++ b/src/screens/Tabs/Shared/Search/Empty.tsx @@ -1,4 +1,5 @@ import ComponentHashtag from '@components/Hashtag' +import { Loading } from '@components/Loading' import ComponentSeparator from '@components/Separator' import CustomText from '@components/Text' import { useTrendsQuery } from '@utils/queryHooks/trends' @@ -6,26 +7,37 @@ import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React from 'react' import { Trans, useTranslation } from 'react-i18next' -import { StyleSheet, TextInput, View } from 'react-native' -import { Circle } from 'react-native-animated-spinkit' +import { StyleSheet, View } from 'react-native' export interface Props { isFetching: boolean - inputRef: React.RefObject - setSearchTerm: React.Dispatch> + searchTerm: string } -const SearchEmpty: React.FC = ({ isFetching, inputRef, setSearchTerm }) => { +const SearchEmpty: React.FC = ({ isFetching, searchTerm }) => { const { colors } = useTheme() const { t } = useTranslation('screenTabs') const trendsTags = useTrendsQuery({ type: 'tags' }) return ( - + {isFetching ? ( - - + + + + ) : searchTerm.length ? ( + + ) : ( <> diff --git a/src/screens/Tabs/Shared/Search/index.tsx b/src/screens/Tabs/Shared/Search/index.tsx index 89c530d0..d06e239e 100644 --- a/src/screens/Tabs/Shared/Search/index.tsx +++ b/src/screens/Tabs/Shared/Search/index.tsx @@ -131,7 +131,6 @@ const TabSharedSearch: React.FC> style={{ flex: 1 }} > { switch (section.title) { @@ -146,9 +145,7 @@ const TabSharedSearch: React.FC> } }} stickySectionHeadersEnabled - ListEmptyComponent={ - - } + ListEmptyComponent={} keyboardShouldPersistTaps='always' renderSectionHeader={({ section: { translation } }) => ( > = ({ @@ -109,7 +109,7 @@ const TabSharedToot: React.FC> = ({ } }, { - placeholderData: { pages: [{ body: [toot] }] }, + placeholderData: { pages: [{ body: [{ ...toot, _level: 0 }] }] }, enabled: !toot._remote, staleTime: 0, refetchOnMount: true, @@ -121,7 +121,7 @@ const TabSharedToot: React.FC> = ({ } } ) - useQuery( + const remoteQuery = useQuery( queryKey.remote, async () => { const domain = match?.domain @@ -446,18 +446,9 @@ const TabSharedToot: React.FC> = ({ ) }} ListFooterComponent={ - - {query.isLoading ? ( - - ) : null} - + query.isFetching || remoteQuery.isFetching ? ( + } /> + ) : null } {...(loaded.current && { maintainVisibleContentPosition: { minIndexForVisible: 0 } })} {...(Platform.OS !== 'ios' && { diff --git a/src/screens/Tabs/Shared/Users.tsx b/src/screens/Tabs/Shared/Users.tsx index e161b1f8..b2994a6e 100644 --- a/src/screens/Tabs/Shared/Users.tsx +++ b/src/screens/Tabs/Shared/Users.tsx @@ -1,6 +1,7 @@ import ComponentAccount from '@components/Account' import { HeaderLeft } from '@components/Header' import Icon from '@components/Icon' +import { Loading } from '@components/Loading' import ComponentSeparator from '@components/Separator' import CustomText from '@components/Text' import apiInstance from '@utils/api/instance' @@ -13,7 +14,6 @@ import { useTheme } from '@utils/styles/ThemeManager' import React, { 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> = ({ @@ -36,7 +36,7 @@ const TabSharedUsers: React.FC> = ...queryKey[1] }) - const [isSearching, setIsSearching] = useState(false) + const [isSearching, setIsSearching] = useState(null) return ( > = minHeight: '100%', paddingVertical: StyleConstants.Spacing.Global.PagePadding }} - renderItem={({ item }) => ( + renderItem={({ item, index }) => ( { if (data?.pages[0]?.remoteData) { - setIsSearching(true) + setIsSearching(index) apiInstance({ version: 'v2', method: 'get', @@ -66,18 +66,18 @@ const TabSharedUsers: React.FC> = } }) .then(res => { - setIsSearching(false) + setIsSearching(null) if (res.body.accounts[0]) { navigation.push('Tab-Shared-Account', { account: res.body.accounts[0] }) } }) - .catch(() => setIsSearching(false)) + .catch(() => setIsSearching(null)) } else { navigation.push('Tab-Shared-Account', { account: item }) } } }} - children={} + children={} /> )} onEndReached={() => hasNextPage && !isFetchingNextPage && fetchNextPage()} @@ -93,7 +93,7 @@ const TabSharedUsers: React.FC> = alignItems: 'center' }} > - + ) : null } diff --git a/yarn.lock b/yarn.lock index ef1812a4..224235d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9549,16 +9549,6 @@ __metadata: languageName: node linkType: hard -"react-native-animated-spinkit@npm:^1.5.2": - version: 1.5.2 - resolution: "react-native-animated-spinkit@npm:1.5.2" - peerDependencies: - react: "*" - react-native: "*" - checksum: 5d84b0958b3f9db5223d7c2af242eae45f133cf50715be56f1e266d7c04fe5ecdbd7e01b08146748b06daaf914fe8bc64f13df7faaee98bc687094ca10cae61e - languageName: node - linkType: hard - "react-native-blurhash@npm:^1.1.10": version: 1.1.10 resolution: "react-native-blurhash@npm:1.1.10" @@ -11412,7 +11402,6 @@ __metadata: react-i18next: ^12.1.4 react-intl: ^6.2.5 react-native: ^0.70.6 - react-native-animated-spinkit: ^1.5.2 react-native-blurhash: ^1.1.10 react-native-clean-project: ^4.0.1 react-native-fast-image: ^8.6.3