Skip to content

Commit f85f2f5

Browse files
authored
feat: allow hide private subscription in view timeline (#3773)
1 parent 2ba2948 commit f85f2f5

File tree

21 files changed

+181
-31
lines changed

21 files changed

+181
-31
lines changed

apps/desktop/layer/renderer/src/lib/defineQuery.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ export type DefinedQuery<TQueryKey extends QueryKey, TData> = Readonly<{
1212
cancel: (key?: (key: TQueryKey) => QueryKey) => Promise<void>
1313
remove: (key?: (key: TQueryKey) => QueryKey) => Promise<void>
1414

15-
invalidate: (key?: (key: TQueryKey) => QueryKey) => Promise<void>
15+
invalidate: (options?: {
16+
keyExtractor?: (key: TQueryKey) => QueryKey
17+
exact?: boolean
18+
}) => Promise<void>
1619
invalidateRoot: () => void
1720

1821
refetch: () => Promise<TData | undefined>
@@ -107,12 +110,14 @@ export function defineQuery<
107110
const queryKey = typeof keyExtactor === "function" ? keyExtactor(key) : key
108111
queryClient.removeQueries({ queryKey })
109112
},
110-
invalidate: async (keyExtactor) => {
111-
const queryKey = typeof keyExtactor === "function" ? keyExtactor(key) : key
113+
invalidate: async (args) => {
114+
const { keyExtractor, exact } = args || {}
115+
const queryKey = typeof keyExtractor === "function" ? keyExtractor(key) : key
112116

113117
await queryClient.invalidateQueries({
114118
queryKey,
115119
refetchType: "all",
120+
exact,
116121
})
117122
options?.onInvalidate?.()
118123
},

apps/desktop/layer/renderer/src/modules/discover/FeedForm.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ import { useTranslation } from "react-i18next"
2525
import { toast } from "sonner"
2626
import { z } from "zod"
2727

28+
import { getGeneralSettings } from "~/atoms/settings/general"
2829
import { Autocomplete } from "~/components/ui/auto-completion"
2930
import { useCurrentModal, useIsInModal } from "~/components/ui/modal/stacked/hooks"
3031
import { getRouteParams } from "~/hooks/biz/useRouteParams"
3132
import { useAuthQuery, useI18n } from "~/hooks/common"
3233
import { apiClient } from "~/lib/api-fetch"
3334
import { tipcClient } from "~/lib/client"
3435
import { toastFetchError } from "~/lib/error-parser"
36+
import { entries as entriesQuery } from "~/queries/entries"
3537
import { feed as feedQuery, useFeedQuery } from "~/queries/feed"
3638
import { subscription as subscriptionQuery } from "~/queries/subscriptions"
3739
import { useFeedByIdOrUrl } from "~/store/feed"
@@ -245,6 +247,16 @@ const FeedInnerForm = ({
245247
})
246248
},
247249
onSuccess: (_, variables) => {
250+
if (getGeneralSettings().hidePrivateSubscriptionsInTimeline) {
251+
entriesQuery
252+
.entries({
253+
feedId: "all",
254+
view: Number(variables.view),
255+
excludePrivate: true,
256+
})
257+
.invalidate({ exact: true })
258+
}
259+
248260
if (isSubscribed && variables.view !== `${subscription?.view}`) {
249261
feedUnreadActions.fetchUnreadByView(subscription?.view)
250262
} else {

apps/desktop/layer/renderer/src/modules/discover/ListForm.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ import { useTranslation } from "react-i18next"
2424
import { toast } from "sonner"
2525
import { z } from "zod"
2626

27+
import { getGeneralSettings } from "~/atoms/settings/general"
2728
import { useCurrentModal } from "~/components/ui/modal/stacked/hooks"
2829
import { useI18n } from "~/hooks/common"
2930
import { apiClient } from "~/lib/api-fetch"
3031
import { tipcClient } from "~/lib/client"
3132
import { getFetchErrorMessage, toastFetchError } from "~/lib/error-parser"
3233
import { getNewIssueUrl } from "~/lib/issues"
3334
import { FollowSummary } from "~/modules/feed/feed-summary"
35+
import { entries as entriesQuery } from "~/queries/entries"
3436
import { lists as listsQuery, useList } from "~/queries/lists"
3537
import { subscription as subscriptionQuery } from "~/queries/subscriptions"
3638
import { useListById } from "~/store/list"
@@ -219,6 +221,16 @@ const ListInnerForm = ({
219221
})
220222
},
221223
onSuccess: (_, variables) => {
224+
if (getGeneralSettings().hidePrivateSubscriptionsInTimeline) {
225+
entriesQuery
226+
.entries({
227+
feedId: "all",
228+
view: Number(variables.view),
229+
excludePrivate: true,
230+
})
231+
.invalidate({ exact: true })
232+
}
233+
222234
if (isSubscribed && variables.view !== `${subscription?.view}`) {
223235
feedUnreadActions.fetchUnreadByView(subscription?.view)
224236
} else {

apps/desktop/layer/renderer/src/modules/entry-column/hooks/useEntriesByView.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ const useRemoteEntries = (): UseEntriesReturn => {
5151
const isPreview = useIsPreviewFeed()
5252

5353
const unreadOnly = useGeneralSettingKey("unreadOnly")
54+
const hidePrivateSubscriptionsInTimeline = useGeneralSettingKey(
55+
"hidePrivateSubscriptionsInTimeline",
56+
)
5457

5558
const folderIds = useFolderFeedsByFeedId({
5659
feedId,
@@ -64,14 +67,24 @@ const useRemoteEntries = (): UseEntriesReturn => {
6467
listId,
6568
view,
6669
...(unreadOnly === true && !isPreview && { read: false }),
70+
...(hidePrivateSubscriptionsInTimeline === true && { excludePrivate: true }),
6771
}
6872

6973
if (feedId && listId && isBizId(feedId)) {
7074
delete params.listId
7175
}
7276

7377
return params
74-
}, [feedId, folderIds, inboxId, listId, unreadOnly, view, isPreview])
78+
}, [
79+
feedId,
80+
folderIds,
81+
inboxId,
82+
listId,
83+
unreadOnly,
84+
isPreview,
85+
view,
86+
hidePrivateSubscriptionsInTimeline,
87+
])
7588
const query = useEntries(entriesOptions)
7689

7790
const [fetchedTime, setFetchedTime] = useState<number>()
@@ -134,6 +147,9 @@ const useLocalEntries = (): UseEntriesReturn => {
134147
const { feedId, view, inboxId, listId, isAllFeeds } = useRouteParams()
135148

136149
const unreadOnly = useGeneralSettingKey("unreadOnly")
150+
const hidePrivateSubscriptionsInTimeline = useGeneralSettingKey(
151+
"hidePrivateSubscriptionsInTimeline",
152+
)
137153

138154
const folderIds = useFolderFeedsByFeedId({
139155
feedId,
@@ -145,6 +161,7 @@ const useLocalEntries = (): UseEntriesReturn => {
145161
{
146162
unread: unreadOnly,
147163
view,
164+
excludePrivate: hidePrivateSubscriptionsInTimeline,
148165
},
149166
)
150167

apps/desktop/layer/renderer/src/modules/entry-column/hooks/useMarkAll.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getGeneralSettings } from "~/atoms/settings/general"
12
import { getRouteParams } from "~/hooks/biz/useRouteParams"
23
import { getFolderFeedsByFeedId, subscriptionActions } from "~/store/subscription"
34

@@ -17,7 +18,12 @@ export const markAllByRoute = async (filter?: MarkAllFilter) => {
1718
if (!routerParams) return
1819

1920
if (typeof routerParams.feedId === "number" || routerParams.isAllFeeds) {
20-
subscriptionActions.markReadByView(view, filter)
21+
const { hidePrivateSubscriptionsInTimeline } = getGeneralSettings()
22+
subscriptionActions.markReadByView({
23+
view,
24+
filter,
25+
excludePrivate: hidePrivateSubscriptionsInTimeline,
26+
})
2127
} else if (inboxId) {
2228
subscriptionActions.markReadByFeedIds({
2329
inboxId,

apps/desktop/layer/renderer/src/modules/settings/tabs/general.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,14 @@ export const SettingGeneral = () => {
103103
label: t("general.auto_group.label"),
104104
description: t("general.auto_group.description"),
105105
}),
106-
107106
defineSettingItem("hideAllReadSubscriptions", {
108107
label: t("general.hide_all_read_subscriptions.label"),
109108
description: t("general.hide_all_read_subscriptions.description"),
110109
}),
110+
defineSettingItem("hidePrivateSubscriptionsInTimeline", {
111+
label: t("general.hide_private_subscriptions_in_timeline.label"),
112+
description: t("general.hide_private_subscriptions_in_timeline.description"),
113+
}),
111114

112115
{
113116
type: "title",

apps/desktop/layer/renderer/src/queries/entries.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,27 @@ export const entries = {
1313
listId,
1414
view,
1515
read,
16+
excludePrivate,
1617
limit,
1718
}: {
1819
feedId?: number | string
1920
inboxId?: number | string
2021
listId?: number | string
2122
view?: number
2223
read?: boolean
24+
excludePrivate?: boolean
2325
limit?: number
2426
}) =>
2527
defineQuery(
26-
["entries", inboxId || listId || feedId, view, read, limit],
28+
["entries", inboxId || listId || feedId, view, read, excludePrivate, limit],
2729
async ({ pageParam }) =>
2830
entryActions.fetchEntries({
2931
feedId,
3032
inboxId,
3133
listId,
3234
view,
3335
read,
36+
excludePrivate,
3437
limit,
3538
pageParam: pageParam as string,
3639
}),
@@ -130,30 +133,35 @@ export const useEntries = ({
130133
listId,
131134
view,
132135
read,
136+
excludePrivate,
133137
}: {
134138
feedId?: number | string
135139
inboxId?: number | string
136140
listId?: number | string
137141
view?: number
138142
read?: boolean
143+
excludePrivate?: boolean
139144
}) => {
140145
const fetchUnread = read === false
141146
const feedUnreadDirty = useFeedUnreadIsDirty((feedId as string) || "")
142147

143-
return useAuthInfiniteQuery(entries.entries({ feedId, inboxId, listId, view, read }), {
144-
enabled: feedId !== undefined || inboxId !== undefined || listId !== undefined,
145-
getNextPageParam: (lastPage) => lastPage.data?.at(-1)?.entries.publishedAt,
146-
initialPageParam: undefined,
147-
refetchOnWindowFocus: false,
148-
refetchOnReconnect: false,
149-
// DON'T refetch when the router is pop to previous page
150-
refetchOnMount: fetchUnread && feedUnreadDirty && !history.isPop ? "always" : false,
148+
return useAuthInfiniteQuery(
149+
entries.entries({ feedId, inboxId, listId, view, read, excludePrivate }),
150+
{
151+
enabled: feedId !== undefined || inboxId !== undefined || listId !== undefined,
152+
getNextPageParam: (lastPage) => lastPage.data?.at(-1)?.entries.publishedAt,
153+
initialPageParam: undefined,
154+
refetchOnWindowFocus: false,
155+
refetchOnReconnect: false,
156+
// DON'T refetch when the router is pop to previous page
157+
refetchOnMount: fetchUnread && feedUnreadDirty && !history.isPop ? "always" : false,
151158

152-
staleTime:
153-
// Force refetch unread entries when feed is dirty
154-
// HACK: disable refetch when the router is pop to previous page
155-
history.isPop ? Infinity : fetchUnread && feedUnreadDirty ? 0 : defaultStaleTime,
156-
})
159+
staleTime:
160+
// Force refetch unread entries when feed is dirty
161+
// HACK: disable refetch when the router is pop to previous page
162+
history.isPop ? Infinity : fetchUnread && feedUnreadDirty ? 0 : defaultStaleTime,
163+
},
164+
)
157165
}
158166

159167
export const useEntriesPreview = ({ id }: { id?: string }) =>

apps/desktop/layer/renderer/src/store/entry/hooks.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useCallback } from "react"
55
import { FEED_COLLECTION_LIST, ROUTE_FEED_IN_FOLDER } from "~/constants"
66

77
import { useListsFeedIds } from "../list"
8-
import { useFeedIdByView } from "../subscription"
8+
import { useFeedIdByView, useNonPrivateSubscriptionIds } from "../subscription"
99
import { getEntryIsInView, getFilteredFeedIds } from "./helper"
1010
import { useEntryStore } from "./store"
1111
import type { EntryFilter, FlatEntryModel } from "./types"
@@ -82,12 +82,15 @@ export const useEntryIdsByFeedId = (feedId: string, filter?: EntryFilter) =>
8282

8383
export const useEntryIdsByView = (view: FeedViewType, filter?: EntryFilter) => {
8484
const feedIds = useFeedIdByView(view)
85-
const listFeedIds = useListsFeedIds(feedIds)
85+
const nonPrivateFeedIds = useNonPrivateSubscriptionIds(feedIds)
86+
const finalFeedIds = filter?.excludePrivate ? nonPrivateFeedIds : feedIds
87+
const listFeedIds = useListsFeedIds(finalFeedIds)
8688

8789
return useEntryStore(
8890
useCallback(
89-
() => getFilteredFeedIds(Array.from(new Set([...feedIds, ...listFeedIds])), filter) || [],
90-
[feedIds, listFeedIds, filter],
91+
() =>
92+
getFilteredFeedIds(Array.from(new Set([...finalFeedIds, ...listFeedIds])), filter) || [],
93+
[finalFeedIds, listFeedIds, filter],
9194
),
9295
)
9396
}

apps/desktop/layer/renderer/src/store/entry/store.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class EntryActions {
113113
listId,
114114
view,
115115
read,
116+
excludePrivate,
116117
limit,
117118
pageParam,
118119
}: {
@@ -121,6 +122,7 @@ class EntryActions {
121122
listId?: number | string
122123
view?: number
123124
read?: boolean
125+
excludePrivate?: boolean
124126
limit?: number
125127
pageParam?: string
126128
}) {
@@ -163,6 +165,7 @@ class EntryActions {
163165
publishedAfter: pageParam,
164166
read,
165167
limit,
168+
excludePrivate,
166169
...params,
167170
},
168171
})

apps/desktop/layer/renderer/src/store/entry/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ export interface EntryState {
3131
export interface EntryFilter {
3232
unread?: boolean
3333
view?: FeedViewType
34+
excludePrivate?: boolean
3435
}

0 commit comments

Comments
 (0)