diff options
Diffstat (limited to 'src/state/queries')
-rw-r--r-- | src/state/queries/feed.ts | 101 | ||||
-rw-r--r-- | src/state/queries/post-feed.ts | 6 | ||||
-rw-r--r-- | src/state/queries/preferences/const.ts | 17 | ||||
-rw-r--r-- | src/state/queries/preferences/index.ts | 71 | ||||
-rw-r--r-- | src/state/queries/preferences/types.ts | 5 |
5 files changed, 98 insertions, 102 deletions
diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts index 1741d113c..19cded087 100644 --- a/src/state/queries/feed.ts +++ b/src/state/queries/feed.ts @@ -1,4 +1,5 @@ import { + AppBskyActorDefs, AppBskyFeedDefs, AppBskyGraphDefs, AppBskyUnspeccedGetPopularFeedGenerators, @@ -13,16 +14,19 @@ import { useQuery, } from '@tanstack/react-query' +import {DISCOVER_FEED_URI, DISCOVER_SAVED_FEED} from '#/lib/constants' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' import {STALE} from '#/state/queries' import {usePreferencesQuery} from '#/state/queries/preferences' import {useAgent, useSession} from '#/state/session' import {router} from '#/routes' +import {FeedDescriptor} from './post-feed' export type FeedSourceFeedInfo = { type: 'feed' uri: string + feedDescriptor: FeedDescriptor route: { href: string name: string @@ -41,6 +45,7 @@ export type FeedSourceFeedInfo = { export type FeedSourceListInfo = { type: 'list' uri: string + feedDescriptor: FeedDescriptor route: { href: string name: string @@ -79,6 +84,7 @@ export function hydrateFeedGenerator( return { type: 'feed', uri: view.uri, + feedDescriptor: `feedgen|${view.uri}`, cid: view.cid, route: { href, @@ -110,6 +116,7 @@ export function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo { return { type: 'list', uri: view.uri, + feedDescriptor: `list|${view.uri}`, route: { href, name: route[0], @@ -202,27 +209,15 @@ export function useSearchPopularFeedsMutation() { }) } -const FOLLOWING_FEED_STUB: FeedSourceInfo = { - type: 'feed', - displayName: 'Following', - uri: '', - route: { - href: '/', - name: 'Home', - params: {}, - }, - cid: '', - avatar: '', - description: new RichText({text: ''}), - creatorDid: '', - creatorHandle: '', - likeCount: 0, - likeUri: '', +export type SavedFeedSourceInfo = FeedSourceInfo & { + savedFeed: AppBskyActorDefs.SavedFeed } -const DISCOVER_FEED_STUB: FeedSourceInfo = { + +const PWI_DISCOVER_FEED_STUB: SavedFeedSourceInfo = { type: 'feed', displayName: 'Discover', - uri: '', + uri: DISCOVER_FEED_URI, + feedDescriptor: `feedgen|${DISCOVER_FEED_URI}`, route: { href: '/', name: 'Home', @@ -235,6 +230,11 @@ const DISCOVER_FEED_STUB: FeedSourceInfo = { creatorHandle: '', likeCount: 0, likeUri: '', + // --- + savedFeed: { + id: 'pwi-discover', + ...DISCOVER_SAVED_FEED, + }, } const pinnedFeedInfosQueryKeyRoot = 'pinnedFeedsInfos' @@ -243,43 +243,45 @@ export function usePinnedFeedsInfos() { const {hasSession} = useSession() const {getAgent} = useAgent() const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery() - const pinnedUris = preferences?.feeds?.pinned ?? [] + const pinnedItems = preferences?.savedFeeds.filter(feed => feed.pinned) ?? [] return useQuery({ staleTime: STALE.INFINITY, enabled: !isLoadingPrefs, queryKey: [ pinnedFeedInfosQueryKeyRoot, - (hasSession ? 'authed:' : 'unauthed:') + pinnedUris.join(','), + (hasSession ? 'authed:' : 'unauthed:') + + pinnedItems.map(f => f.value).join(','), ], queryFn: async () => { - let resolved = new Map() + if (!hasSession) { + return [PWI_DISCOVER_FEED_STUB] + } + + let resolved = new Map<string, FeedSourceInfo>() // Get all feeds. We can do this in a batch. - const feedUris = pinnedUris.filter( - uri => getFeedTypeFromUri(uri) === 'feed', - ) + const pinnedFeeds = pinnedItems.filter(feed => feed.type === 'feed') let feedsPromise = Promise.resolve() - if (feedUris.length > 0) { + if (pinnedFeeds.length > 0) { feedsPromise = getAgent() .app.bsky.feed.getFeedGenerators({ - feeds: feedUris, + feeds: pinnedFeeds.map(f => f.value), }) .then(res => { - for (let feedView of res.data.feeds) { + for (let i = 0; i < res.data.feeds.length; i++) { + const feedView = res.data.feeds[i] resolved.set(feedView.uri, hydrateFeedGenerator(feedView)) } }) } // Get all lists. This currently has to be done individually. - const listUris = pinnedUris.filter( - uri => getFeedTypeFromUri(uri) === 'list', - ) - const listsPromises = listUris.map(listUri => + const pinnedLists = pinnedItems.filter(feed => feed.type === 'list') + const listsPromises = pinnedLists.map(list => getAgent() .app.bsky.graph.getList({ - list: listUri, + list: list.value, limit: 1, }) .then(res => { @@ -288,12 +290,37 @@ export function usePinnedFeedsInfos() { }), ) - // The returned result will have the original order. - const result = [hasSession ? FOLLOWING_FEED_STUB : DISCOVER_FEED_STUB] await Promise.allSettled([feedsPromise, ...listsPromises]) - for (let pinnedUri of pinnedUris) { - if (resolved.has(pinnedUri)) { - result.push(resolved.get(pinnedUri)) + + // order the feeds/lists in the order they were pinned + const result: SavedFeedSourceInfo[] = [] + for (let pinnedItem of pinnedItems) { + const feedInfo = resolved.get(pinnedItem.value) + if (feedInfo) { + result.push({ + ...feedInfo, + savedFeed: pinnedItem, + }) + } else if (pinnedItem.type === 'timeline') { + result.push({ + type: 'feed', + displayName: 'Following', + uri: pinnedItem.value, + feedDescriptor: 'following', + route: { + href: '/', + name: 'Home', + params: {}, + }, + cid: '', + avatar: '', + description: new RichText({text: ''}), + creatorDid: '', + creatorHandle: '', + likeCount: 0, + likeUri: '', + savedFeed: pinnedItem, + }) } } return result diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts index dc86a9ba0..7b312edfe 100644 --- a/src/state/queries/post-feed.ts +++ b/src/state/queries/post-feed.ts @@ -44,8 +44,8 @@ type AuthorFilter = | 'posts_with_media' type FeedUri = string type ListUri = string + export type FeedDescriptor = - | 'home' | 'following' | `author|${ActorDid}|${AuthorFilter}` | `feedgen|${FeedUri}` @@ -390,7 +390,7 @@ function createApi({ userInterests?: string getAgent: () => BskyAgent }) { - if (feedDesc === 'home') { + if (feedDesc === 'following') { if (feedParams.mergeFeedEnabled) { return new MergeFeedAPI({ getAgent, @@ -401,8 +401,6 @@ function createApi({ } else { return new HomeFeedAPI({getAgent, userInterests}) } - } else if (feedDesc === 'following') { - return new FollowingFeedAPI({getAgent}) } else if (feedDesc.startsWith('author')) { const [_, actor, filter] = feedDesc.split('|') return new AuthorFeedAPI({getAgent, feedParams: {actor, filter}}) diff --git a/src/state/queries/preferences/const.ts b/src/state/queries/preferences/const.ts index 4cb4d1e96..d94edb47e 100644 --- a/src/state/queries/preferences/const.ts +++ b/src/state/queries/preferences/const.ts @@ -1,8 +1,8 @@ +import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/moderation' import { - UsePreferencesQueryResponse, ThreadViewPreferences, + UsePreferencesQueryResponse, } from '#/state/queries/preferences/types' -import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/moderation' export const DEFAULT_HOME_FEED_PREFS: UsePreferencesQueryResponse['feedViewPrefs'] = { @@ -20,20 +20,8 @@ export const DEFAULT_THREAD_VIEW_PREFS: ThreadViewPreferences = { lab_treeViewEnabled: false, } -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_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,4 +33,5 @@ export const DEFAULT_LOGGED_OUT_PREFERENCES: UsePreferencesQueryResponse = { threadViewPrefs: DEFAULT_THREAD_VIEW_PREFS, userAge: 13, // TODO(pwi) interests: {tags: []}, + savedFeeds: [], } diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index f51eaac2a..b3d2fa9ec 100644 --- a/src/state/queries/preferences/index.ts +++ b/src/state/queries/preferences/index.ts @@ -51,14 +51,11 @@ 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) - }) || [], - }, + savedFeeds: res.savedFeeds.filter(f => f.type !== 'unknown'), + /** + * Special preference, only used for following feed, previously + * called `home` + */ feedViewPrefs: { ...DEFAULT_HOME_FEED_PREFS, ...(res.feedViewPrefs.home || {}), @@ -168,6 +165,10 @@ export function useSetFeedViewPreferencesMutation() { return useMutation<void, unknown, Partial<BskyFeedViewPreference>>({ mutationFn: async prefs => { + /* + * special handling here, merged into `feedViewPrefs` above, since + * following was previously called `home` + */ await getAgent().setFeedViewPrefs('home', prefs) // triggers a refetch await queryClient.invalidateQueries({ @@ -192,17 +193,13 @@ export function useSetThreadViewPreferencesMutation() { }) } -export function useSetSaveFeedsMutation() { +export function useOverwriteSavedFeedsMutation() { const queryClient = useQueryClient() const {getAgent} = useAgent() - return useMutation< - void, - unknown, - Pick<UsePreferencesQueryResponse['feeds'], 'saved' | 'pinned'> - >({ - mutationFn: async ({saved, pinned}) => { - await getAgent().setSavedFeeds(saved, pinned) + return useMutation<void, unknown, AppBskyActorDefs.SavedFeed[]>({ + mutationFn: async savedFeeds => { + await getAgent().overwriteSavedFeeds(savedFeeds) // triggers a refetch await queryClient.invalidateQueries({ queryKey: preferencesQueryKey, @@ -211,13 +208,17 @@ export function useSetSaveFeedsMutation() { }) } -export function useSaveFeedMutation() { +export function useAddSavedFeedsMutation() { const queryClient = useQueryClient() const {getAgent} = useAgent() - return useMutation<void, unknown, {uri: string}>({ - mutationFn: async ({uri}) => { - await getAgent().addSavedFeed(uri) + return useMutation< + void, + unknown, + Pick<AppBskyActorDefs.SavedFeed, 'type' | 'value' | 'pinned'>[] + >({ + mutationFn: async savedFeeds => { + await getAgent().addSavedFeeds(savedFeeds) track('CustomFeed:Save') // triggers a refetch await queryClient.invalidateQueries({ @@ -231,9 +232,9 @@ export function useRemoveFeedMutation() { const queryClient = useQueryClient() const {getAgent} = useAgent() - return useMutation<void, unknown, {uri: string}>({ - mutationFn: async ({uri}) => { - await getAgent().removeSavedFeed(uri) + return useMutation<void, unknown, Pick<AppBskyActorDefs.SavedFeed, 'id'>>({ + mutationFn: async savedFeed => { + await getAgent().removeSavedFeeds([savedFeed.id]) track('CustomFeed:Unsave') // triggers a refetch await queryClient.invalidateQueries({ @@ -243,30 +244,14 @@ export function useRemoveFeedMutation() { }) } -export function usePinFeedMutation() { +export function useUpdateSavedFeedsMutation() { const queryClient = useQueryClient() const {getAgent} = useAgent() - return useMutation<void, unknown, {uri: string}>({ - mutationFn: async ({uri}) => { - await getAgent().addPinnedFeed(uri) - track('CustomFeed:Pin', {uri}) - // triggers a refetch - await queryClient.invalidateQueries({ - queryKey: preferencesQueryKey, - }) - }, - }) -} - -export function useUnpinFeedMutation() { - const queryClient = useQueryClient() - const {getAgent} = useAgent() + return useMutation<void, unknown, AppBskyActorDefs.SavedFeed[]>({ + mutationFn: async feeds => { + await getAgent().updateSavedFeeds(feeds) - return useMutation<void, unknown, {uri: string}>({ - mutationFn: async ({uri}) => { - await getAgent().removePinnedFeed(uri) - track('CustomFeed:Unpin', {uri}) // triggers a refetch await queryClient.invalidateQueries({ queryKey: preferencesQueryKey, diff --git a/src/state/queries/preferences/types.ts b/src/state/queries/preferences/types.ts index 96da16f1a..928bb90da 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<BskyPreferences['feeds']> & { - unpinned: string[] - } } export type ThreadViewPreferences = Pick< |