feat: seprate listIds and inboxIds with feedIds in subscription store

This commit is contained in:
DIYgod 2025-02-10 17:09:41 +08:00
parent b7fce0e640
commit 5f2cf8f20e
No known key found for this signature in database
5 changed files with 50 additions and 95 deletions

View File

@ -11,18 +11,10 @@ export const TimelineTabs = () => {
const { view, listId, inboxId, folderName } = routerParams
const listsData = useSubscriptionStore(
useCallback(
(state) =>
state.feedIdByView[view].map((id) => state.data[id]).filter((s) => s && "listId" in s),
[view],
),
useCallback((state) => Array.from(state.listIds).map((id) => state.data[id]), []),
)
const inboxData = useSubscriptionStore(
useCallback(
(state) =>
state.feedIdByView[view].map((id) => state.data[id]).filter((s) => s && "inboxId" in s),
[view],
),
useCallback((state) => Array.from(state.inboxIds).map((id) => state.data[id]), []),
)
const categoriesData = useSubscriptionStore(
useCallback(

View File

@ -25,7 +25,7 @@ import {
useInboxesGroupedData,
useListsGroupedData,
} from "./list.shared"
import { SortableFeedList, SortByAlphabeticalInbox, SortByAlphabeticalList } from "./sort-by"
import { SortableFeedList } from "./sort-by"
const FeedListImpl = forwardRef<HTMLDivElement, { className?: string; view: number }>(
({ className, view }, ref) => {
@ -214,23 +214,6 @@ const FeedListImpl = forwardRef<HTMLDivElement, { className?: string; view: numb
rootClassName={cn("h-full", shouldFreeUpSpace && "overflow-visible")}
>
<StarredItem view={view} />
{hasListData && (
<>
<div className="mt-1 flex h-6 w-full shrink-0 items-center rounded-md px-2.5 text-xs font-semibold text-theme-vibrancyFg transition-colors">
{t("words.lists")}
</div>
<SortByAlphabeticalList view={view} data={listsData} />
</>
)}
{hasInboxData && (
<>
<div className="mt-1 flex h-6 w-full shrink-0 items-center rounded-md px-2.5 text-xs font-semibold text-theme-vibrancyFg transition-colors">
{t("words.inbox")}
</div>
<SortByAlphabeticalInbox view={view} data={inboxesData} />
</>
)}
<DraggableContext.Provider value={draggableContextValue}>
<div className="space-y-px" id="feeds-area" ref={setNodeRef}>
{(hasListData || hasInboxData) && (

View File

@ -14,7 +14,6 @@ import {
subscriptionCategoryExistSelector,
subscriptionsByFeedIsdSelector,
} from "./selector"
import type { SubscriptionFlatModel } from "./store"
import { useSubscriptionStore } from "./store"
type FeedId = string
@ -88,13 +87,14 @@ export const useAllFeeds = () => {
(store) => {
const feedInfo = [] as { title: string; id: string }[]
const allSubscriptions = Object.values(store.feedIdByView).flat()
const allSubscriptions = Object.values(store.data).filter(
(subscription) => !subscription.listId && !subscription.inboxId,
)
for (const feedId of allSubscriptions) {
const subscription = store.data[feedId]!
const feed = feedTitleMap[feedId]
for (const subscription of allSubscriptions) {
const feed = feedTitleMap[subscription.feedId]
if (feed) {
feedInfo.push({ title: subscription.title || feed || "", id: feedId })
feedInfo.push({ title: subscription.title || feed || "", id: subscription.feedId })
}
}
return feedInfo
@ -106,36 +106,18 @@ export const useAllFeeds = () => {
export const useAllLists = () => {
return useSubscriptionStore(
useCallback((store) => {
const lists = [] as SubscriptionFlatModel[]
const allSubscriptions = Object.values(store.feedIdByView).flat()
for (const feedId of allSubscriptions) {
const subscription = store.data[feedId]!
if (subscription.listId) {
lists.push(subscription)
}
}
return lists
}, []),
useCallback(
(store) => Object.values(store.data).filter((subscription) => subscription.listId),
[],
),
)
}
export const useAllInboxes = () => {
return useSubscriptionStore(
useCallback((store) => {
const inboxes = [] as SubscriptionFlatModel[]
const allSubscriptions = Object.values(store.feedIdByView).flat()
for (const feedId of allSubscriptions) {
const subscription = store.data[feedId]!
if (subscription.inboxId) {
inboxes.push(subscription)
}
}
return inboxes
}, []),
useCallback(
(store) => Object.values(store.data).filter((subscription) => subscription.inboxId),
[],
),
)
}

View File

@ -1,4 +1,4 @@
import { FeedViewType } from "@follow/constants"
import type { FeedViewType } from "@follow/constants"
import { FEED_COLLECTION_LIST, ROUTE_FEED_IN_FOLDER } from "~/constants"
@ -10,10 +10,7 @@ export const subscriptionCategoryExistSelector = (name: string) => (state: State
state.categories.has(name)
export const feedSubscriptionCountSelector = (state: State) =>
Object.values(state.data).filter(
// FIXME: Backend data compatibility
(s) => !!s.feedId && !s.listId && !s.inboxId && state.subscriptionIdSet.has(s.feedId),
).length
state.listIds.size + state.inboxIds.size + Object.values(state.feedIdByView).flat().length
export const feedIdByViewSelector = (view: FeedViewType) => (state: State) =>
state.feedIdByView[view]
@ -54,11 +51,6 @@ export const folderFeedsByFeedIdSelector =
return feedIds
}
export const listSubscriptionCountSelector = (state: State) =>
Object.values(state.data).filter((s) => !!s.listId && state.subscriptionIdSet.has(s.listId))
.length
export const listSubscriptionCountSelector = (state: State) => state.listIds.size
export const inboxSubscriptionCountSelector = (state: State) =>
Object.values(state.data).filter(
(s) => !!s.inboxId && state.feedIdByView[FeedViewType.Articles].includes(s.inboxId),
).length
export const inboxSubscriptionCountSelector = (state: State) => state.inboxIds.size

View File

@ -52,9 +52,13 @@ interface SubscriptionState {
*/
categories: Set<string>
/**
* All subscription ids set
* All list ids set
*/
subscriptionIdSet: Set<string>
listIds: Set<string>
/**
* All inbox ids set
*/
inboxIds: Set<string>
}
function morphResponseData(data: SubscriptionModel[]): SubscriptionFlatModel[] {
@ -102,7 +106,8 @@ export const useSubscriptionStore = createZustandStore<SubscriptionState>("subsc
feedIdByView: { ...emptyDataIdByView },
categoryOpenStateByView: { ...emptyCategoryOpenStateByView },
categories: new Set(),
subscriptionIdSet: new Set(),
listIds: new Set(),
inboxIds: new Set(),
}))
const set = useSubscriptionStore.setState
@ -118,18 +123,6 @@ class SubscriptionActions {
constructor() {
if (subscribeOnce) return
subscribeOnce = true
// autorun
useSubscriptionStore.subscribe((next, prev) => {
if (next.feedIdByView !== prev.feedIdByView) {
const allSubscriptionIds = Object.values(next.feedIdByView).flat()
set((state) => {
return {
...state,
subscriptionIdSet: new Set(allSubscriptionIds),
}
})
}
})
useSubscriptionStore.subscribe((state, prev) => {
if (state.data === prev.data) return
@ -199,12 +192,18 @@ class SubscriptionActions {
})
immerSet((state) => {
subscriptions.forEach((subscription) => {
if (subscription.listId) {
state.listIds.add(subscription.listId)
} else if (subscription.inboxId) {
state.inboxIds.add(subscription.inboxId)
} else {
state.feedIdByView[subscription.view].push(subscription.feedId)
}
state.data[subscription.feedId] = omit(subscription, [
"feeds",
"lists",
"inboxes",
]) as SubscriptionFlatModel
state.feedIdByView[subscription.view].push(subscription.feedId)
})
})
}
@ -398,6 +397,7 @@ class SubscriptionActions {
await tx.run()
}
// TODO
async unfollow(feedIds: string[]) {
// const feed = getFeedById(feedId)
const feeds = feedIds.map((feedId) => getFeedById(feedId))
@ -422,15 +422,21 @@ class SubscriptionActions {
delete draft.data[feedId]
for (const view in draft.feedIdByView) {
const currentViewFeedIds = draft.feedIdByView[view] as string[]
if (subscription.listId) {
draft.listIds.delete(subscription.listId)
} else if (subscription.inboxId) {
draft.inboxIds.delete(subscription.inboxId)
} else {
for (const view in draft.feedIdByView) {
const currentViewFeedIds = draft.feedIdByView[view] as string[]
const idx = currentViewFeedIds.indexOf(feedId)
const idx = currentViewFeedIds.indexOf(feedId)
if (idx !== -1) {
currentViewFeedIds.splice(idx, 1)
ctx.viewDeleted[feedId] = ctx.viewDeleted[feedId] || []
ctx.viewDeleted[feedId].push(Number.parseInt(view))
if (idx !== -1) {
currentViewFeedIds.splice(idx, 1)
ctx.viewDeleted[feedId] = ctx.viewDeleted[feedId] || []
ctx.viewDeleted[feedId].push(Number.parseInt(view))
}
}
}
}