From 85b75284bac1618139af4887a905a442c6de335f Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 22 Oct 2024 22:30:03 +0100 Subject: [PATCH] Feed creation --- src/screens/Feeds/Wizard/State.tsx | 2 +- src/screens/Feeds/Wizard/StepDetails.tsx | 12 ++-- src/screens/Feeds/Wizard/index.tsx | 37 +++-------- src/state/queries/feed.ts | 85 ++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 37 deletions(-) diff --git a/src/screens/Feeds/Wizard/State.tsx b/src/screens/Feeds/Wizard/State.tsx index 8e25229b3f..5a054bef3b 100644 --- a/src/screens/Feeds/Wizard/State.tsx +++ b/src/screens/Feeds/Wizard/State.tsx @@ -61,7 +61,7 @@ function reducer(state: State, action: Action): State { switch (action.type) { case 'SetName': - updatedState = {...state, name: action.name.slice(0, 50)} + updatedState = {...state, name: action.name.slice(0, 24)} break case 'SetDescription': updatedState = {...state, description: action.description} diff --git a/src/screens/Feeds/Wizard/StepDetails.tsx b/src/screens/Feeds/Wizard/StepDetails.tsx index 11a3db9fda..9a04569dd6 100644 --- a/src/screens/Feeds/Wizard/StepDetails.tsx +++ b/src/screens/Feeds/Wizard/StepDetails.tsx @@ -5,12 +5,12 @@ import {useLingui} from '@lingui/react' import {useProfileQuery} from '#/state/queries/profile' import {useSession} from '#/state/session' -import {useWizardState} from '#/screens/StarterPack/Wizard/State' import {atoms as a, useTheme} from '#/alf' import * as TextField from '#/components/forms/TextField' import {StarterPack} from '#/components/icons/StarterPack' import {ScreenTransition} from '#/components/StarterPack/Wizard/ScreenTransition' import {Text} from '#/components/Typography' +import {useWizardState} from './State' export function StepDetails() { const {_} = useLingui() @@ -41,17 +41,13 @@ export function StepDetails() { dispatch({type: 'SetName', name: text})} /> - + - {state.name?.length ?? 0}/50 + {state.name?.length ?? 0}/24 diff --git a/src/screens/Feeds/Wizard/index.tsx b/src/screens/Feeds/Wizard/index.tsx index ad37f8855f..8f593d73ed 100644 --- a/src/screens/Feeds/Wizard/index.tsx +++ b/src/screens/Feeds/Wizard/index.tsx @@ -4,7 +4,6 @@ import { KeyboardAwareScrollView, useKeyboardController, } from 'react-native-keyboard-controller' -import {Image} from 'expo-image' import {AppBskyGraphDefs, AtUri} from '@atproto/api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, Trans} from '@lingui/macro' @@ -13,20 +12,15 @@ import {useFocusEffect, useNavigation} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' import {HITSLOP_10} 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' -import { - getStarterPackOgCard, - parseStarterPackUri, -} from '#/lib/strings/starter-pack' +import {parseStarterPackUri} from '#/lib/strings/starter-pack' import {logger} from '#/logger' import {isAndroid, isWeb} from '#/platform/detection' import {useModerationOpts} from '#/state/preferences/moderation-opts' +import {useCreateFeedMutation} from '#/state/queries/feed' import {useAllListMembersQuery} from '#/state/queries/list-members' import {useProfileQuery} from '#/state/queries/profile' import { - useCreateStarterPackMutation, useEditStarterPackMutation, useStarterPackQuery, } from '#/state/queries/starter-packs' @@ -127,10 +121,6 @@ function WizardInner({ const {setEnabled} = useKeyboardController() const [state, dispatch] = useWizardState() const {currentAccount} = useSession() - const {data: currentProfile} = useProfileQuery({ - did: currentAccount?.did, - staleTime: 0, - }) const parsed = parseStarterPackUri(currentStarterPack?.uri) React.useEffect(() => { @@ -152,8 +142,7 @@ function WizardInner({ ) const getDefaultName = () => { - const displayName = createSanitizedDisplayName(currentProfile!, true) - return _(msg`${displayName}'s Starter Pack`).slice(0, 50) + return _(msg`Your feed name`) } const wizardUiStrings: Record< @@ -169,18 +158,10 @@ function WizardInner({ const onSuccessCreate = (data: {uri: string; cid: string}) => { const rkey = new AtUri(data.uri).rkey - logEvent('starterPack:create', { - setName: state.name != null, - setDescription: state.description != null, - profilesCount: state.profiles.length, - feedsCount: state.feeds.length, - }) - Image.prefetch([getStarterPackOgCard(currentProfile!.did, rkey)]) dispatch({type: 'SetProcessing', processing: false}) - navigation.replace('StarterPack', { + navigation.replace('ProfileFeed', { name: currentAccount!.handle, rkey, - new: true, }) } @@ -188,14 +169,14 @@ function WizardInner({ if (navigation.canGoBack()) { navigation.goBack() } else { - navigation.replace('StarterPack', { + navigation.replace('ProfileFeed', { name: currentAccount!.handle, rkey: parsed!.rkey, }) } } - const {mutate: createStarterPack} = useCreateStarterPackMutation({ + const {mutate: createFeed} = useCreateFeedMutation({ onSuccess: onSuccessCreate, onError: e => { logger.error('Failed to create starter pack', {safeMessage: e}) @@ -203,6 +184,8 @@ function WizardInner({ Toast.show(_(msg`Failed to create starter pack`), 'xmark') }, }) + + // TODO const {mutate: editStarterPack} = useEditStarterPackMutation({ onSuccess: onSuccessEdit, onError: e => { @@ -224,11 +207,9 @@ function WizardInner({ currentListItems: currentListItems, }) } else { - createStarterPack({ + createFeed({ name: state.name?.trim() || getDefaultName(), description: state.description?.trim(), - profiles: state.profiles, - feeds: state.feeds, }) } } diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts index e5ce19a9ad..d2ce596e1c 100644 --- a/src/state/queries/feed.ts +++ b/src/state/queries/feed.ts @@ -1,4 +1,9 @@ import {useCallback, useEffect, useMemo, useRef} from 'react' +import { + AppBskyFeedGetFeedGenerator, + AppBskyRichtextFacet, + BskyAgent, +} from '@atproto/api' import { AppBskyActorDefs, AppBskyFeedDefs, @@ -19,6 +24,7 @@ import { useQueryClient, } from '@tanstack/react-query' +import {until} from '#/lib/async/until' import {DISCOVER_FEED_URI, DISCOVER_SAVED_FEED} from '#/lib/constants' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' @@ -639,3 +645,82 @@ export function precacheFeedFromGeneratorView( const hydratedFeed = hydrateFeedGenerator(view) precacheFeed(queryClient, hydratedFeed) } + +// --- feeds playground --- + +interface UseCreateFeedMutationParams { + name: string + description?: string +} + +export function useCreateFeedMutation({ + onSuccess, + onError, +}: { + onSuccess: (data: {uri: string; cid: string}) => void + onError: (e: Error) => void +}) { + const agent = useAgent() + + return useMutation< + {uri: string; cid: string}, + Error, + UseCreateFeedMutationParams + >({ + mutationFn: async ({name, description}) => { + let descriptionFacets: AppBskyRichtextFacet.Main[] | undefined + if (description) { + const rt = new RichText({text: description}) + await rt.detectFacets(agent) + descriptionFacets = rt.facets + } + + const res = await agent.api.com.atproto.repo.createRecord({ + repo: agent.session!.did, + collection: 'app.bsky.feed.generator', + record: { + $type: 'app.bsky.feed.generator', + createdAt: new Date().toISOString(), + did: 'did:web:feeed.club', + displayName: name, + description, + descriptionFacets, + labels: { + $type: 'com.atproto.label.defs#selfLabels', + values: [{val: 'feeed-club'}], + }, + '$club.feeed.generator': { + $type: 'club.feeed.generator', + actors: [], + handleSuffixes: [], + tags: [], + }, + }, + }) + return res.data + }, + onSuccess: async data => { + await whenAppViewReady(agent, data.uri, v => { + return typeof v?.data.view.uri === 'string' + }) + // TODO: Invalidate query + onSuccess(data) + }, + onError: async error => { + onError(error) + }, + }) +} + +async function whenAppViewReady( + agent: BskyAgent, + uri: string, + fn: (res?: AppBskyFeedGetFeedGenerator.Response) => boolean, +) { + await until( + 5, // 5 tries + 1e3, // 1s delay between tries + fn, + () => agent.app.bsky.feed.getFeedGenerator({feed: uri}), + ) +}