Simplify list logic further to prevent misuse (#3334)
* simplify list logic further more simplification simplify by removing `isEmpty` use `isFetchingNextPage` everywhere for clarity change `isFetching` to `isFetchingNextPage` for clarity remove some useless `useMemo`s move `renderItem` and `keyExtractor` out of component * clean bundle size check * update deploy * adjust * adjust * one test * try now * test it * donezio/stable
parent
b1bd7ab6e3
commit
8e393b16f5
|
@ -82,15 +82,16 @@ jobs:
|
||||||
uses: expo/expo-github-action/fingerprint@main
|
uses: expo/expo-github-action/fingerprint@main
|
||||||
with:
|
with:
|
||||||
previous-git-commit: ${{ steps.base-commit.outputs.base-commit }}
|
previous-git-commit: ${{ steps.base-commit.outputs.base-commit }}
|
||||||
|
args:
|
||||||
|
|
||||||
- name: 👀 Debug fingerprint
|
- name: 👀 Debug fingerprint
|
||||||
id: fingerprint-debug
|
id: fingerprint-debug
|
||||||
run: |
|
run: |
|
||||||
echo "fingerprint-diff=${{ steps.fingerprint.outputs.fingerprint-diff }}"
|
|
||||||
echo "previousGitCommit=${{ steps.fingerprint.outputs.previous-git-commit }} currentGitCommit=${{ steps.fingerprint.outputs.current-git-commit }}"
|
echo "previousGitCommit=${{ steps.fingerprint.outputs.previous-git-commit }} currentGitCommit=${{ steps.fingerprint.outputs.current-git-commit }}"
|
||||||
echo "isPreviousFingerprintEmpty=${{ steps.fingerprint.outputs.previous-fingerprint == '' }}"
|
echo "isPreviousFingerprintEmpty=${{ steps.fingerprint.outputs.previous-fingerprint == '' }}"
|
||||||
|
|
||||||
if [ "${{ steps.fingerprint.outputs.fingerprint-diff }}" != '[]' ]; then
|
fingerprintDiff="${{ steps.fingerprint.outputs.fingerprint-diff }}"
|
||||||
|
if [[ $fingerprintDiff =~ "bareRncliAutolinking" || $fingerprintDiff =~ "expoAutolinkingAndroid" || $fingerprintDiff =~ "expoAutolinkingIos" ]]; then
|
||||||
echo fingerprint-is-different="true" >> "$GITHUB_OUTPUT"
|
echo fingerprint-is-different="true" >> "$GITHUB_OUTPUT"
|
||||||
else
|
else
|
||||||
echo fingerprint-is-different="false" >> "$GITHUB_OUTPUT"
|
echo fingerprint-is-different="false" >> "$GITHUB_OUTPUT"
|
||||||
|
@ -98,37 +99,37 @@ jobs:
|
||||||
|
|
||||||
- name: 🔨 Setup EAS
|
- name: 🔨 Setup EAS
|
||||||
uses: expo/expo-github-action@v8
|
uses: expo/expo-github-action@v8
|
||||||
if: ${{ steps.fingerprint.outputs.fingerprint-diff == '[]' }}
|
if: ${{ steps.fingerprint-debug.outputs.fingerprint-is-different == 'false'}}
|
||||||
with:
|
with:
|
||||||
expo-version: latest
|
expo-version: latest
|
||||||
eas-version: latest
|
eas-version: latest
|
||||||
token: ${{ secrets.EXPO_TOKEN }}
|
token: ${{ secrets.EXPO_TOKEN }}
|
||||||
|
|
||||||
- name: ⛏️ Setup Expo
|
- name: ⛏️ Setup Expo
|
||||||
if: ${{ steps.fingerprint.outputs.fingerprint-diff == '[]' }}
|
if: ${{ steps.fingerprint-debug.outputs.fingerprint-is-different == 'false'}}
|
||||||
run: yarn global add eas-cli-local-build-plugin
|
run: yarn global add eas-cli-local-build-plugin
|
||||||
|
|
||||||
- name: 🪛 Setup jq
|
- name: 🪛 Setup jq
|
||||||
if: ${{ steps.fingerprint.outputs.fingerprint-diff == '[]' }}
|
if: ${{ steps.fingerprint-debug.outputs.fingerprint-is-different == 'false'}}
|
||||||
uses: dcarbone/install-jq-action@v2
|
uses: dcarbone/install-jq-action@v2
|
||||||
|
|
||||||
- name: 🔤 Compile Translations
|
- name: 🔤 Compile Translations
|
||||||
if: ${{ steps.fingerprint.outputs.fingerprint-diff == '[]' }}
|
if: ${{ steps.fingerprint-debug.outputs.fingerprint-is-different == 'false'}}
|
||||||
run: yarn intl:build
|
run: yarn intl:build
|
||||||
|
|
||||||
- name: ✏️ Write environment variables
|
- name: ✏️ Write environment variables
|
||||||
if: ${{ steps.fingerprint.outputs.fingerprint-diff == '[]' }}
|
if: ${{ steps.fingerprint-debug.outputs.fingerprint-is-different == 'false'}}
|
||||||
run: |
|
run: |
|
||||||
export json='${{ secrets.GOOGLE_SERVICES_TOKEN }}'
|
export json='${{ secrets.GOOGLE_SERVICES_TOKEN }}'
|
||||||
echo "${{ secrets.ENV_TOKEN }}" > .env
|
echo "${{ secrets.ENV_TOKEN }}" > .env
|
||||||
echo "$json" > google-services.json
|
echo "$json" > google-services.json
|
||||||
|
|
||||||
- name: 🏗️ Create Bundle
|
- name: 🏗️ Create Bundle
|
||||||
if: ${{ steps.fingerprint.outputs.fingerprint-diff == '[]' }}
|
if: ${{ steps.fingerprint-debug.outputs.fingerprint-is-different == 'false'}}
|
||||||
run: EXPO_PUBLIC_ENV="${{ inputs.channel || 'testflight' }}" yarn export
|
run: EXPO_PUBLIC_ENV="${{ inputs.channel || 'testflight' }}" yarn export
|
||||||
|
|
||||||
- name: 📦 Package Bundle and 🚀 Deploy
|
- name: 📦 Package Bundle and 🚀 Deploy
|
||||||
if: ${{ steps.fingerprint.outputs.fingerprint-diff == '[]' }}
|
if: ${{ steps.fingerprint-debug.outputs.fingerprint-is-different == 'false'}}
|
||||||
run: yarn use-build-number bash scripts/bundleUpdate.sh
|
run: yarn use-build-number bash scripts/bundleUpdate.sh
|
||||||
env:
|
env:
|
||||||
DENIS_API_KEY: ${{ secrets.DENIS_API_KEY }}
|
DENIS_API_KEY: ${{ secrets.DENIS_API_KEY }}
|
||||||
|
|
|
@ -80,7 +80,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
base_path: 'stats-base.json'
|
base_path: 'stats-base.json'
|
||||||
pr_path: '../stats-new.json'
|
pr_path: '../stats-new.json'
|
||||||
excluded_assets: '(.+).js.map|(.+).json|(.+).png'
|
excluded_assets: '(.+).chunk.js|(.+).js.map|(.+).json|(.+).png'
|
||||||
|
|
||||||
- name: 🔍 Find old comment if it exists
|
- name: 🔍 Find old comment if it exists
|
||||||
uses: peter-evans/find-comment@v2
|
uses: peter-evans/find-comment@v2
|
||||||
|
@ -99,7 +99,7 @@ jobs:
|
||||||
const body = `<!-- webpack-analyzer comment -->
|
const body = `<!-- webpack-analyzer comment -->
|
||||||
| Old size | New size | Diff |
|
| Old size | New size | Diff |
|
||||||
|----------|----------|-----------------------|
|
|----------|----------|-----------------------|
|
||||||
| ${{ steps.get-diff.outputs.base_file_string }} | ${{ steps.get-diff.outputs.pr_file_string }} | ${{ steps.get-diff.outputs.diff_file_string }} (${{ steps.get-diff.outputs.percent }}% |
|
| ${{ steps.get-diff.outputs.base_file_string }} | ${{ steps.get-diff.outputs.pr_file_string }} | ${{ steps.get-diff.outputs.diff_file_string }} (${{ steps.get-diff.outputs.percent }}%) |
|
||||||
`;
|
`;
|
||||||
|
|
||||||
github.rest.issues.createComment({
|
github.rest.issues.createComment({
|
||||||
|
|
|
@ -39,7 +39,6 @@
|
||||||
"perf:test:results": "NODE_ENV=test flashlight report .perf/results.json",
|
"perf:test:results": "NODE_ENV=test flashlight report .perf/results.json",
|
||||||
"perf:measure": "NODE_ENV=test flashlight measure",
|
"perf:measure": "NODE_ENV=test flashlight measure",
|
||||||
"intl:build": "yarn intl:extract && yarn intl:compile",
|
"intl:build": "yarn intl:extract && yarn intl:compile",
|
||||||
"intl:check": "yarn intl:extract && git diff-index -G'(^[^\\*# /])|(^#\\w)|(^\\s+[^\\*#/])' HEAD || (echo '\n⚠️ i18n detected un-extracted translations\n' && exit 1)",
|
|
||||||
"intl:extract": "lingui extract",
|
"intl:extract": "lingui extract",
|
||||||
"intl:compile": "lingui compile",
|
"intl:compile": "lingui compile",
|
||||||
"nuke": "rm -rf ./node_modules && rm -rf ./ios && rm -rf ./android",
|
"nuke": "rm -rf ./node_modules && rm -rf ./ios && rm -rf ./android",
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {useNavigation} from '@react-navigation/core'
|
|
||||||
import {StackActions} from '@react-navigation/native'
|
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useNavigation} from '@react-navigation/core'
|
||||||
|
import {StackActions} from '@react-navigation/native'
|
||||||
|
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
import {CenteredView} from 'view/com/util/Views'
|
import {CenteredView} from 'view/com/util/Views'
|
||||||
|
|
|
@ -1,47 +1,54 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {View} from 'react-native'
|
|
||||||
import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
|
import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
|
||||||
import {Trans} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {List} from '#/view/com/util/List'
|
|
||||||
import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
|
|
||||||
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
|
||||||
import {useLikedByQuery} from '#/state/queries/post-liked-by'
|
import {useLikedByQuery} from '#/state/queries/post-liked-by'
|
||||||
|
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
import {ListFooter} from '#/components/Lists'
|
import {cleanError} from 'lib/strings/errors'
|
||||||
|
import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
|
||||||
|
import {List} from '#/view/com/util/List'
|
||||||
|
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
function renderItem({item}: {item: GetLikes.Like}) {
|
||||||
import {Loader} from '#/components/Loader'
|
return <ProfileCardWithFollowBtn key={item.actor.did} profile={item.actor} />
|
||||||
import {Text} from '#/components/Typography'
|
}
|
||||||
|
|
||||||
|
function keyExtractor(item: GetLikes.Like) {
|
||||||
|
return item.actor.did
|
||||||
|
}
|
||||||
|
|
||||||
export function LikedByList({uri}: {uri: string}) {
|
export function LikedByList({uri}: {uri: string}) {
|
||||||
const t = useTheme()
|
const {_} = useLingui()
|
||||||
|
const initialNumToRender = useInitialNumToRender()
|
||||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: resolvedUri,
|
data: resolvedUri,
|
||||||
error: resolveError,
|
error: resolveError,
|
||||||
isFetching: isFetchingResolvedUri,
|
isLoading: isUriLoading,
|
||||||
} = useResolveUriQuery(uri)
|
} = useResolveUriQuery(uri)
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
isFetching,
|
isLoading: isLikedByLoading,
|
||||||
isFetched,
|
isFetchingNextPage,
|
||||||
isRefetching,
|
|
||||||
hasNextPage,
|
hasNextPage,
|
||||||
fetchNextPage,
|
fetchNextPage,
|
||||||
isError,
|
|
||||||
error: likedByError,
|
error: likedByError,
|
||||||
refetch,
|
refetch,
|
||||||
} = useLikedByQuery(resolvedUri?.uri)
|
} = useLikedByQuery(resolvedUri?.uri)
|
||||||
|
|
||||||
|
const error = resolveError || likedByError
|
||||||
|
const isError = !!resolveError || !!likedByError
|
||||||
|
|
||||||
const likes = React.useMemo(() => {
|
const likes = React.useMemo(() => {
|
||||||
if (data?.pages) {
|
if (data?.pages) {
|
||||||
return data.pages.flatMap(page => page.likes)
|
return data.pages.flatMap(page => page.likes)
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
}, [data])
|
}, [data])
|
||||||
const initialNumToRender = useInitialNumToRender()
|
|
||||||
const error = resolveError || likedByError
|
|
||||||
|
|
||||||
const onRefresh = React.useCallback(async () => {
|
const onRefresh = React.useCallback(async () => {
|
||||||
setIsPTRing(true)
|
setIsPTRing(true)
|
||||||
|
@ -54,56 +61,47 @@ export function LikedByList({uri}: {uri: string}) {
|
||||||
}, [refetch, setIsPTRing])
|
}, [refetch, setIsPTRing])
|
||||||
|
|
||||||
const onEndReached = React.useCallback(async () => {
|
const onEndReached = React.useCallback(async () => {
|
||||||
if (isFetching || !hasNextPage || isError) return
|
if (isFetchingNextPage || !hasNextPage || isError) return
|
||||||
try {
|
try {
|
||||||
await fetchNextPage()
|
await fetchNextPage()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Failed to load more likes', {message: err})
|
logger.error('Failed to load more likes', {message: err})
|
||||||
}
|
}
|
||||||
}, [isFetching, hasNextPage, isError, fetchNextPage])
|
}, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
|
||||||
|
|
||||||
const renderItem = React.useCallback(({item}: {item: GetLikes.Like}) => {
|
if (likes.length < 1) {
|
||||||
return (
|
return (
|
||||||
<ProfileCardWithFollowBtn key={item.actor.did} profile={item.actor} />
|
<ListMaybePlaceholder
|
||||||
)
|
isLoading={isUriLoading || isLikedByLoading}
|
||||||
}, [])
|
isError={isError}
|
||||||
|
emptyType="results"
|
||||||
if (isFetchingResolvedUri || !isFetched) {
|
emptyMessage={_(
|
||||||
return (
|
msg`Nobody has liked this yet. Maybe you should be the first!`,
|
||||||
<View style={[a.w_full, a.align_center, a.p_lg]}>
|
)}
|
||||||
<Loader size="xl" />
|
errorMessage={cleanError(resolveError || error)}
|
||||||
</View>
|
onRetry={isError ? refetch : undefined}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return likes.length ? (
|
return (
|
||||||
<List
|
<List
|
||||||
data={likes}
|
data={likes}
|
||||||
keyExtractor={item => item.actor.did}
|
renderItem={renderItem}
|
||||||
|
keyExtractor={keyExtractor}
|
||||||
refreshing={isPTRing}
|
refreshing={isPTRing}
|
||||||
onRefresh={onRefresh}
|
onRefresh={onRefresh}
|
||||||
onEndReached={onEndReached}
|
onEndReached={onEndReached}
|
||||||
onEndReachedThreshold={3}
|
ListFooterComponent={
|
||||||
renderItem={renderItem}
|
|
||||||
initialNumToRender={initialNumToRender}
|
|
||||||
ListFooterComponent={() => (
|
|
||||||
<ListFooter
|
<ListFooter
|
||||||
isFetching={isFetching && !isRefetching}
|
isFetchingNextPage={isFetchingNextPage}
|
||||||
isError={isError}
|
error={cleanError(error)}
|
||||||
error={error ? error.toString() : undefined}
|
|
||||||
onRetry={fetchNextPage}
|
onRetry={fetchNextPage}
|
||||||
/>
|
/>
|
||||||
)}
|
}
|
||||||
|
onEndReachedThreshold={3}
|
||||||
|
initialNumToRender={initialNumToRender}
|
||||||
|
windowSize={11}
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
<View style={[a.p_lg]}>
|
|
||||||
<View style={[a.p_lg, a.rounded_sm, t.atoms.bg_contrast_25]}>
|
|
||||||
<Text style={[a.text_md, a.leading_snug]}>
|
|
||||||
<Trans>
|
|
||||||
Nobody has liked this yet. Maybe you should be the first!
|
|
||||||
</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,23 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {Trans, msg} from '@lingui/macro'
|
|
||||||
|
|
||||||
import {CenteredView} from 'view/com/util/Views'
|
|
||||||
import {Loader} from '#/components/Loader'
|
|
||||||
import {cleanError} from 'lib/strings/errors'
|
import {cleanError} from 'lib/strings/errors'
|
||||||
|
import {CenteredView} from 'view/com/util/Views'
|
||||||
|
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||||
import {Button} from '#/components/Button'
|
import {Button} from '#/components/Button'
|
||||||
import {Text} from '#/components/Typography'
|
|
||||||
import {Error} from '#/components/Error'
|
import {Error} from '#/components/Error'
|
||||||
|
import {Loader} from '#/components/Loader'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
export function ListFooter({
|
export function ListFooter({
|
||||||
isFetching,
|
isFetchingNextPage,
|
||||||
isError,
|
|
||||||
error,
|
error,
|
||||||
onRetry,
|
onRetry,
|
||||||
height,
|
height,
|
||||||
}: {
|
}: {
|
||||||
isFetching?: boolean
|
isFetchingNextPage?: boolean
|
||||||
isError?: boolean
|
|
||||||
error?: string
|
error?: string
|
||||||
onRetry?: () => Promise<unknown>
|
onRetry?: () => Promise<unknown>
|
||||||
height?: number
|
height?: number
|
||||||
|
@ -36,32 +34,26 @@ export function ListFooter({
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
{height: height ?? 180, paddingTop: 30},
|
{height: height ?? 180, paddingTop: 30},
|
||||||
]}>
|
]}>
|
||||||
{isFetching ? (
|
{isFetchingNextPage ? (
|
||||||
<Loader size="xl" />
|
<Loader size="xl" />
|
||||||
) : (
|
) : (
|
||||||
<ListFooterMaybeError
|
<ListFooterMaybeError error={error} onRetry={onRetry} />
|
||||||
isError={isError}
|
|
||||||
error={error}
|
|
||||||
onRetry={onRetry}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ListFooterMaybeError({
|
function ListFooterMaybeError({
|
||||||
isError,
|
|
||||||
error,
|
error,
|
||||||
onRetry,
|
onRetry,
|
||||||
}: {
|
}: {
|
||||||
isError?: boolean
|
|
||||||
error?: string
|
error?: string
|
||||||
onRetry?: () => Promise<unknown>
|
onRetry?: () => Promise<unknown>
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
|
||||||
if (!isError) return null
|
if (!error) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.w_full, a.px_lg]}>
|
<View style={[a.w_full, a.px_lg]}>
|
||||||
|
@ -128,7 +120,7 @@ export function ListHeaderDesktop({
|
||||||
|
|
||||||
export function ListMaybePlaceholder({
|
export function ListMaybePlaceholder({
|
||||||
isLoading,
|
isLoading,
|
||||||
isEmpty,
|
noEmpty,
|
||||||
isError,
|
isError,
|
||||||
emptyTitle,
|
emptyTitle,
|
||||||
emptyMessage,
|
emptyMessage,
|
||||||
|
@ -138,7 +130,7 @@ export function ListMaybePlaceholder({
|
||||||
onRetry,
|
onRetry,
|
||||||
}: {
|
}: {
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
isEmpty?: boolean
|
noEmpty?: boolean
|
||||||
isError?: boolean
|
isError?: boolean
|
||||||
emptyTitle?: string
|
emptyTitle?: string
|
||||||
emptyMessage?: string
|
emptyMessage?: string
|
||||||
|
@ -151,16 +143,6 @@ export function ListMaybePlaceholder({
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {gtMobile, gtTablet} = useBreakpoints()
|
const {gtMobile, gtTablet} = useBreakpoints()
|
||||||
|
|
||||||
if (!isLoading && isError) {
|
|
||||||
return (
|
|
||||||
<Error
|
|
||||||
title={errorTitle ?? _(msg`Oops!`)}
|
|
||||||
message={errorMessage ?? _(`Something went wrong!`)}
|
|
||||||
onRetry={onRetry}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<CenteredView
|
<CenteredView
|
||||||
|
@ -180,7 +162,17 @@ export function ListMaybePlaceholder({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEmpty) {
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<Error
|
||||||
|
title={errorTitle ?? _(msg`Oops!`)}
|
||||||
|
message={errorMessage ?? _(`Something went wrong!`)}
|
||||||
|
onRetry={onRetry}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noEmpty) {
|
||||||
return (
|
return (
|
||||||
<Error
|
<Error
|
||||||
title={
|
title={
|
||||||
|
@ -197,4 +189,6 @@ export function ListMaybePlaceholder({
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleProp, TextStyle, ViewStyle} from 'react-native'
|
import {StyleProp, TextStyle, ViewStyle} from 'react-native'
|
||||||
import Svg, {Path, Rect, Line, Ellipse} from 'react-native-svg'
|
import Svg, {Ellipse, Line, Path, Rect} from 'react-native-svg'
|
||||||
|
|
||||||
export function GridIcon({
|
export function GridIcon({
|
||||||
style,
|
style,
|
||||||
|
|
|
@ -1,28 +1,30 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {ListRenderItemInfo, Pressable} from 'react-native'
|
import {ListRenderItemInfo, Pressable} from 'react-native'
|
||||||
import {useFocusEffect} from '@react-navigation/native'
|
|
||||||
import {useSetMinimalShellMode} from 'state/shell'
|
|
||||||
import {ViewHeader} from 'view/com/util/ViewHeader'
|
|
||||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
|
||||||
import {CommonNavigatorParams} from 'lib/routes/types'
|
|
||||||
import {useSearchPostsQuery} from 'state/queries/search-posts'
|
|
||||||
import {Post} from 'view/com/post/Post'
|
|
||||||
import {PostView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
|
import {PostView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
|
||||||
|
import {msg} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useFocusEffect} from '@react-navigation/native'
|
||||||
|
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||||
|
|
||||||
|
import {HITSLOP_10} from 'lib/constants'
|
||||||
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
|
import {CommonNavigatorParams} from 'lib/routes/types'
|
||||||
|
import {shareUrl} from 'lib/sharing'
|
||||||
|
import {cleanError} from 'lib/strings/errors'
|
||||||
|
import {sanitizeHandle} from 'lib/strings/handles'
|
||||||
import {enforceLen} from 'lib/strings/helpers'
|
import {enforceLen} from 'lib/strings/helpers'
|
||||||
|
import {isNative} from 'platform/detection'
|
||||||
|
import {useSearchPostsQuery} from 'state/queries/search-posts'
|
||||||
|
import {useSetMinimalShellMode} from 'state/shell'
|
||||||
|
import {Post} from 'view/com/post/Post'
|
||||||
|
import {List} from 'view/com/util/List'
|
||||||
|
import {ViewHeader} from 'view/com/util/ViewHeader'
|
||||||
|
import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOutOfBox'
|
||||||
import {
|
import {
|
||||||
ListFooter,
|
ListFooter,
|
||||||
ListHeaderDesktop,
|
ListHeaderDesktop,
|
||||||
ListMaybePlaceholder,
|
ListMaybePlaceholder,
|
||||||
} from '#/components/Lists'
|
} from '#/components/Lists'
|
||||||
import {List} from 'view/com/util/List'
|
|
||||||
import {msg} from '@lingui/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {sanitizeHandle} from 'lib/strings/handles'
|
|
||||||
import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOutOfBox'
|
|
||||||
import {shareUrl} from 'lib/sharing'
|
|
||||||
import {HITSLOP_10} from 'lib/constants'
|
|
||||||
import {isNative} from 'platform/detection'
|
|
||||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
|
||||||
|
|
||||||
const renderItem = ({item}: ListRenderItemInfo<PostView>) => {
|
const renderItem = ({item}: ListRenderItemInfo<PostView>) => {
|
||||||
return <Post post={item} />
|
return <Post post={item} />
|
||||||
|
@ -61,9 +63,8 @@ export default function HashtagScreen({
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
isFetching,
|
isFetchingNextPage,
|
||||||
isLoading,
|
isLoading,
|
||||||
isRefetching,
|
|
||||||
isError,
|
isError,
|
||||||
error,
|
error,
|
||||||
refetch,
|
refetch,
|
||||||
|
@ -97,9 +98,9 @@ export default function HashtagScreen({
|
||||||
}, [refetch])
|
}, [refetch])
|
||||||
|
|
||||||
const onEndReached = React.useCallback(() => {
|
const onEndReached = React.useCallback(() => {
|
||||||
if (isFetching || !hasNextPage || error) return
|
if (isFetchingNextPage || !hasNextPage || error) return
|
||||||
fetchNextPage()
|
fetchNextPage()
|
||||||
}, [isFetching, hasNextPage, error, fetchNextPage])
|
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -123,16 +124,16 @@ export default function HashtagScreen({
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
{posts.length < 1 ? (
|
||||||
<ListMaybePlaceholder
|
<ListMaybePlaceholder
|
||||||
isLoading={isLoading || isRefetching}
|
isLoading={isLoading}
|
||||||
isError={isError}
|
isError={isError}
|
||||||
isEmpty={posts.length < 1}
|
|
||||||
onRetry={refetch}
|
onRetry={refetch}
|
||||||
emptyTitle="results"
|
emptyType="results"
|
||||||
emptyMessage={_(msg`We couldn't find any results for that hashtag.`)}
|
emptyMessage={_(msg`We couldn't find any results for that hashtag.`)}
|
||||||
/>
|
/>
|
||||||
{!isLoading && posts.length > 0 && (
|
) : (
|
||||||
<List<PostView>
|
<List
|
||||||
data={posts}
|
data={posts}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
keyExtractor={keyExtractor}
|
keyExtractor={keyExtractor}
|
||||||
|
@ -150,9 +151,8 @@ export default function HashtagScreen({
|
||||||
}
|
}
|
||||||
ListFooterComponent={
|
ListFooterComponent={
|
||||||
<ListFooter
|
<ListFooter
|
||||||
isFetching={isFetching && !isRefetching}
|
isFetchingNextPage={isFetchingNextPage}
|
||||||
isError={isError}
|
error={cleanError(error)}
|
||||||
error={error?.name}
|
|
||||||
onRetry={fetchNextPage}
|
onRetry={fetchNextPage}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,11 @@ import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useFocusEffect} from '@react-navigation/native'
|
import {useFocusEffect} from '@react-navigation/native'
|
||||||
|
|
||||||
import {NativeStackScreenProps, CommonNavigatorParams} from '#/lib/routes/types'
|
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
|
||||||
|
import {makeRecordUri} from '#/lib/strings/url-helpers'
|
||||||
|
import {useSetMinimalShellMode} from '#/state/shell'
|
||||||
import {ViewHeader} from '#/view/com/util/ViewHeader'
|
import {ViewHeader} from '#/view/com/util/ViewHeader'
|
||||||
import {LikedByList} from '#/components/LikedByList'
|
import {LikedByList} from '#/components/LikedByList'
|
||||||
import {useSetMinimalShellMode} from '#/state/shell'
|
|
||||||
import {makeRecordUri} from '#/lib/strings/url-helpers'
|
|
||||||
|
|
||||||
import {atoms as a, useBreakpoints} from '#/alf'
|
|
||||||
|
|
||||||
export function ProfileLabelerLikedByScreen({
|
export function ProfileLabelerLikedByScreen({
|
||||||
route,
|
route,
|
||||||
|
@ -19,7 +17,6 @@ export function ProfileLabelerLikedByScreen({
|
||||||
const {name: handleOrDid} = route.params
|
const {name: handleOrDid} = route.params
|
||||||
const uri = makeRecordUri(handleOrDid, 'app.bsky.labeler.service', 'self')
|
const uri = makeRecordUri(handleOrDid, 'app.bsky.labeler.service', 'self')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {gtMobile} = useBreakpoints()
|
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
React.useCallback(() => {
|
React.useCallback(() => {
|
||||||
|
@ -28,17 +25,7 @@ export function ProfileLabelerLikedByScreen({
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View style={{flex: 1}}>
|
||||||
style={[
|
|
||||||
a.mx_auto,
|
|
||||||
a.w_full,
|
|
||||||
a.h_full_vh,
|
|
||||||
gtMobile && [
|
|
||||||
{
|
|
||||||
maxWidth: 600,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]}>
|
|
||||||
<ViewHeader title={_(msg`Liked By`)} />
|
<ViewHeader title={_(msg`Liked By`)} />
|
||||||
<LikedByList uri={uri} />
|
<LikedByList uri={uri} />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -368,16 +368,20 @@ export function PostThread({
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (error || !thread) {
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<ListMaybePlaceholder
|
<ListMaybePlaceholder
|
||||||
isLoading={(!preferences || !thread) && !error}
|
isLoading={(!preferences || !thread) && !error}
|
||||||
isError={!!error}
|
isError={!!error}
|
||||||
|
noEmpty
|
||||||
onRetry={refetch}
|
onRetry={refetch}
|
||||||
errorTitle={error?.title}
|
errorTitle={error?.title}
|
||||||
errorMessage={error?.message}
|
errorMessage={error?.message}
|
||||||
/>
|
/>
|
||||||
{!error && thread && (
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
<List
|
<List
|
||||||
ref={ref}
|
ref={ref}
|
||||||
data={posts}
|
data={posts}
|
||||||
|
@ -397,7 +401,10 @@ export function PostThread({
|
||||||
removeClippedSubviews={isAndroid ? false : undefined}
|
removeClippedSubviews={isAndroid ? false : undefined}
|
||||||
ListFooterComponent={
|
ListFooterComponent={
|
||||||
<ListFooter
|
<ListFooter
|
||||||
isFetching={isFetching}
|
// Using `isFetching` over `isFetchingNextPage` is done on purpose here so we get the loader on
|
||||||
|
// initial render
|
||||||
|
isFetchingNextPage={isFetching}
|
||||||
|
error={cleanError(threadError)}
|
||||||
onRetry={refetch}
|
onRetry={refetch}
|
||||||
// 300 is based on the minimum height of a post. This is enough extra height for the `maintainVisPos` to
|
// 300 is based on the minimum height of a post. This is enough extra height for the `maintainVisPos` to
|
||||||
// work without causing weird jumps on web or glitches on native
|
// work without causing weird jumps on web or glitches on native
|
||||||
|
@ -407,8 +414,6 @@ export function PostThread({
|
||||||
initialNumToRender={initialNumToRender}
|
initialNumToRender={initialNumToRender}
|
||||||
windowSize={11}
|
windowSize={11}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||||
import {List} from '../util/List'
|
import {msg} from '@lingui/macro'
|
||||||
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
|
import {logger} from '#/logger'
|
||||||
import {useProfileFollowersQuery} from '#/state/queries/profile-followers'
|
import {useProfileFollowersQuery} from '#/state/queries/profile-followers'
|
||||||
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
||||||
import {logger} from '#/logger'
|
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
|
||||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
|
import {useSession} from 'state/session'
|
||||||
import {
|
import {
|
||||||
ListFooter,
|
ListFooter,
|
||||||
ListHeaderDesktop,
|
ListHeaderDesktop,
|
||||||
ListMaybePlaceholder,
|
ListMaybePlaceholder,
|
||||||
} from '#/components/Lists'
|
} from '#/components/Lists'
|
||||||
import {msg} from '@lingui/macro'
|
import {List} from '../util/List'
|
||||||
import {useLingui} from '@lingui/react'
|
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||||
import {useSession} from 'state/session'
|
|
||||||
import {View} from 'react-native'
|
|
||||||
|
|
||||||
function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) {
|
function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) {
|
||||||
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
|
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||||
|
@ -39,7 +39,6 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
isLoading: isFollowersLoading,
|
isLoading: isFollowersLoading,
|
||||||
isFetching,
|
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
hasNextPage,
|
hasNextPage,
|
||||||
fetchNextPage,
|
fetchNextPage,
|
||||||
|
@ -47,14 +46,8 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||||
refetch,
|
refetch,
|
||||||
} = useProfileFollowersQuery(resolvedDid)
|
} = useProfileFollowersQuery(resolvedDid)
|
||||||
|
|
||||||
const isError = React.useMemo(
|
const isError = !!resolveError || !!error
|
||||||
() => !!resolveError || !!error,
|
const isMe = resolvedDid === currentAccount?.did
|
||||||
[resolveError, error],
|
|
||||||
)
|
|
||||||
|
|
||||||
const isMe = React.useMemo(() => {
|
|
||||||
return resolvedDid === currentAccount?.did
|
|
||||||
}, [resolvedDid, currentAccount?.did])
|
|
||||||
|
|
||||||
const followers = React.useMemo(() => {
|
const followers = React.useMemo(() => {
|
||||||
if (data?.pages) {
|
if (data?.pages) {
|
||||||
|
@ -73,20 +66,19 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||||
setIsPTRing(false)
|
setIsPTRing(false)
|
||||||
}, [refetch, setIsPTRing])
|
}, [refetch, setIsPTRing])
|
||||||
|
|
||||||
const onEndReached = async () => {
|
const onEndReached = React.useCallback(async () => {
|
||||||
if (isFetching || !hasNextPage || !!error) return
|
if (isFetchingNextPage || !hasNextPage || !!error) return
|
||||||
try {
|
try {
|
||||||
await fetchNextPage()
|
await fetchNextPage()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Failed to load more followers', {message: err})
|
logger.error('Failed to load more followers', {message: err})
|
||||||
}
|
}
|
||||||
}
|
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
|
||||||
|
|
||||||
|
if (followers.length < 1) {
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
|
||||||
<ListMaybePlaceholder
|
<ListMaybePlaceholder
|
||||||
isLoading={isDidLoading || isFollowersLoading}
|
isLoading={isDidLoading || isFollowersLoading}
|
||||||
isEmpty={followers.length < 1}
|
|
||||||
isError={isError}
|
isError={isError}
|
||||||
emptyType="results"
|
emptyType="results"
|
||||||
emptyMessage={
|
emptyMessage={
|
||||||
|
@ -97,7 +89,10 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||||
errorMessage={cleanError(resolveError || error)}
|
errorMessage={cleanError(resolveError || error)}
|
||||||
onRetry={isError ? refetch : undefined}
|
onRetry={isError ? refetch : undefined}
|
||||||
/>
|
/>
|
||||||
{followers.length > 0 && (
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
<List
|
<List
|
||||||
data={followers}
|
data={followers}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
|
@ -107,13 +102,17 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||||
onEndReached={onEndReached}
|
onEndReached={onEndReached}
|
||||||
onEndReachedThreshold={4}
|
onEndReachedThreshold={4}
|
||||||
ListHeaderComponent={<ListHeaderDesktop title={_(msg`Followers`)} />}
|
ListHeaderComponent={<ListHeaderDesktop title={_(msg`Followers`)} />}
|
||||||
ListFooterComponent={<ListFooter isFetching={isFetchingNextPage} />}
|
ListFooterComponent={
|
||||||
|
<ListFooter
|
||||||
|
isFetchingNextPage={isFetchingNextPage}
|
||||||
|
error={cleanError(error)}
|
||||||
|
onRetry={fetchNextPage}
|
||||||
|
/>
|
||||||
|
}
|
||||||
// @ts-ignore our .web version only -prf
|
// @ts-ignore our .web version only -prf
|
||||||
desktopFixedHeight
|
desktopFixedHeight
|
||||||
initialNumToRender={initialNumToRender}
|
initialNumToRender={initialNumToRender}
|
||||||
windowSize={11}
|
windowSize={11}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||||
import {List} from '../util/List'
|
import {msg} from '@lingui/macro'
|
||||||
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
|
import {logger} from '#/logger'
|
||||||
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
||||||
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
||||||
import {logger} from '#/logger'
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {useSession} from 'state/session'
|
||||||
import {
|
import {
|
||||||
ListFooter,
|
ListFooter,
|
||||||
ListHeaderDesktop,
|
ListHeaderDesktop,
|
||||||
ListMaybePlaceholder,
|
ListMaybePlaceholder,
|
||||||
} from '#/components/Lists'
|
} from '#/components/Lists'
|
||||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
import {List} from '../util/List'
|
||||||
import {useSession} from 'state/session'
|
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||||
import {msg} from '@lingui/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) {
|
function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) {
|
||||||
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
|
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||||
|
@ -38,7 +39,6 @@ export function ProfileFollows({name}: {name: string}) {
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
isLoading: isFollowsLoading,
|
isLoading: isFollowsLoading,
|
||||||
isFetching,
|
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
hasNextPage,
|
hasNextPage,
|
||||||
fetchNextPage,
|
fetchNextPage,
|
||||||
|
@ -46,14 +46,8 @@ export function ProfileFollows({name}: {name: string}) {
|
||||||
refetch,
|
refetch,
|
||||||
} = useProfileFollowsQuery(resolvedDid)
|
} = useProfileFollowsQuery(resolvedDid)
|
||||||
|
|
||||||
const isError = React.useMemo(
|
const isError = !!resolveError || !!error
|
||||||
() => !!resolveError || !!error,
|
const isMe = resolvedDid === currentAccount?.did
|
||||||
[resolveError, error],
|
|
||||||
)
|
|
||||||
|
|
||||||
const isMe = React.useMemo(() => {
|
|
||||||
return resolvedDid === currentAccount?.did
|
|
||||||
}, [resolvedDid, currentAccount?.did])
|
|
||||||
|
|
||||||
const follows = React.useMemo(() => {
|
const follows = React.useMemo(() => {
|
||||||
if (data?.pages) {
|
if (data?.pages) {
|
||||||
|
@ -72,20 +66,19 @@ export function ProfileFollows({name}: {name: string}) {
|
||||||
setIsPTRing(false)
|
setIsPTRing(false)
|
||||||
}, [refetch, setIsPTRing])
|
}, [refetch, setIsPTRing])
|
||||||
|
|
||||||
const onEndReached = async () => {
|
const onEndReached = React.useCallback(async () => {
|
||||||
if (isFetching || !hasNextPage || !!error) return
|
if (isFetchingNextPage || !hasNextPage || !!error) return
|
||||||
try {
|
try {
|
||||||
await fetchNextPage()
|
await fetchNextPage()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Failed to load more follows', {error: err})
|
logger.error('Failed to load more follows', {error: err})
|
||||||
}
|
}
|
||||||
}
|
}, [error, fetchNextPage, hasNextPage, isFetchingNextPage])
|
||||||
|
|
||||||
|
if (follows.length < 1) {
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<ListMaybePlaceholder
|
<ListMaybePlaceholder
|
||||||
isLoading={isDidLoading || isFollowsLoading}
|
isLoading={isDidLoading || isFollowsLoading}
|
||||||
isEmpty={follows.length < 1}
|
|
||||||
isError={isError}
|
isError={isError}
|
||||||
emptyType="results"
|
emptyType="results"
|
||||||
emptyMessage={
|
emptyMessage={
|
||||||
|
@ -96,7 +89,10 @@ export function ProfileFollows({name}: {name: string}) {
|
||||||
errorMessage={cleanError(resolveError || error)}
|
errorMessage={cleanError(resolveError || error)}
|
||||||
onRetry={isError ? refetch : undefined}
|
onRetry={isError ? refetch : undefined}
|
||||||
/>
|
/>
|
||||||
{follows.length > 0 && (
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
<List
|
<List
|
||||||
data={follows}
|
data={follows}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
|
@ -106,13 +102,17 @@ export function ProfileFollows({name}: {name: string}) {
|
||||||
onEndReached={onEndReached}
|
onEndReached={onEndReached}
|
||||||
onEndReachedThreshold={4}
|
onEndReachedThreshold={4}
|
||||||
ListHeaderComponent={<ListHeaderDesktop title={_(msg`Following`)} />}
|
ListHeaderComponent={<ListHeaderDesktop title={_(msg`Following`)} />}
|
||||||
ListFooterComponent={<ListFooter isFetching={isFetchingNextPage} />}
|
ListFooterComponent={
|
||||||
|
<ListFooter
|
||||||
|
isFetchingNextPage={isFetchingNextPage}
|
||||||
|
error={cleanError(error)}
|
||||||
|
onRetry={fetchNextPage}
|
||||||
|
/>
|
||||||
|
}
|
||||||
// @ts-ignore our .web version only -prf
|
// @ts-ignore our .web version only -prf
|
||||||
desktopFixedHeight
|
desktopFixedHeight
|
||||||
initialNumToRender={initialNumToRender}
|
initialNumToRender={initialNumToRender}
|
||||||
windowSize={11}
|
windowSize={11}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue