import {View} from 'react-native' import {type AppBskyGraphDefs, type AppBskyGraphStarterpack} from '@atproto/api' import {TID} from '@atproto/common-web' import {type $Typed} from '@atproto/lex' import { type AtIdentifierString, AtUri, type AtUriString, toDatetimeString, } from '@atproto/syntax' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import {Trans} from '@lingui/react/macro' import {useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import chunk from 'lodash.chunk' import {until} from '#/lib/async/until' import {wait} from '#/lib/async/wait' import {type NavigationProp} from '#/lib/routes/types' import {logger} from '#/logger' import {getAllListMembers} from '#/state/queries/list-members' import {useAppviewClient, usePdsClient, useSession} from '#/state/session' import {atoms as a, platform, useTheme, web} from '#/alf' import {Admonition} from '#/components/Admonition' import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {Loader} from '#/components/Loader' import * as Toast from '#/components/Toast' import {Text} from '#/components/Typography' import {useAnalytics} from '#/analytics' import {app, com} from '#/lexicons' import {CreateOrEditListDialog} from './CreateOrEditListDialog' export function CreateListFromStarterPackDialog({ control, starterPack, }: { control: Dialog.DialogControlProps starterPack: AppBskyGraphDefs.StarterPackView }) { const {_} = useLingui() const t = useTheme() const appviewClient = useAppviewClient() const pdsClient = usePdsClient() const ax = useAnalytics() const {currentAccount} = useSession() const navigation = useNavigation() const queryClient = useQueryClient() const createDialogControl = Dialog.useDialogControl() const loadingDialogControl = Dialog.useDialogControl() const record = starterPack.record as AppBskyGraphStarterpack.Record const onPressCreate = () => { control.close(() => createDialogControl.open()) } const addMembersAndNavigate = async (listUri: string) => { const navigateToList = () => { const urip = new AtUri(listUri) navigation.navigate('ProfileList', { name: urip.hostname, rkey: urip.rkey, }) } if (!starterPack.list || !currentAccount) { loadingDialogControl.close(navigateToList) return } try { // Fetch all members and add them, with minimum 3s duration for UX const listItems = await wait( 3000, (async () => { const items = await getAllListMembers( appviewClient, starterPack.list!.uri, ) if (items.length > 0) { const listitemWrites: $Typed[] = items.map(item => { const listitemRecord: $Typed = { $type: 'app.bsky.graph.listitem', subject: item.subject.did, list: listUri as AtUriString, createdAt: toDatetimeString(new Date()), } return { $type: 'com.atproto.repo.applyWrites#create', collection: 'app.bsky.graph.listitem', rkey: TID.nextStr(), value: listitemRecord, } }) const chunks = chunk(listitemWrites, 50) for (const c of chunks) { await pdsClient.call(com.atproto.repo.applyWrites, { repo: currentAccount.did as AtIdentifierString, writes: c, }) } await until( 5, 1e3, (res: {items: unknown[]}) => res.items.length > 0, () => appviewClient.call(app.bsky.graph.getList, { list: listUri as AtUriString, limit: 1, }), ) } return items })(), ) queryClient.invalidateQueries({queryKey: ['list-members', listUri]}) ax.metric('starterPack:convertToList', { starterPack: starterPack.uri, memberCount: listItems.length, }) } catch (e) { logger.error('Failed to add members to list', {safeMessage: e}) Toast.show(_(msg`List created, but failed to add some members`), { type: 'error', }) } loadingDialogControl.close(navigateToList) } const onListCreated = (listUri: string) => { loadingDialogControl.open() addMembersAndNavigate(listUri) } return ( <> Create list from starter pack This will create a new list with the same name, description, and members as this starter pack. Changes to the starter pack will not be reflected in the list after creation. The list will be an independent copy. Adding members to list... ) }