Prevent duplicate keys
This commit is contained in:
+36
-20
@@ -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',
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user