diff options
-rw-r--r-- | .github/workflows/bundle-deploy-eas-update.yml | 19 | ||||
-rw-r--r-- | .github/workflows/pull-request-commit.yml | 4 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/components/Error.tsx | 4 | ||||
-rw-r--r-- | src/components/LikedByList.tsx | 96 | ||||
-rw-r--r-- | src/components/Lists.tsx | 56 | ||||
-rw-r--r-- | src/lib/icons.tsx | 2 | ||||
-rw-r--r-- | src/screens/Hashtag.tsx | 60 | ||||
-rw-r--r-- | src/screens/Profile/ProfileLabelerLikedBy.tsx | 21 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThread.tsx | 71 | ||||
-rw-r--r-- | src/view/com/profile/ProfileFollowers.tsx | 79 | ||||
-rw-r--r-- | src/view/com/profile/ProfileFollows.tsx | 80 |
12 files changed, 238 insertions, 255 deletions
diff --git a/.github/workflows/bundle-deploy-eas-update.yml b/.github/workflows/bundle-deploy-eas-update.yml index a4cae0044..e12690789 100644 --- a/.github/workflows/bundle-deploy-eas-update.yml +++ b/.github/workflows/bundle-deploy-eas-update.yml @@ -82,15 +82,16 @@ jobs: uses: expo/expo-github-action/fingerprint@main with: previous-git-commit: ${{ steps.base-commit.outputs.base-commit }} + args: - name: š Debug fingerprint id: fingerprint-debug 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 "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" else echo fingerprint-is-different="false" >> "$GITHUB_OUTPUT" @@ -98,37 +99,37 @@ jobs: - name: šØ Setup EAS uses: expo/expo-github-action@v8 - if: ${{ steps.fingerprint.outputs.fingerprint-diff == '[]' }} + if: ${{ steps.fingerprint-debug.outputs.fingerprint-is-different == 'false'}} with: expo-version: latest eas-version: latest token: ${{ secrets.EXPO_TOKEN }} - 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 - name: šŖ Setup jq - if: ${{ steps.fingerprint.outputs.fingerprint-diff == '[]' }} + if: ${{ steps.fingerprint-debug.outputs.fingerprint-is-different == 'false'}} uses: dcarbone/install-jq-action@v2 - name: š¤ Compile Translations - if: ${{ steps.fingerprint.outputs.fingerprint-diff == '[]' }} + if: ${{ steps.fingerprint-debug.outputs.fingerprint-is-different == 'false'}} run: yarn intl:build - name: āļø Write environment variables - if: ${{ steps.fingerprint.outputs.fingerprint-diff == '[]' }} + if: ${{ steps.fingerprint-debug.outputs.fingerprint-is-different == 'false'}} run: | export json='${{ secrets.GOOGLE_SERVICES_TOKEN }}' echo "${{ secrets.ENV_TOKEN }}" > .env echo "$json" > google-services.json - 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 - 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 env: DENIS_API_KEY: ${{ secrets.DENIS_API_KEY }} diff --git a/.github/workflows/pull-request-commit.yml b/.github/workflows/pull-request-commit.yml index d3aade3f5..24c0a7919 100644 --- a/.github/workflows/pull-request-commit.yml +++ b/.github/workflows/pull-request-commit.yml @@ -80,7 +80,7 @@ jobs: with: base_path: 'stats-base.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 uses: peter-evans/find-comment@v2 @@ -99,7 +99,7 @@ jobs: const body = `<!-- webpack-analyzer comment --> | 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({ diff --git a/package.json b/package.json index 475969268..b9ffbe2dd 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "perf:test:results": "NODE_ENV=test flashlight report .perf/results.json", "perf:measure": "NODE_ENV=test flashlight measure", "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:compile": "lingui compile", "nuke": "rm -rf ./node_modules && rm -rf ./ios && rm -rf ./android", diff --git a/src/components/Error.tsx b/src/components/Error.tsx index 7df166c3f..91b33f48e 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -1,9 +1,9 @@ import React from 'react' import {View} from 'react-native' -import {useNavigation} from '@react-navigation/core' -import {StackActions} from '@react-navigation/native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useNavigation} from '@react-navigation/core' +import {StackActions} from '@react-navigation/native' import {NavigationProp} from 'lib/routes/types' import {CenteredView} from 'view/com/util/Views' diff --git a/src/components/LikedByList.tsx b/src/components/LikedByList.tsx index bd1213639..239a7044f 100644 --- a/src/components/LikedByList.tsx +++ b/src/components/LikedByList.tsx @@ -1,47 +1,54 @@ import React from 'react' -import {View} from 'react-native' 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 {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 {useResolveUriQuery} from '#/state/queries/resolve-uri' 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' -import {Loader} from '#/components/Loader' -import {Text} from '#/components/Typography' +function renderItem({item}: {item: GetLikes.Like}) { + return <ProfileCardWithFollowBtn key={item.actor.did} profile={item.actor} /> +} + +function keyExtractor(item: GetLikes.Like) { + return item.actor.did +} export function LikedByList({uri}: {uri: string}) { - const t = useTheme() + const {_} = useLingui() + const initialNumToRender = useInitialNumToRender() const [isPTRing, setIsPTRing] = React.useState(false) + const { data: resolvedUri, error: resolveError, - isFetching: isFetchingResolvedUri, + isLoading: isUriLoading, } = useResolveUriQuery(uri) const { data, - isFetching, - isFetched, - isRefetching, + isLoading: isLikedByLoading, + isFetchingNextPage, hasNextPage, fetchNextPage, - isError, error: likedByError, refetch, } = useLikedByQuery(resolvedUri?.uri) + + const error = resolveError || likedByError + const isError = !!resolveError || !!likedByError + const likes = React.useMemo(() => { if (data?.pages) { return data.pages.flatMap(page => page.likes) } return [] }, [data]) - const initialNumToRender = useInitialNumToRender() - const error = resolveError || likedByError const onRefresh = React.useCallback(async () => { setIsPTRing(true) @@ -54,56 +61,47 @@ export function LikedByList({uri}: {uri: string}) { }, [refetch, setIsPTRing]) const onEndReached = React.useCallback(async () => { - if (isFetching || !hasNextPage || isError) return + if (isFetchingNextPage || !hasNextPage || isError) return try { await fetchNextPage() } catch (err) { logger.error('Failed to load more likes', {message: err}) } - }, [isFetching, hasNextPage, isError, fetchNextPage]) - - const renderItem = React.useCallback(({item}: {item: GetLikes.Like}) => { - return ( - <ProfileCardWithFollowBtn key={item.actor.did} profile={item.actor} /> - ) - }, []) + }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) - if (isFetchingResolvedUri || !isFetched) { + if (likes.length < 1) { return ( - <View style={[a.w_full, a.align_center, a.p_lg]}> - <Loader size="xl" /> - </View> + <ListMaybePlaceholder + isLoading={isUriLoading || isLikedByLoading} + isError={isError} + emptyType="results" + emptyMessage={_( + msg`Nobody has liked this yet. Maybe you should be the first!`, + )} + errorMessage={cleanError(resolveError || error)} + onRetry={isError ? refetch : undefined} + /> ) } - return likes.length ? ( + return ( <List data={likes} - keyExtractor={item => item.actor.did} + renderItem={renderItem} + keyExtractor={keyExtractor} refreshing={isPTRing} onRefresh={onRefresh} onEndReached={onEndReached} - onEndReachedThreshold={3} - renderItem={renderItem} - initialNumToRender={initialNumToRender} - ListFooterComponent={() => ( + ListFooterComponent={ <ListFooter - isFetching={isFetching && !isRefetching} - isError={isError} - error={error ? error.toString() : undefined} + isFetchingNextPage={isFetchingNextPage} + error={cleanError(error)} 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> ) } diff --git a/src/components/Lists.tsx b/src/components/Lists.tsx index d3e072028..605626fef 100644 --- a/src/components/Lists.tsx +++ b/src/components/Lists.tsx @@ -1,25 +1,23 @@ import React from 'react' -import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {View} from 'react-native' +import {msg, Trans} from '@lingui/macro' 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 {CenteredView} from 'view/com/util/Views' +import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {Button} from '#/components/Button' -import {Text} from '#/components/Typography' import {Error} from '#/components/Error' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' export function ListFooter({ - isFetching, - isError, + isFetchingNextPage, error, onRetry, height, }: { - isFetching?: boolean - isError?: boolean + isFetchingNextPage?: boolean error?: string onRetry?: () => Promise<unknown> height?: number @@ -36,32 +34,26 @@ export function ListFooter({ t.atoms.border_contrast_low, {height: height ?? 180, paddingTop: 30}, ]}> - {isFetching ? ( + {isFetchingNextPage ? ( <Loader size="xl" /> ) : ( - <ListFooterMaybeError - isError={isError} - error={error} - onRetry={onRetry} - /> + <ListFooterMaybeError error={error} onRetry={onRetry} /> )} </View> ) } function ListFooterMaybeError({ - isError, error, onRetry, }: { - isError?: boolean error?: string onRetry?: () => Promise<unknown> }) { const t = useTheme() const {_} = useLingui() - if (!isError) return null + if (!error) return null return ( <View style={[a.w_full, a.px_lg]}> @@ -128,7 +120,7 @@ export function ListHeaderDesktop({ export function ListMaybePlaceholder({ isLoading, - isEmpty, + noEmpty, isError, emptyTitle, emptyMessage, @@ -138,7 +130,7 @@ export function ListMaybePlaceholder({ onRetry, }: { isLoading: boolean - isEmpty?: boolean + noEmpty?: boolean isError?: boolean emptyTitle?: string emptyMessage?: string @@ -151,16 +143,6 @@ export function ListMaybePlaceholder({ const {_} = useLingui() const {gtMobile, gtTablet} = useBreakpoints() - if (!isLoading && isError) { - return ( - <Error - title={errorTitle ?? _(msg`Oops!`)} - message={errorMessage ?? _(`Something went wrong!`)} - onRetry={onRetry} - /> - ) - } - if (isLoading) { return ( <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 ( <Error title={ @@ -197,4 +189,6 @@ export function ListMaybePlaceholder({ /> ) } + + return null } diff --git a/src/lib/icons.tsx b/src/lib/icons.tsx index 8783682d0..93b45ea3a 100644 --- a/src/lib/icons.tsx +++ b/src/lib/icons.tsx @@ -1,6 +1,6 @@ import React from 'react' 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({ style, diff --git a/src/screens/Hashtag.tsx b/src/screens/Hashtag.tsx index 46452f087..5388593f1 100644 --- a/src/screens/Hashtag.tsx +++ b/src/screens/Hashtag.tsx @@ -1,28 +1,30 @@ import React from 'react' import {ListRenderItemInfo, Pressable} from 'react-native' +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 {useSetMinimalShellMode} from 'state/shell' -import {ViewHeader} from 'view/com/util/ViewHeader' 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 {isNative} from 'platform/detection' import {useSearchPostsQuery} from 'state/queries/search-posts' +import {useSetMinimalShellMode} from 'state/shell' import {Post} from 'view/com/post/Post' -import {PostView} from '@atproto/api/dist/client/types/app/bsky/feed/defs' -import {enforceLen} from 'lib/strings/helpers' +import {List} from 'view/com/util/List' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOutOfBox' import { ListFooter, ListHeaderDesktop, ListMaybePlaceholder, } 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>) => { return <Post post={item} /> @@ -61,9 +63,8 @@ export default function HashtagScreen({ const { data, - isFetching, + isFetchingNextPage, isLoading, - isRefetching, isError, error, refetch, @@ -97,9 +98,9 @@ export default function HashtagScreen({ }, [refetch]) const onEndReached = React.useCallback(() => { - if (isFetching || !hasNextPage || error) return + if (isFetchingNextPage || !hasNextPage || error) return fetchNextPage() - }, [isFetching, hasNextPage, error, fetchNextPage]) + }, [isFetchingNextPage, hasNextPage, error, fetchNextPage]) return ( <> @@ -123,16 +124,16 @@ export default function HashtagScreen({ : undefined } /> - <ListMaybePlaceholder - isLoading={isLoading || isRefetching} - isError={isError} - isEmpty={posts.length < 1} - onRetry={refetch} - emptyTitle="results" - emptyMessage={_(msg`We couldn't find any results for that hashtag.`)} - /> - {!isLoading && posts.length > 0 && ( - <List<PostView> + {posts.length < 1 ? ( + <ListMaybePlaceholder + isLoading={isLoading} + isError={isError} + onRetry={refetch} + emptyType="results" + emptyMessage={_(msg`We couldn't find any results for that hashtag.`)} + /> + ) : ( + <List data={posts} renderItem={renderItem} keyExtractor={keyExtractor} @@ -150,9 +151,8 @@ export default function HashtagScreen({ } ListFooterComponent={ <ListFooter - isFetching={isFetching && !isRefetching} - isError={isError} - error={error?.name} + isFetchingNextPage={isFetchingNextPage} + error={cleanError(error)} onRetry={fetchNextPage} /> } diff --git a/src/screens/Profile/ProfileLabelerLikedBy.tsx b/src/screens/Profile/ProfileLabelerLikedBy.tsx index 1d2167520..8650ac2e6 100644 --- a/src/screens/Profile/ProfileLabelerLikedBy.tsx +++ b/src/screens/Profile/ProfileLabelerLikedBy.tsx @@ -4,13 +4,11 @@ import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' 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 {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({ route, @@ -19,7 +17,6 @@ export function ProfileLabelerLikedByScreen({ const {name: handleOrDid} = route.params const uri = makeRecordUri(handleOrDid, 'app.bsky.labeler.service', 'self') const {_} = useLingui() - const {gtMobile} = useBreakpoints() useFocusEffect( React.useCallback(() => { @@ -28,17 +25,7 @@ export function ProfileLabelerLikedByScreen({ ) return ( - <View - style={[ - a.mx_auto, - a.w_full, - a.h_full_vh, - gtMobile && [ - { - maxWidth: 600, - }, - ], - ]}> + <View style={{flex: 1}}> <ViewHeader title={_(msg`Liked By`)} /> <LikedByList uri={uri} /> </View> diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index c1159379d..f4bf3b1ac 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -368,47 +368,52 @@ export function PostThread({ ], ) - return ( - <> + if (error || !thread) { + return ( <ListMaybePlaceholder isLoading={(!preferences || !thread) && !error} isError={!!error} + noEmpty onRetry={refetch} errorTitle={error?.title} errorMessage={error?.message} /> - {!error && thread && ( - <List - ref={ref} - data={posts} - renderItem={renderItem} - keyExtractor={keyExtractor} - onContentSizeChange={isNative ? undefined : onContentSizeChangeWeb} - onStartReached={onStartReached} - onEndReached={onEndReached} - onEndReachedThreshold={2} - onMomentumScrollEnd={onMomentumScrollEnd} - onScrollToTop={onScrollToTop} - maintainVisibleContentPosition={ - isNative ? MAINTAIN_VISIBLE_CONTENT_POSITION : undefined - } - // @ts-ignore our .web version only -prf - desktopFixedHeight - removeClippedSubviews={isAndroid ? false : undefined} - ListFooterComponent={ - <ListFooter - isFetching={isFetching} - onRetry={refetch} - // 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 - height={windowHeight - 200} - /> - } - initialNumToRender={initialNumToRender} - windowSize={11} + ) + } + + return ( + <List + ref={ref} + data={posts} + renderItem={renderItem} + keyExtractor={keyExtractor} + onContentSizeChange={isNative ? undefined : onContentSizeChangeWeb} + onStartReached={onStartReached} + onEndReached={onEndReached} + onEndReachedThreshold={2} + onMomentumScrollEnd={onMomentumScrollEnd} + onScrollToTop={onScrollToTop} + maintainVisibleContentPosition={ + isNative ? MAINTAIN_VISIBLE_CONTENT_POSITION : undefined + } + // @ts-ignore our .web version only -prf + desktopFixedHeight + removeClippedSubviews={isAndroid ? false : undefined} + ListFooterComponent={ + <ListFooter + // Using `isFetching` over `isFetchingNextPage` is done on purpose here so we get the loader on + // initial render + isFetchingNextPage={isFetching} + error={cleanError(threadError)} + onRetry={refetch} + // 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 + height={windowHeight - 200} /> - )} - </> + } + initialNumToRender={initialNumToRender} + windowSize={11} + /> ) } diff --git a/src/view/com/profile/ProfileFollowers.tsx b/src/view/com/profile/ProfileFollowers.tsx index b11a33f27..94ca33e6e 100644 --- a/src/view/com/profile/ProfileFollowers.tsx +++ b/src/view/com/profile/ProfileFollowers.tsx @@ -1,21 +1,21 @@ import React from 'react' import {AppBskyActorDefs as ActorDefs} from '@atproto/api' -import {List} from '../util/List' -import {ProfileCardWithFollowBtn} from './ProfileCard' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {cleanError} from '#/lib/strings/errors' +import {logger} from '#/logger' import {useProfileFollowersQuery} from '#/state/queries/profile-followers' import {useResolveDidQuery} from '#/state/queries/resolve-uri' -import {logger} from '#/logger' -import {cleanError} from '#/lib/strings/errors' import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' +import {useSession} from 'state/session' import { ListFooter, ListHeaderDesktop, ListMaybePlaceholder, } from '#/components/Lists' -import {msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {useSession} from 'state/session' -import {View} from 'react-native' +import {List} from '../util/List' +import {ProfileCardWithFollowBtn} from './ProfileCard' function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) { return <ProfileCardWithFollowBtn key={item.did} profile={item} /> @@ -39,7 +39,6 @@ export function ProfileFollowers({name}: {name: string}) { const { data, isLoading: isFollowersLoading, - isFetching, isFetchingNextPage, hasNextPage, fetchNextPage, @@ -47,14 +46,8 @@ export function ProfileFollowers({name}: {name: string}) { refetch, } = useProfileFollowersQuery(resolvedDid) - const isError = React.useMemo( - () => !!resolveError || !!error, - [resolveError, error], - ) - - const isMe = React.useMemo(() => { - return resolvedDid === currentAccount?.did - }, [resolvedDid, currentAccount?.did]) + const isError = !!resolveError || !!error + const isMe = resolvedDid === currentAccount?.did const followers = React.useMemo(() => { if (data?.pages) { @@ -73,20 +66,19 @@ export function ProfileFollowers({name}: {name: string}) { setIsPTRing(false) }, [refetch, setIsPTRing]) - const onEndReached = async () => { - if (isFetching || !hasNextPage || !!error) return + const onEndReached = React.useCallback(async () => { + if (isFetchingNextPage || !hasNextPage || !!error) return try { await fetchNextPage() } catch (err) { logger.error('Failed to load more followers', {message: err}) } - } + }, [isFetchingNextPage, hasNextPage, error, fetchNextPage]) - return ( - <View style={{flex: 1}}> + if (followers.length < 1) { + return ( <ListMaybePlaceholder isLoading={isDidLoading || isFollowersLoading} - isEmpty={followers.length < 1} isError={isError} emptyType="results" emptyMessage={ @@ -97,23 +89,30 @@ export function ProfileFollowers({name}: {name: string}) { errorMessage={cleanError(resolveError || error)} onRetry={isError ? refetch : undefined} /> - {followers.length > 0 && ( - <List - data={followers} - renderItem={renderItem} - keyExtractor={keyExtractor} - refreshing={isPTRing} - onRefresh={onRefresh} - onEndReached={onEndReached} - onEndReachedThreshold={4} - ListHeaderComponent={<ListHeaderDesktop title={_(msg`Followers`)} />} - ListFooterComponent={<ListFooter isFetching={isFetchingNextPage} />} - // @ts-ignore our .web version only -prf - desktopFixedHeight - initialNumToRender={initialNumToRender} - windowSize={11} + ) + } + + return ( + <List + data={followers} + renderItem={renderItem} + keyExtractor={keyExtractor} + refreshing={isPTRing} + onRefresh={onRefresh} + onEndReached={onEndReached} + onEndReachedThreshold={4} + ListHeaderComponent={<ListHeaderDesktop title={_(msg`Followers`)} />} + ListFooterComponent={ + <ListFooter + isFetchingNextPage={isFetchingNextPage} + error={cleanError(error)} + onRetry={fetchNextPage} /> - )} - </View> + } + // @ts-ignore our .web version only -prf + desktopFixedHeight + initialNumToRender={initialNumToRender} + windowSize={11} + /> ) } diff --git a/src/view/com/profile/ProfileFollows.tsx b/src/view/com/profile/ProfileFollows.tsx index d99e2b840..9b447c955 100644 --- a/src/view/com/profile/ProfileFollows.tsx +++ b/src/view/com/profile/ProfileFollows.tsx @@ -1,20 +1,21 @@ import React from 'react' import {AppBskyActorDefs as ActorDefs} from '@atproto/api' -import {List} from '../util/List' -import {ProfileCardWithFollowBtn} from './ProfileCard' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {cleanError} from '#/lib/strings/errors' +import {logger} from '#/logger' import {useProfileFollowsQuery} from '#/state/queries/profile-follows' import {useResolveDidQuery} from '#/state/queries/resolve-uri' -import {logger} from '#/logger' -import {cleanError} from '#/lib/strings/errors' +import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' +import {useSession} from 'state/session' import { ListFooter, ListHeaderDesktop, ListMaybePlaceholder, } from '#/components/Lists' -import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' -import {useSession} from 'state/session' -import {msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' +import {List} from '../util/List' +import {ProfileCardWithFollowBtn} from './ProfileCard' function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) { return <ProfileCardWithFollowBtn key={item.did} profile={item} /> @@ -38,7 +39,6 @@ export function ProfileFollows({name}: {name: string}) { const { data, isLoading: isFollowsLoading, - isFetching, isFetchingNextPage, hasNextPage, fetchNextPage, @@ -46,14 +46,8 @@ export function ProfileFollows({name}: {name: string}) { refetch, } = useProfileFollowsQuery(resolvedDid) - const isError = React.useMemo( - () => !!resolveError || !!error, - [resolveError, error], - ) - - const isMe = React.useMemo(() => { - return resolvedDid === currentAccount?.did - }, [resolvedDid, currentAccount?.did]) + const isError = !!resolveError || !!error + const isMe = resolvedDid === currentAccount?.did const follows = React.useMemo(() => { if (data?.pages) { @@ -72,20 +66,19 @@ export function ProfileFollows({name}: {name: string}) { setIsPTRing(false) }, [refetch, setIsPTRing]) - const onEndReached = async () => { - if (isFetching || !hasNextPage || !!error) return + const onEndReached = React.useCallback(async () => { + if (isFetchingNextPage || !hasNextPage || !!error) return try { await fetchNextPage() } catch (err) { logger.error('Failed to load more follows', {error: err}) } - } + }, [error, fetchNextPage, hasNextPage, isFetchingNextPage]) - return ( - <> + if (follows.length < 1) { + return ( <ListMaybePlaceholder isLoading={isDidLoading || isFollowsLoading} - isEmpty={follows.length < 1} isError={isError} emptyType="results" emptyMessage={ @@ -96,23 +89,30 @@ export function ProfileFollows({name}: {name: string}) { errorMessage={cleanError(resolveError || error)} onRetry={isError ? refetch : undefined} /> - {follows.length > 0 && ( - <List - data={follows} - renderItem={renderItem} - keyExtractor={keyExtractor} - refreshing={isPTRing} - onRefresh={onRefresh} - onEndReached={onEndReached} - onEndReachedThreshold={4} - ListHeaderComponent={<ListHeaderDesktop title={_(msg`Following`)} />} - ListFooterComponent={<ListFooter isFetching={isFetchingNextPage} />} - // @ts-ignore our .web version only -prf - desktopFixedHeight - initialNumToRender={initialNumToRender} - windowSize={11} + ) + } + + return ( + <List + data={follows} + renderItem={renderItem} + keyExtractor={keyExtractor} + refreshing={isPTRing} + onRefresh={onRefresh} + onEndReached={onEndReached} + onEndReachedThreshold={4} + ListHeaderComponent={<ListHeaderDesktop title={_(msg`Following`)} />} + ListFooterComponent={ + <ListFooter + isFetchingNextPage={isFetchingNextPage} + error={cleanError(error)} + onRetry={fetchNextPage} /> - )} - </> + } + // @ts-ignore our .web version only -prf + desktopFixedHeight + initialNumToRender={initialNumToRender} + windowSize={11} + /> ) } |