diff --git a/src/components/StarterPack/ProfileStarterPacks.tsx b/src/components/StarterPack/ProfileStarterPacks.tsx index 90098ed0d3..41fab57ec1 100644 --- a/src/components/StarterPack/ProfileStarterPacks.tsx +++ b/src/components/StarterPack/ProfileStarterPacks.tsx @@ -8,16 +8,14 @@ import { } from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useQueryClient} from '@tanstack/react-query' import {cleanError} from '#/lib/strings/errors' import {useTheme} from '#/lib/ThemeContext' import {logger} from '#/logger' import {isNative} from '#/platform/detection' -import {hydrateFeedGenerator} from '#/state/queries/feed' -import {usePreferencesQuery} from '#/state/queries/preferences' -import {RQKEY, useProfileFeedgensQuery} from '#/state/queries/profile-feedgens' import {usePalette} from 'lib/hooks/usePalette' +import {useActorStarterPacksQuery} from 'state/queries/actor-starter-packs' +import {usePreferencesQuery} from 'state/queries/preferences' import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {ErrorMessage} from 'view/com/util/error/ErrorMessage' import {List, ListRef} from 'view/com/util/List' @@ -55,20 +53,23 @@ export const ProfileStarterPacks = React.forwardRef< const {_} = useLingui() const theme = useTheme() const [isPTRing, setIsPTRing] = React.useState(false) - const opts = React.useMemo(() => ({enabled}), [enabled]) + const { data, isFetching, - isFetched, - hasNextPage, - fetchNextPage, isError, - error, + isFetched, + fetchNextPage, refetch, - } = useProfileFeedgensQuery(did, opts) - const isEmpty = !isFetching && !data?.pages[0]?.feeds.length + hasNextPage, + error, + } = useActorStarterPacksQuery({ + did, + }) const {data: preferences} = usePreferencesQuery() + const isEmpty = !isFetching && data?.pages.length === 0 + const items = React.useMemo(() => { let items: any[] = [] if (isError && isEmpty) { @@ -80,7 +81,7 @@ export const ProfileStarterPacks = React.forwardRef< items = items.concat([EMPTY]) } else if (data?.pages) { for (const page of data?.pages) { - items = items.concat(page.feeds.map(feed => hydrateFeedGenerator(feed))) + items = page.starterPacks } } if (isError && !isEmpty) { @@ -89,18 +90,8 @@ export const ProfileStarterPacks = React.forwardRef< return items }, [isError, isEmpty, isFetched, isFetching, data]) - const queryClient = useQueryClient() - - const onScrollToTop = React.useCallback(() => { - scrollElRef.current?.scrollToOffset({ - animated: isNative, - offset: -headerOffset, - }) - queryClient.invalidateQueries({queryKey: RQKEY(did)}) - }, [scrollElRef, queryClient, headerOffset, did]) - React.useImperativeHandle(ref, () => ({ - scrollToTop: onScrollToTop, + scrollToTop: () => {}, })) const onRefresh = React.useCallback(async () => { diff --git a/src/screens/StarterPack/Wizard/State.tsx b/src/screens/StarterPack/Wizard/State.tsx index 2b778ff741..68c573f129 100644 --- a/src/screens/StarterPack/Wizard/State.tsx +++ b/src/screens/StarterPack/Wizard/State.tsx @@ -2,6 +2,8 @@ import React from 'react' import {AppBskyActorDefs} from '@atproto/api' import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs' +import {useAgent, useSession} from 'state/session' + const steps = ['Details', 'Profiles', 'Feeds'] as const type Step = (typeof steps)[number] @@ -28,11 +30,12 @@ interface State { processing: boolean } -type TStateContext = [State, (action: Action) => void] +type TStateContext = [State, (action: Action) => void, () => Promise] const StateContext = React.createContext([ {} as State, (_: Action) => {}, + async () => {}, ]) export const useWizardState = () => React.useContext(StateContext) @@ -107,7 +110,10 @@ export function Provider({ initialStep?: Step children: React.ReactNode }) { - const stateAndReducer = React.useReducer( + const agent = useAgent() + const {currentAccount} = useSession() + + const [state, dispatch] = React.useReducer( reducer, initialState ? { @@ -123,8 +129,60 @@ export function Provider({ }, ) + const submit = async () => { + dispatch({type: 'SetProcessing', processing: true}) + + try { + const list = await agent.app.bsky.graph.list.create( + {repo: currentAccount?.did}, + { + name: state.name ?? '', + description: state.description ?? '', + descriptionFacets: [], + avatar: undefined, + createdAt: new Date().toISOString(), + purpose: 'app.bsky.graph.defs#referencelist', + }, + ) + + await agent.com.atproto.repo.applyWrites({ + repo: currentAccount!.did, + writes: state.profiles.map(p => ({ + collection: 'app.bsky.graph.listitem', + value: { + $type: 'app.bsky.graph.listitem', + subject: p.did, + list: list.uri, + createdAt: new Date().toISOString(), + }, + })), + }) + + await agent.app.bsky.graph.starterpack.create( + { + repo: currentAccount!.did, + }, + { + name: state.name ?? '', + description: state.description ?? '', + descriptionFacets: [], + list: list.uri, + feeds: state.feeds.map(f => ({ + uri: f.uri, + })), + createdAt: new Date().toISOString(), + }, + ) + } catch (e) { + // TODO add error to state + console.error(e) + } finally { + dispatch({type: 'SetProcessing', processing: false}) + } + } + return ( - + {children} ) diff --git a/src/screens/StarterPack/Wizard/index.tsx b/src/screens/StarterPack/Wizard/index.tsx index 7d5baaeaa8..853151e4f3 100644 --- a/src/screens/StarterPack/Wizard/index.tsx +++ b/src/screens/StarterPack/Wizard/index.tsx @@ -75,7 +75,7 @@ function WizardInner() { const navigation = useNavigation() const {_} = useLingui() const t = useTheme() - const [state, dispatch] = useWizardState() + const [state, dispatch, submit] = useWizardState() const {currentAccount} = useSession() const {data: currentProfile} = useProfileQuery({ did: currentAccount?.did, @@ -130,7 +130,7 @@ function WizardInner() { ), }) } else if (state.currentStep === 'Feeds') { - dispatch({type: 'SetProcessing', processing: true}) + submit() return } diff --git a/src/state/queries/actor-starter-packs.ts b/src/state/queries/actor-starter-packs.ts new file mode 100644 index 0000000000..fda876e64f --- /dev/null +++ b/src/state/queries/actor-starter-packs.ts @@ -0,0 +1,33 @@ +import {AppBskyGraphGetActorStarterPacks} from '@atproto/api' +import {InfiniteData, QueryKey, useInfiniteQuery} from '@tanstack/react-query' + +import {STALE} from 'state/queries/index' +import {useAgent} from 'state/session' + +const RQKEY_ROOT = 'actor-starter-packs' + +export function useActorStarterPacksQuery({did}: {did?: string}) { + const agent = useAgent() + + return useInfiniteQuery< + AppBskyGraphGetActorStarterPacks.OutputSchema, + Error, + InfiniteData, + QueryKey, + string | undefined + >({ + queryKey: [RQKEY_ROOT, did], + queryFn: async ({pageParam}: {pageParam?: string}) => { + const res = await agent.app.bsky.graph.getActorStarterPacks({ + actor: did!, + limit: 10, + cursor: pageParam, + }) + return res.data + }, + enabled: Boolean(did), + initialPageParam: undefined, + getNextPageParam: lastPage => lastPage.cursor, + staleTime: STALE.MINUTES.ONE, + }) +}