Prevent duplicate keys

This commit is contained in:
Eric Bailey
2024-04-23 18:56:11 -05:00
parent c978fc20c6
commit 4569981543
3 changed files with 39 additions and 23 deletions
+36 -20
View File
@@ -22,6 +22,7 @@ import {getAgent, useSession} from '#/state/session'
import {router} from '#/routes' import {router} from '#/routes'
export type FeedSourceFeedInfo = { export type FeedSourceFeedInfo = {
id: string
type: 'feed' type: 'feed'
uri: string uri: string
route: { route: {
@@ -40,6 +41,7 @@ export type FeedSourceFeedInfo = {
} }
export type FeedSourceListInfo = { export type FeedSourceListInfo = {
id: string
type: 'list' type: 'list'
uri: string uri: string
route: { route: {
@@ -70,6 +72,7 @@ const feedSourceNSIDs = {
export function hydrateFeedGenerator( export function hydrateFeedGenerator(
view: AppBskyFeedDefs.GeneratorView, view: AppBskyFeedDefs.GeneratorView,
extra?: {id: string},
): FeedSourceInfo { ): FeedSourceInfo {
const urip = new AtUri(view.uri) const urip = new AtUri(view.uri)
const collection = const collection =
@@ -78,6 +81,7 @@ export function hydrateFeedGenerator(
const route = router.matchPath(href) const route = router.matchPath(href)
return { return {
id: extra?.id || '',
type: 'feed', type: 'feed',
uri: view.uri, uri: view.uri,
cid: view.cid, cid: view.cid,
@@ -101,7 +105,10 @@ export function hydrateFeedGenerator(
} }
} }
export function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo { export function hydrateList(
view: AppBskyGraphDefs.ListView,
extra?: {id: string},
): FeedSourceInfo {
const urip = new AtUri(view.uri) const urip = new AtUri(view.uri)
const collection = const collection =
urip.collection === 'app.bsky.feed.generator' ? 'feed' : 'lists' urip.collection === 'app.bsky.feed.generator' ? 'feed' : 'lists'
@@ -109,6 +116,7 @@ export function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo {
const route = router.matchPath(href) const route = router.matchPath(href)
return { return {
id: extra?.id || '',
type: 'list', type: 'list',
uri: view.uri, uri: view.uri,
route: { route: {
@@ -204,6 +212,7 @@ export function useSearchPopularFeedsMutation() {
* The following feed, with fallbacks to Discover * The following feed, with fallbacks to Discover
*/ */
const PWI_DISCOVER_FEED_STUB: FeedSourceInfo = { const PWI_DISCOVER_FEED_STUB: FeedSourceInfo = {
id: 'pwi',
type: 'feed', type: 'feed',
displayName: 'Discover', displayName: 'Discover',
uri: DISCOVER_FEED_URI, uri: DISCOVER_FEED_URI,
@@ -226,13 +235,9 @@ const pinnedFeedInfosQueryKeyRoot = 'pinnedFeedsInfos'
export function usePinnedFeedsInfos() { export function usePinnedFeedsInfos() {
const {hasSession} = useSession() const {hasSession} = useSession()
const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery() const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery()
const pinnedFeeds = preferences?.savedFeeds.filter(feed => feed.pinned) ?? [] const pinnedItems = preferences?.savedFeeds.filter(feed => feed.pinned) ?? []
const feedUris = pinnedFeeds const pinnedFeeds = pinnedItems.filter(feed => feed.type === 'feed')
.filter(feed => feed.type === 'feed') const pinnedLists = pinnedItems.filter(feed => feed.type === 'list')
.map(f => f.value)
const listUris = pinnedFeeds
.filter(feed => feed.type === 'list')
.map(f => f.value)
return useQuery({ return useQuery({
staleTime: STALE.INFINITY, staleTime: STALE.INFINITY,
@@ -240,35 +245,45 @@ export function usePinnedFeedsInfos() {
queryKey: [ queryKey: [
pinnedFeedInfosQueryKeyRoot, pinnedFeedInfosQueryKeyRoot,
(hasSession ? 'authed:' : 'unauthed:') + (hasSession ? 'authed:' : 'unauthed:') +
pinnedFeeds.map(f => f.value).join(','), pinnedItems.map(f => f.value).join(','),
], ],
queryFn: async () => { queryFn: async () => {
let resolved = new Map<string, FeedSourceInfo>() let resolved = new Map<string, FeedSourceInfo>()
let pinnedFeedsIds = pinnedFeeds.map(f => f.id)
// Get all feeds. We can do this in a batch. // Get all feeds. We can do this in a batch.
let feedsPromise = Promise.resolve() let feedsPromise = Promise.resolve()
if (feedUris.length > 0) { if (pinnedFeeds.length > 0) {
feedsPromise = getAgent() feedsPromise = getAgent()
.app.bsky.feed.getFeedGenerators({ .app.bsky.feed.getFeedGenerators({
feeds: feedUris, feeds: pinnedFeeds.map(f => f.value),
}) })
.then(res => { .then(res => {
for (let feedView of res.data.feeds) { for (let i = 0; i < res.data.feeds.length; i++) {
resolved.set(feedView.uri, hydrateFeedGenerator(feedView)) const feedView = res.data.feeds[i]
resolved.set(
feedView.uri + pinnedFeedsIds[i],
hydrateFeedGenerator(feedView, {
id: pinnedFeedsIds[i],
}),
)
} }
}) })
} }
// Get all lists. This currently has to be done individually. // Get all lists. This currently has to be done individually.
const listsPromises = listUris.map(listUri => const listsPromises = pinnedLists.map(list =>
getAgent() getAgent()
.app.bsky.graph.getList({ .app.bsky.graph.getList({
list: listUri, list: list.value,
limit: 1, limit: 1,
}) })
.then(res => { .then(res => {
const listView = res.data.list const listView = res.data.list
resolved.set(listView.uri, hydrateList(listView)) resolved.set(
listView.uri + list.id,
hydrateList(listView, {id: list.id}),
)
}), }),
) )
@@ -277,15 +292,16 @@ export function usePinnedFeedsInfos() {
await Promise.allSettled([feedsPromise, ...listsPromises]) await Promise.allSettled([feedsPromise, ...listsPromises])
// order the feeds/lists in the order they were pinned // order the feeds/lists in the order they were pinned
for (let pinnedFeed of pinnedFeeds) { for (let pinnedItem of pinnedItems) {
const feedInfo = resolved.get(pinnedFeed.value) const feedInfo = resolved.get(pinnedItem.value + pinnedItem.id)
if (feedInfo) { if (feedInfo) {
result.push(feedInfo) result.push(feedInfo)
} else if (pinnedFeed.type === 'timeline') { } else if (pinnedItem.type === 'timeline') {
result.push({ result.push({
id: pinnedItem.id,
type: 'feed', type: 'feed',
displayName: 'Following', displayName: 'Following',
uri: pinnedFeed.value, uri: pinnedItem.value,
route: { route: {
href: '/', href: '/',
name: 'Home', name: 'Home',
+2 -2
View File
@@ -256,7 +256,7 @@ export function FeedsScreen(_props: Props) {
return f.pinned return f.pinned
}) })
.map(feed => ({ .map(feed => ({
key: `savedFeed:${feed.value}`, key: `savedFeed:${feed.value}:${feed.id}`,
type: 'savedFeed', type: 'savedFeed',
feedUri: feed.value, feedUri: feed.value,
savedFeedConfig: feed, savedFeedConfig: feed,
@@ -268,7 +268,7 @@ export function FeedsScreen(_props: Props) {
return !f.pinned return !f.pinned
}) })
.map(feed => ({ .map(feed => ({
key: `savedFeed:${feed.value}`, key: `savedFeed:${feed.value}:${feed.id}`,
type: 'savedFeed', type: 'savedFeed',
feedUri: feed.value, feedUri: feed.value,
savedFeedConfig: feed, savedFeedConfig: feed,
+1 -1
View File
@@ -47,7 +47,7 @@ export function DesktopFeeds() {
} }
return ( return (
<FeedItem <FeedItem
key={feed} key={feedInfo.id}
href={'/?' + new URLSearchParams([['feed', feed]])} href={'/?' + new URLSearchParams([['feed', feed]])}
title={feedInfo.displayName} title={feedInfo.displayName}
current={route.name === 'Home' && feed === selectedFeed} current={route.name === 'Home' && feed === selectedFeed}