import {View} from 'react-native' import { type $Typed, type AppBskyGraphDefs, type AppBskyGraphListitem, type AppBskyGraphStarterpack, AtUri, type ComAtprotoRepoApplyWrites, } from '@atproto/api' import {TID} from '@atproto/common-web' 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 {useAgent, 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 {CreateOrEditListDialog} from './CreateOrEditListDialog' export function CreateListFromStarterPackDialog({ control, starterPack, }: { control: Dialog.DialogControlProps starterPack: AppBskyGraphDefs.StarterPackView }) { const {_} = useLingui() const t = useTheme() const agent = useAgent() 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(agent, 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, createdAt: new Date().toISOString(), } 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 agent.com.atproto.repo.applyWrites({ repo: currentAccount.did, writes: c, }) } await until( 5, 1e3, (res: {data: {items: unknown[]}}) => res.data.items.length > 0, () => agent.app.bsky.graph.getList({ list: listUri, 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... ) }