diff --git a/src/screens/Onboarding/StepFinished.tsx b/src/screens/Onboarding/StepFinished.tsx index 56635c672d..b4a28c3084 100644 --- a/src/screens/Onboarding/StepFinished.tsx +++ b/src/screens/Onboarding/StepFinished.tsx @@ -1,5 +1,6 @@ import React from 'react' import {View} from 'react-native' +import {TID} from '@atproto/common-web' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -62,10 +63,14 @@ export function StepFinished() { // these must be serial (async () => { await getAgent().setInterestsPref({tags: selectedInterests}) - await saveFeeds({ - saved: selectedFeeds, - pinned: selectedFeeds, - }) + await saveFeeds( + selectedFeeds.map(f => ({ + id: TID.nextStr(), + type: 'feed', + value: f, + pinned: true, + })), + ) })(), ]) } catch (e: any) { diff --git a/src/screens/Signup/state.ts b/src/screens/Signup/state.ts index 86a144368f..9e4ab32c07 100644 --- a/src/screens/Signup/state.ts +++ b/src/screens/Signup/state.ts @@ -14,9 +14,9 @@ import {createFullHandle, validateHandle} from '#/lib/strings/handles' import {getAge} from '#/lib/strings/time' import {logger} from '#/logger' import { - DEFAULT_PROD_FEEDS, + DEFAULT_PROD_FEED, + useAddSavedFeedMutation, usePreferencesSetBirthDateMutation, - useSetSaveFeedsMutation, } from '#/state/queries/preferences' import {useSessionApi} from '#/state/session' import {useOnboardingDispatch} from '#/state/shell' @@ -208,7 +208,7 @@ export function useSubmitSignup({ const {_} = useLingui() const {createAccount} = useSessionApi() const {mutateAsync: setBirthDate} = usePreferencesSetBirthDateMutation() - const {mutate: setSavedFeeds} = useSetSaveFeedsMutation() + const {mutate: addSavedFeed} = useAddSavedFeedMutation() const onboardingDispatch = useOnboardingDispatch() return useCallback( @@ -266,7 +266,7 @@ export function useSubmitSignup({ }) await setBirthDate({birthDate: state.dateOfBirth}) if (IS_PROD_SERVICE(state.serviceUrl)) { - setSavedFeeds(DEFAULT_PROD_FEEDS) + addSavedFeed(DEFAULT_PROD_FEED) } } catch (e: any) { onboardingDispatch({type: 'skip'}) // undo starting the onboard @@ -314,7 +314,7 @@ export function useSubmitSignup({ onboardingDispatch, createAccount, setBirthDate, - setSavedFeeds, + addSavedFeed, ], ) } diff --git a/src/state/queries/preferences/const.ts b/src/state/queries/preferences/const.ts index 314b6a8232..b0ae7a9310 100644 --- a/src/state/queries/preferences/const.ts +++ b/src/state/queries/preferences/const.ts @@ -22,18 +22,14 @@ export const DEFAULT_THREAD_VIEW_PREFS: ThreadViewPreferences = { const DEFAULT_PROD_FEED_PREFIX = (rkey: string) => `at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/${rkey}` -export const DEFAULT_PROD_FEEDS = { - pinned: [DEFAULT_PROD_FEED_PREFIX('whats-hot')], - saved: [DEFAULT_PROD_FEED_PREFIX('whats-hot')], +export const DEFAULT_PROD_FEED = { + type: 'feed', + value: DEFAULT_PROD_FEED_PREFIX('whats-hot'), + pinned: true, } export const DEFAULT_LOGGED_OUT_PREFERENCES: UsePreferencesQueryResponse = { birthDate: new Date('2022-11-17'), // TODO(pwi) - feeds: { - saved: [], - pinned: [], - unpinned: [], - }, moderationPrefs: { adultContentEnabled: false, labels: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES, @@ -45,5 +41,5 @@ export const DEFAULT_LOGGED_OUT_PREFERENCES: UsePreferencesQueryResponse = { threadViewPrefs: DEFAULT_THREAD_VIEW_PREFS, userAge: 13, // TODO(pwi) interests: {tags: []}, - savedFeeds: [], // TODO could put stub ehre + savedFeeds: [], } diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index 7200a4dd5b..d719660e60 100644 --- a/src/state/queries/preferences/index.ts +++ b/src/state/queries/preferences/index.ts @@ -54,14 +54,6 @@ export function usePreferencesQuery() { const preferences: UsePreferencesQueryResponse = { ...res, - feeds: { - saved: res.feeds?.saved || [], - pinned: res.feeds?.pinned || [], - unpinned: - res.feeds.saved?.filter(f => { - return !res.feeds.pinned?.includes(f) - }) || [], - }, feedViewPrefs: { ...DEFAULT_HOME_FEED_PREFS, ...(res.feedViewPrefs.home || {}), diff --git a/src/state/queries/preferences/types.ts b/src/state/queries/preferences/types.ts index 96da16f1a8..928bb90da8 100644 --- a/src/state/queries/preferences/types.ts +++ b/src/state/queries/preferences/types.ts @@ -1,7 +1,7 @@ import { + BskyFeedViewPreference, BskyPreferences, BskyThreadViewPreference, - BskyFeedViewPreference, } from '@atproto/api' export type UsePreferencesQueryResponse = Omit< @@ -16,9 +16,6 @@ export type UsePreferencesQueryResponse = Omit< */ threadViewPrefs: ThreadViewPreferences userAge: number | undefined - feeds: Required & { - unpinned: string[] - } } export type ThreadViewPreferences = Pick< diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index 2b8fde632c..7bdf399ec3 100644 --- a/src/view/com/feeds/FeedPage.tsx +++ b/src/view/com/feeds/FeedPage.tsx @@ -1,5 +1,6 @@ import React from 'react' import {useWindowDimensions, View} from 'react-native' +import {AppBskyActorDefs} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' @@ -34,6 +35,7 @@ export function FeedPage({ feedParams, renderEmptyState, renderEndOfFeed, + savedFeedConfig, }: { testID?: string feed: FeedDescriptor @@ -41,6 +43,7 @@ export function FeedPage({ isPageFocused: boolean renderEmptyState: () => JSX.Element renderEndOfFeed?: () => JSX.Element + savedFeedConfig?: AppBskyActorDefs.SavedFeed }) { const {hasSession} = useSession() const {_} = useLingui() @@ -132,6 +135,7 @@ export function FeedPage({ renderEmptyState={renderEmptyState} renderEndOfFeed={renderEndOfFeed} headerOffset={headerOffset} + savedFeedConfig={savedFeedConfig} /> {(isScrolledDown || hasNew) && ( diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index fb67d35c5c..439091ef2e 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -8,6 +8,7 @@ import { View, ViewStyle, } from 'react-native' +import {AppBskyActorDefs} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' @@ -63,6 +64,7 @@ let Feed = ({ desktopFixedHeightOffset, ListHeaderComponent, extraData, + savedFeedConfig, }: { feed: FeedDescriptor feedParams?: FeedParams @@ -81,6 +83,7 @@ let Feed = ({ desktopFixedHeightOffset?: number ListHeaderComponent?: () => JSX.Element extraData?: any + savedFeedConfig?: AppBskyActorDefs.SavedFeed }): React.ReactNode => { const theme = useTheme() const {track} = useAnalytics() @@ -278,6 +281,7 @@ let Feed = ({ feedDesc={feed} error={error ?? undefined} onPressTryAgain={onPressTryAgain} + savedFeedConfig={savedFeedConfig} /> ) } else if (item === LOAD_MORE_ERROR_ITEM) { @@ -300,7 +304,15 @@ let Feed = ({ } return }, - [feed, error, onPressTryAgain, onPressRetryLoadMore, renderEmptyState, _], + [ + feed, + error, + onPressTryAgain, + onPressRetryLoadMore, + renderEmptyState, + _, + savedFeedConfig, + ], ) const shouldRenderEndOfFeed = diff --git a/src/view/com/posts/FeedErrorMessage.tsx b/src/view/com/posts/FeedErrorMessage.tsx index d4ca38d07e..a152bc9095 100644 --- a/src/view/com/posts/FeedErrorMessage.tsx +++ b/src/view/com/posts/FeedErrorMessage.tsx @@ -1,21 +1,22 @@ import React from 'react' import {View} from 'react-native' -import {AppBskyFeedGetAuthorFeed, AtUri} from '@atproto/api' -import {Text} from '../util/text/Text' -import {Button} from '../util/forms/Button' -import * as Toast from '../util/Toast' -import {ErrorMessage} from '../util/error/ErrorMessage' -import {usePalette} from 'lib/hooks/usePalette' -import {useNavigation} from '@react-navigation/native' -import {NavigationProp} from 'lib/routes/types' -import {logger} from '#/logger' +import {AppBskyActorDefs, AppBskyFeedGetAuthorFeed, AtUri} from '@atproto/api' import {msg as msgLingui, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {FeedDescriptor} from '#/state/queries/post-feed' -import {EmptyState} from '../util/EmptyState' +import {useNavigation} from '@react-navigation/native' + import {cleanError} from '#/lib/strings/errors' +import {logger} from '#/logger' +import {FeedDescriptor} from '#/state/queries/post-feed' import {useRemoveFeedMutation} from '#/state/queries/preferences' +import {usePalette} from 'lib/hooks/usePalette' +import {NavigationProp} from 'lib/routes/types' import * as Prompt from '#/components/Prompt' +import {EmptyState} from '../util/EmptyState' +import {ErrorMessage} from '../util/error/ErrorMessage' +import {Button} from '../util/forms/Button' +import {Text} from '../util/text/Text' +import * as Toast from '../util/Toast' export enum KnownError { Block = 'Block', @@ -33,10 +34,12 @@ export function FeedErrorMessage({ feedDesc, error, onPressTryAgain, + savedFeedConfig, }: { feedDesc: FeedDescriptor error?: Error onPressTryAgain: () => void + savedFeedConfig?: AppBskyActorDefs.SavedFeed }) { const {_: _l} = useLingui() const knownError = React.useMemo( @@ -46,13 +49,15 @@ export function FeedErrorMessage({ if ( typeof knownError !== 'undefined' && knownError !== KnownError.Unknown && - (feedDesc.startsWith('feedgen') || knownError === KnownError.FeedNSFPublic) + (savedFeedConfig?.type === 'feed' || + knownError === KnownError.FeedNSFPublic) ) { return ( ) } @@ -79,10 +84,12 @@ function FeedgenErrorMessage({ feedDesc, knownError, rawError, + savedFeedConfig, }: { feedDesc: FeedDescriptor knownError: KnownError rawError?: Error + savedFeedConfig?: AppBskyActorDefs.SavedFeed }) { const pal = usePalette('default') const {_: _l} = useLingui() @@ -131,7 +138,8 @@ function FeedgenErrorMessage({ const onRemoveFeed = React.useCallback(async () => { try { - await removeFeed({uri}) + if (!savedFeedConfig) return + await removeFeed(savedFeedConfig) } catch (err) { Toast.show( _l( @@ -140,7 +148,7 @@ function FeedgenErrorMessage({ ) logger.error('Failed to remove feed', {message: err}) } - }, [uri, removeFeed, _l]) + }, [removeFeed, _l, savedFeedConfig]) const cta = React.useMemo(() => { switch (knownError) { @@ -154,13 +162,14 @@ function FeedgenErrorMessage({ case KnownError.FeedgenUnknown: { return ( - {knownError === KnownError.FeedgenDoesNotExist && ( -