diff options
author | Hailey <me@haileyok.com> | 2024-08-21 19:35:34 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-21 19:35:34 -0700 |
commit | 61f0be705d614a31331945e1c4b9361d71b81403 (patch) | |
tree | 093eae09977443d66ad917f45c6c5e1362559001 /src | |
parent | 6616a6467ec53aa71e5f823c2d8c46dc01442703 (diff) | |
download | voidsky-61f0be705d614a31331945e1c4b9361d71b81403.tar.zst |
Change size (#4957)
Diffstat (limited to 'src')
-rw-r--r-- | src/components/StarterPack/Main/ProfilesList.tsx | 31 | ||||
-rw-r--r-- | src/components/StarterPack/Wizard/WizardEditListDialog.tsx | 3 | ||||
-rw-r--r-- | src/components/StarterPack/Wizard/WizardListCard.tsx | 5 | ||||
-rw-r--r-- | src/lib/constants.ts | 1 | ||||
-rw-r--r-- | src/screens/Onboarding/StepFinished.tsx | 19 | ||||
-rw-r--r-- | src/screens/Onboarding/util.ts | 12 | ||||
-rw-r--r-- | src/screens/StarterPack/StarterPackScreen.tsx | 71 | ||||
-rw-r--r-- | src/screens/StarterPack/Wizard/State.tsx | 10 | ||||
-rw-r--r-- | src/screens/StarterPack/Wizard/index.tsx | 12 | ||||
-rw-r--r-- | src/state/queries/list-members.ts | 41 | ||||
-rw-r--r-- | src/state/queries/starter-packs.ts | 51 |
11 files changed, 168 insertions, 88 deletions
diff --git a/src/components/StarterPack/Main/ProfilesList.tsx b/src/components/StarterPack/Main/ProfilesList.tsx index 3249f1b32..6174bff02 100644 --- a/src/components/StarterPack/Main/ProfilesList.tsx +++ b/src/components/StarterPack/Main/ProfilesList.tsx @@ -9,14 +9,15 @@ import { import {InfiniteData, UseInfiniteQueryResult} from '@tanstack/react-query' import {useBottomBarOffset} from 'lib/hooks/useBottomBarOffset' +import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' import {isBlockedOrBlocking} from 'lib/moderation/blocked-and-muted' import {isNative, isWeb} from 'platform/detection' -import {useListMembersQuery} from 'state/queries/list-members' +import {useAllListMembersQuery} from 'state/queries/list-members' import {useSession} from 'state/session' import {List, ListRef} from 'view/com/util/List' import {SectionRef} from '#/screens/Profile/Sections/types' import {atoms as a, useTheme} from '#/alf' -import {ListMaybePlaceholder} from '#/components/Lists' +import {ListFooter, ListMaybePlaceholder} from '#/components/Lists' import {Default as ProfileCard} from '#/components/ProfileCard' function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic, index: number) { @@ -39,17 +40,20 @@ export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>( ref, ) { const t = useTheme() - const [initialHeaderHeight] = React.useState(headerHeight) - const bottomBarOffset = useBottomBarOffset(20) + const bottomBarOffset = useBottomBarOffset(200) + const initialNumToRender = useInitialNumToRender() const {currentAccount} = useSession() - const {data, refetch, isError} = useListMembersQuery(listUri, 50) + const {data, refetch, isError} = useAllListMembersQuery(listUri) const [isPTRing, setIsPTRing] = React.useState(false) // The server returns these sorted by descending creation date, so we want to invert - const profiles = data?.pages - .flatMap(p => p.items.map(i => i.subject)) - .filter(p => !isBlockedOrBlocking(p) && !p.associated?.labeler) + + const profiles = data + ?.filter( + p => !isBlockedOrBlocking(p.subject) && !p.subject.associated?.labeler, + ) + .map(p => p.subject) .reverse() const isOwn = new AtUri(listUri).host === currentAccount?.did @@ -99,7 +103,11 @@ export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>( if (!data) { return ( - <View style={{marginTop: headerHeight, marginBottom: bottomBarOffset}}> + <View + style={[ + a.h_full_vh, + {marginTop: headerHeight, marginBottom: bottomBarOffset}, + ]}> <ListMaybePlaceholder isLoading={true} isError={isError} @@ -118,10 +126,13 @@ export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>( ref={scrollElRef} headerOffset={headerHeight} ListFooterComponent={ - <View style={[{height: initialHeaderHeight + bottomBarOffset}]} /> + <ListFooter + style={{paddingBottom: bottomBarOffset, borderTopWidth: 0}} + /> } showsVerticalScrollIndicator={false} desktopFixedHeight + initialNumToRender={initialNumToRender} refreshing={isPTRing} onRefresh={async () => { setIsPTRing(true) diff --git a/src/components/StarterPack/Wizard/WizardEditListDialog.tsx b/src/components/StarterPack/Wizard/WizardEditListDialog.tsx index cf755e1bc..870cbbb9f 100644 --- a/src/components/StarterPack/Wizard/WizardEditListDialog.tsx +++ b/src/components/StarterPack/Wizard/WizardEditListDialog.tsx @@ -7,6 +7,7 @@ import {BottomSheetFlatListMethods} from '@discord/bottom-sheet' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' import {isWeb} from 'platform/detection' import {useSession} from 'state/session' import {WizardAction, WizardState} from '#/screens/StarterPack/Wizard/State' @@ -42,6 +43,7 @@ export function WizardEditListDialog({ const {_} = useLingui() const t = useTheme() const {currentAccount} = useSession() + const initialNumToRender = useInitialNumToRender() const listRef = useRef<BottomSheetFlatListMethods>(null) @@ -148,6 +150,7 @@ export function WizardEditListDialog({ webInnerStyle={[a.py_0, {maxWidth: 500, minWidth: 200}]} keyboardDismissMode="on-drag" removeClippedSubviews={true} + initialNumToRender={initialNumToRender} /> </Dialog.Outer> ) diff --git a/src/components/StarterPack/Wizard/WizardListCard.tsx b/src/components/StarterPack/Wizard/WizardListCard.tsx index 55cf0f02b..bd308fc73 100644 --- a/src/components/StarterPack/Wizard/WizardListCard.tsx +++ b/src/components/StarterPack/Wizard/WizardListCard.tsx @@ -12,7 +12,7 @@ import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {DISCOVER_FEED_URI} from 'lib/constants' +import {DISCOVER_FEED_URI, STARTER_PACK_MAX_SIZE} from 'lib/constants' import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' import {useSession} from 'state/session' @@ -130,7 +130,8 @@ export function WizardProfileCard({ const isMe = profile.did === currentAccount?.did const included = isMe || state.profiles.some(p => p.did === profile.did) - const disabled = isMe || (!included && state.profiles.length >= 49) + const disabled = + isMe || (!included && state.profiles.length >= STARTER_PACK_MAX_SIZE - 1) const moderationUi = moderateProfile(profile, moderationOpts).ui('avatar') const displayName = profile.displayName ? sanitizeDisplayName(profile.displayName) diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 20f6f2eff..ccd5f2dee 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -12,6 +12,7 @@ export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG export const EMBED_SERVICE = 'https://embed.bsky.app' export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js` export const BSKY_DOWNLOAD_URL = 'https://bsky.app/download' +export const STARTER_PACK_MAX_SIZE = 150 // HACK // Yes, this is exactly what it looks like. It's a hard-coded constant diff --git a/src/screens/Onboarding/StepFinished.tsx b/src/screens/Onboarding/StepFinished.tsx index 825a0e723..379807d8f 100644 --- a/src/screens/Onboarding/StepFinished.tsx +++ b/src/screens/Onboarding/StepFinished.tsx @@ -23,6 +23,7 @@ import {useProgressGuideControls} from '#/state/shell/progress-guide' import {uploadBlob} from 'lib/api' import {useRequestNotificationsPermission} from 'lib/notifications/notifications' import {useSetHasCheckedForStarterPack} from 'state/preferences/used-starter-packs' +import {getAllListMembers} from 'state/queries/list-members' import { useActiveStarterPack, useSetActiveStarterPack, @@ -73,18 +74,20 @@ export function StepFinished() { starterPack: activeStarterPack.uri, }) starterPack = spRes.data.starterPack - - if (starterPack.list) { - const listRes = await agent.app.bsky.graph.getList({ - list: starterPack.list.uri, - limit: 50, - }) - listItems = listRes.data.items - } } catch (e) { logger.error('Failed to fetch starter pack', {safeMessage: e}) // don't tell the user, just get them through onboarding. } + try { + if (starterPack?.list) { + listItems = await getAllListMembers(agent, starterPack.list.uri) + } + } catch (e) { + logger.error('Failed to fetch starter pack list items', { + safeMessage: e, + }) + // don't tell the user, just get them through onboarding. + } } try { diff --git a/src/screens/Onboarding/util.ts b/src/screens/Onboarding/util.ts index b9ecc4b98..14750f34c 100644 --- a/src/screens/Onboarding/util.ts +++ b/src/screens/Onboarding/util.ts @@ -4,6 +4,7 @@ import { BskyAgent, } from '@atproto/api' import {TID} from '@atproto/common-web' +import chunk from 'lodash.chunk' import {until} from '#/lib/async/until' @@ -29,10 +30,13 @@ export async function bulkWriteFollows(agent: BskyAgent, dids: string[]) { value: r, })) - await agent.com.atproto.repo.applyWrites({ - repo: session.did, - writes: followWrites, - }) + const chunks = chunk(followWrites, 50) + for (const chunk of chunks) { + await agent.com.atproto.repo.applyWrites({ + repo: session.did, + writes: chunk, + }) + } await whenFollowsIndexed(agent, session.did, res => !!res.data.follows.length) const followUris = new Map() diff --git a/src/screens/StarterPack/StarterPackScreen.tsx b/src/screens/StarterPack/StarterPackScreen.tsx index 595e18527..5b267ff27 100644 --- a/src/screens/StarterPack/StarterPackScreen.tsx +++ b/src/screens/StarterPack/StarterPackScreen.tsx @@ -32,6 +32,7 @@ import {getStarterPackOgCard} from 'lib/strings/starter-pack' import {isWeb} from 'platform/detection' import {updateProfileShadow} from 'state/cache/profile-shadow' import {useModerationOpts} from 'state/preferences/moderation-opts' +import {getAllListMembers} from 'state/queries/list-members' import {useResolvedStarterPackShortLink} from 'state/queries/resolve-short-link' import {useResolveDidQuery} from 'state/queries/resolve-uri' import {useShortenLink} from 'state/queries/shorten-link' @@ -327,42 +328,52 @@ function Header({ setIsProcessing(true) + let listItems: AppBskyGraphDefs.ListItemView[] = [] try { - const list = await agent.app.bsky.graph.getList({ - list: starterPack.list.uri, - }) - const dids = list.data.items - .filter( - li => - li.subject.did !== currentAccount?.did && - !isBlockedOrBlocking(li.subject) && - !isMuted(li.subject) && - !li.subject.viewer?.following, - ) - .map(li => li.subject.did) - - const followUris = await bulkWriteFollows(agent, dids) - - batchedUpdates(() => { - for (let did of dids) { - updateProfileShadow(queryClient, did, { - followingUri: followUris.get(did), - }) - } + listItems = await getAllListMembers(agent, starterPack.list.uri) + } catch (e) { + setIsProcessing(false) + Toast.show(_(msg`An error occurred while trying to follow all`), 'xmark') + logger.error('Failed to get list members for starter pack', { + safeMessage: e, }) + return + } - logEvent('starterPack:followAll', { - logContext: 'StarterPackProfilesList', - starterPack: starterPack.uri, - count: dids.length, - }) - captureAction(ProgressGuideAction.Follow, dids.length) - Toast.show(_(msg`All accounts have been followed!`)) + const dids = listItems + .filter( + li => + li.subject.did !== currentAccount?.did && + !isBlockedOrBlocking(li.subject) && + !isMuted(li.subject) && + !li.subject.viewer?.following, + ) + .map(li => li.subject.did) + + let followUris: Map<string, string> + try { + followUris = await bulkWriteFollows(agent, dids) } catch (e) { - Toast.show(_(msg`An error occurred while trying to follow all`), 'xmark') - } finally { setIsProcessing(false) + Toast.show(_(msg`An error occurred while trying to follow all`), 'xmark') + logger.error('Failed to follow all accounts', {safeMessage: e}) } + + setIsProcessing(false) + batchedUpdates(() => { + for (let did of dids) { + updateProfileShadow(queryClient, did, { + followingUri: followUris.get(did), + }) + } + }) + Toast.show(_(msg`All accounts have been followed!`)) + captureAction(ProgressGuideAction.Follow, dids.length) + logEvent('starterPack:followAll', { + logContext: 'StarterPackProfilesList', + starterPack: starterPack.uri, + count: dids.length, + }) } if (!AppBskyGraphStarterpack.isRecord(record)) { diff --git a/src/screens/StarterPack/Wizard/State.tsx b/src/screens/StarterPack/Wizard/State.tsx index ba5bb147c..debb7e23c 100644 --- a/src/screens/StarterPack/Wizard/State.tsx +++ b/src/screens/StarterPack/Wizard/State.tsx @@ -7,6 +7,7 @@ import { import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs' import {msg} from '@lingui/macro' +import {STARTER_PACK_MAX_SIZE} from 'lib/constants' import {useSession} from 'state/session' import * as Toast from '#/view/com/util/Toast' @@ -73,9 +74,10 @@ function reducer(state: State, action: Action): State { updatedState = {...state, description: action.description} break case 'AddProfile': - if (state.profiles.length >= 51) { + if (state.profiles.length > STARTER_PACK_MAX_SIZE) { Toast.show( - msg`You may only add up to 50 profiles`.message ?? '', + msg`You may only add up to ${STARTER_PACK_MAX_SIZE} profiles` + .message ?? '', 'info', ) } else { @@ -91,8 +93,8 @@ function reducer(state: State, action: Action): State { } break case 'AddFeed': - if (state.feeds.length >= 50) { - Toast.show(msg`You may only add up to 50 feeds`.message ?? '', 'info') + if (state.feeds.length >= 3) { + Toast.show(msg`You may only add up to 3 feeds`.message ?? '', 'info') } else { updatedState = {...state, feeds: [...state.feeds, action.feed]} } diff --git a/src/screens/StarterPack/Wizard/index.tsx b/src/screens/StarterPack/Wizard/index.tsx index 8d9bb165b..40a4a510b 100644 --- a/src/screens/StarterPack/Wizard/index.tsx +++ b/src/screens/StarterPack/Wizard/index.tsx @@ -20,7 +20,7 @@ import {useFocusEffect, useNavigation} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' import {logger} from '#/logger' -import {HITSLOP_10} from 'lib/constants' +import {HITSLOP_10, STARTER_PACK_MAX_SIZE} from 'lib/constants' import {createSanitizedDisplayName} from 'lib/moderation/create-sanitized-display-name' import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types' import {logEvent} from 'lib/statsig/statsig' @@ -33,7 +33,7 @@ import { } from 'lib/strings/starter-pack' import {isAndroid, isNative, isWeb} from 'platform/detection' import {useModerationOpts} from 'state/preferences/moderation-opts' -import {useListMembersQuery} from 'state/queries/list-members' +import {useAllListMembersQuery} from 'state/queries/list-members' import {useProfileQuery} from 'state/queries/profile' import { useCreateStarterPackMutation, @@ -78,11 +78,10 @@ export function Wizard({ const listUri = starterPack?.list?.uri const { - data: profilesData, + data: listItems, isLoading: isLoadingProfiles, isError: isErrorProfiles, - } = useListMembersQuery(listUri, 50) - const listItems = profilesData?.pages.flatMap(p => p.items) + } = useAllListMembersQuery(listUri) const { data: profile, @@ -428,7 +427,8 @@ function Footer({ {items.length > minimumItems && ( <View style={[a.absolute, {right: 14, top: 31}]}> <Text style={[a.font_bold]}> - {items.length}/{state.currentStep === 'Profiles' ? 50 : 3} + {items.length}/ + {state.currentStep === 'Profiles' ? STARTER_PACK_MAX_SIZE : 3} </Text> </View> )} diff --git a/src/state/queries/list-members.ts b/src/state/queries/list-members.ts index 3131a2ec3..b02cc9910 100644 --- a/src/state/queries/list-members.ts +++ b/src/state/queries/list-members.ts @@ -1,9 +1,15 @@ -import {AppBskyActorDefs, AppBskyGraphGetList} from '@atproto/api' +import { + AppBskyActorDefs, + AppBskyGraphDefs, + AppBskyGraphGetList, + BskyAgent, +} from '@atproto/api' import { InfiniteData, QueryClient, QueryKey, useInfiniteQuery, + useQuery, } from '@tanstack/react-query' import {STALE} from '#/state/queries' @@ -14,6 +20,7 @@ type RQPageParam = string | undefined const RQKEY_ROOT = 'list-members' export const RQKEY = (uri: string) => [RQKEY_ROOT, uri] +export const RQKEY_ALL = (uri: string) => [RQKEY_ROOT, uri, 'all'] export function useListMembersQuery(uri?: string, limit: number = PAGE_SIZE) { const agent = useAgent() @@ -40,6 +47,38 @@ export function useListMembersQuery(uri?: string, limit: number = PAGE_SIZE) { }) } +export function useAllListMembersQuery(uri?: string) { + const agent = useAgent() + return useQuery({ + staleTime: STALE.MINUTES.ONE, + queryKey: RQKEY_ALL(uri ?? ''), + queryFn: async () => { + return getAllListMembers(agent, uri!) + }, + enabled: Boolean(uri), + }) +} + +export async function getAllListMembers(agent: BskyAgent, uri: string) { + let hasMore = true + let cursor: string | undefined + const listItems: AppBskyGraphDefs.ListItemView[] = [] + // We want to cap this at 6 pages, just for anything weird happening with the api + let i = 0 + while (hasMore && i < 6) { + const res = await agent.app.bsky.graph.getList({ + list: uri, + limit: 50, + cursor, + }) + listItems.push(...res.data.items) + hasMore = Boolean(res.data.cursor) + cursor = res.data.cursor + } + i++ + return listItems +} + export async function invalidateListMembersQuery({ queryClient, uri, diff --git a/src/state/queries/starter-packs.ts b/src/state/queries/starter-packs.ts index 2cdb6b850..a3795d792 100644 --- a/src/state/queries/starter-packs.ts +++ b/src/state/queries/starter-packs.ts @@ -16,6 +16,7 @@ import { useQuery, useQueryClient, } from '@tanstack/react-query' +import chunk from 'lodash.chunk' import {until} from 'lib/async/until' import {createStarterPackList} from 'lib/generate-starterpack' @@ -200,36 +201,40 @@ export function useEditStarterPackMutation({ i.subject.did !== agent.session?.did && !profiles.find(p => p.did === i.subject.did && p.did), ) - if (removedItems.length !== 0) { - await agent.com.atproto.repo.applyWrites({ - repo: agent.session!.did, - writes: removedItems.map(i => ({ - $type: 'com.atproto.repo.applyWrites#delete', - collection: 'app.bsky.graph.listitem', - rkey: new AtUri(i.uri).rkey, - })), - }) + const chunks = chunk(removedItems, 50) + for (const chunk of chunks) { + await agent.com.atproto.repo.applyWrites({ + repo: agent.session!.did, + writes: chunk.map(i => ({ + $type: 'com.atproto.repo.applyWrites#delete', + collection: 'app.bsky.graph.listitem', + rkey: new AtUri(i.uri).rkey, + })), + }) + } } const addedProfiles = profiles.filter( p => !currentListItems.find(i => i.subject.did === p.did), ) - if (addedProfiles.length > 0) { - await agent.com.atproto.repo.applyWrites({ - repo: agent.session!.did, - writes: addedProfiles.map(p => ({ - $type: 'com.atproto.repo.applyWrites#create', - collection: 'app.bsky.graph.listitem', - value: { - $type: 'app.bsky.graph.listitem', - subject: p.did, - list: currentStarterPack.list?.uri, - createdAt: new Date().toISOString(), - }, - })), - }) + const chunks = chunk(addedProfiles, 50) + for (const chunk of chunks) { + await agent.com.atproto.repo.applyWrites({ + repo: agent.session!.did, + writes: chunk.map(p => ({ + $type: 'com.atproto.repo.applyWrites#create', + collection: 'app.bsky.graph.listitem', + value: { + $type: 'app.bsky.graph.listitem', + subject: p.did, + list: currentStarterPack.list?.uri, + createdAt: new Date().toISOString(), + }, + })), + }) + } } const rkey = parseStarterPackUri(currentStarterPack.uri)!.rkey |