-
+
+ {APP_VERSION?.[0] === "0" && (
+
+ Early Access
+
+ )}
+
+
+
+
{/* NOTE: tabIndex for main element can get by `document.activeElement` */}
+
+
{isAuthFail && !user && (
}
+
+type IdToIdRecord = Record
+type IdToBooleanRecord = Record
+type IdToAnyObjectRecord = Record>
class ServiceStatic {
- async findAll(
- type: EntryRelatedKey,
- ): Promise> {
+ async findAll(type: EntryRelatedKey.FEED_ID): Promise
+ async findAll(type: EntryRelatedKey.READ): Promise
+ async findAll(type: EntryRelatedKey.COLLECTION): Promise
+
+ async findAll(type: EntryRelatedKey): Promise> {
const data = await entryRelatedModel.table.get(type)
return data ? data.data : {}
}
@@ -24,7 +30,19 @@ class ServiceStatic {
* @param data key is entryId, value is read status
* @returns
*/
- async upsert(type: EntryRelatedKey, data: Record) {
+ async upsert(
+ type: EntryRelatedKey.READ,
+ data: IdToBooleanRecord
+ ): Promise
+ async upsert(
+ type: EntryRelatedKey.FEED_ID,
+ data: IdToIdRecord
+ ): Promise
+ async upsert(
+ type: EntryRelatedKey.COLLECTION,
+ data: IdToAnyObjectRecord
+ ): Promise
+ async upsert(type: any, data: Record) {
const oldData = await this.findAll(type)
return entryRelatedModel.table.put({
@@ -33,11 +51,8 @@ class ServiceStatic {
})
}
- async deleteItem(
- type: EntryRelatedKey,
- key: string,
- ) {
- const oldData = await this.findAll(type)
+ async deleteItem(type: EntryRelatedKey, key: string) {
+ const oldData = await this.findAll(type as any)
delete oldData[key]
return entryRelatedModel.table.put({
diff --git a/src/renderer/src/services/entry.ts b/src/renderer/src/services/entry.ts
index 36d86d365..70cb6fe7d 100644
--- a/src/renderer/src/services/entry.ts
+++ b/src/renderer/src/services/entry.ts
@@ -1,13 +1,10 @@
import { entryModel } from "@renderer/database/models"
import type {
- CombinedEntryModel,
EntryModel,
- FeedModel,
} from "@renderer/models/types"
import { BaseService } from "./base"
import { EntryRelatedKey, EntryRelatedService } from "./entry-related"
-import { FeedService } from "./feed"
type EntryCollection = {
createdAt: string
@@ -17,21 +14,6 @@ class EntryServiceStatic extends BaseService {
super(entryModel.table)
}
- pour(data: CombinedEntryModel[]) {
- const entries = [] as EntryModel[]
- const feeds = [] as FeedModel[]
- for (const entry of data) {
- entries.push(entry.entries)
-
- feeds.push(entry.feeds)
- }
-
- return Promise.all([
- this.upsertMany(entries),
- FeedService.upsertMany(feeds),
- ])
- }
-
bulkStoreReadStatus(record: Record) {
return EntryRelatedService.upsert(EntryRelatedKey.READ, record)
}
diff --git a/src/renderer/src/store/entry/store.ts b/src/renderer/src/store/entry/store.ts
index 57bbac86f..5706de151 100644
--- a/src/renderer/src/store/entry/store.ts
+++ b/src/renderer/src/store/entry/store.ts
@@ -6,8 +6,6 @@ import type {
FeedModel,
} from "@renderer/models"
import { EntryService } from "@renderer/services"
-import type { IFuseOptions } from "fuse.js"
-import Fuse from "fuse.js"
import { produce } from "immer"
import { merge, omit } from "lodash-es"
@@ -293,18 +291,6 @@ class EntryActions {
EntryService.deleteCollection(entryId)
}
}
-
- async createLocalSearch() {
- const data = Object.values(get().flatMapEntries)
-
- const options: IFuseOptions = {
- keys: ["entries.title", "entries.content", "entries.description"],
- }
- const index = Fuse.createIndex(options.keys!, data)
- const fuse = new Fuse(data, options, index)
-
- return fuse
- }
}
export const entryActions = new EntryActions()
diff --git a/src/renderer/src/store/search/constants.ts b/src/renderer/src/store/search/constants.ts
new file mode 100644
index 000000000..386a60b24
--- /dev/null
+++ b/src/renderer/src/store/search/constants.ts
@@ -0,0 +1,12 @@
+const SearchTypeBase = {
+ Feed: 1,
+ Entry: 1 << 1,
+ Subscription: 1 << 2,
+}
+
+export const SearchType = {
+ ...SearchTypeBase,
+ All: Object.values(SearchTypeBase).reduce((acc, cur) => acc | cur, 0),
+}
+
+export type SearchType = typeof SearchType[keyof typeof SearchType]
diff --git a/src/renderer/src/store/search/helper.ts b/src/renderer/src/store/search/helper.ts
new file mode 100644
index 000000000..c2a99ec76
--- /dev/null
+++ b/src/renderer/src/store/search/helper.ts
@@ -0,0 +1,3 @@
+import type { SearchInstance } from "./types"
+
+export const defineSearchInstance = (instance: SearchInstance) => instance
diff --git a/src/renderer/src/store/search/index.ts b/src/renderer/src/store/search/index.ts
new file mode 100644
index 000000000..223557edc
--- /dev/null
+++ b/src/renderer/src/store/search/index.ts
@@ -0,0 +1,118 @@
+import type { EntryModel } from "@renderer/models"
+import {
+ EntryRelatedKey,
+ EntryRelatedService,
+ EntryService,
+ FeedService,
+ SubscriptionService,
+} from "@renderer/services"
+import type { IFuseOptions } from "fuse.js"
+import Fuse from "fuse.js"
+
+import type { SubscriptionPlainModel } from "../subscription"
+import { createZustandStore } from "../utils/helper"
+import { SearchType } from "./constants"
+import { defineSearchInstance } from "./helper"
+import type { SearchResult, SearchState } from "./types"
+
+const createState = (): SearchState => ({
+ feeds: [],
+ entries: [],
+ subscriptions: [],
+ keyword: "",
+ searchType: SearchType.All,
+})
+export const useSearchStore =
+ createZustandStore("search")(createState)
+
+const { getState: get, setState: set } = useSearchStore
+
+class SearchActions {
+ reset() {
+ set(createState)
+ }
+
+ private createFuse(data: T[], keys: (keyof T)[]) {
+ const options: IFuseOptions = {
+ keys: keys as any,
+ }
+ const index = Fuse.createIndex(options.keys!, data)
+ return new Fuse(data, options, index)
+ }
+
+ async createLocalDbSearch() {
+ const [entries, feeds, subscriptions, entryRelated] = await Promise.all([
+ EntryService.findAll(),
+ FeedService.findAll(),
+ SubscriptionService.findAll(),
+ EntryRelatedService.findAll(EntryRelatedKey.FEED_ID),
+ ])
+
+ const entriesFuse = this.createFuse(entries, [
+ "title",
+ "content",
+ "description",
+ ])
+ const feedsFuse = this.createFuse(feeds, ["title", "description"])
+ const subscriptionsFuse = this.createFuse(subscriptions, [
+ "title",
+ "category",
+ ])
+
+ return defineSearchInstance({
+ search(keyword: string) {
+ const type = get().searchType
+ const entries =
+ type & SearchType.Entry ? entriesFuse.search(keyword) : []
+ const feeds = type & SearchType.Feed ? feedsFuse.search(keyword) : []
+
+ const subscriptions =
+ type & SearchType.Subscription ?
+ subscriptionsFuse.search(keyword) :
+ []
+
+ const processedEntries = [] as SearchResult<
+ EntryModel,
+ { feedId: string }
+ >[]
+ for (const entry of entries) {
+ const feedId = entryRelated[entry.item.id]
+ if (feedId) {
+ processedEntries.push({ item: entry.item, feedId })
+ }
+ }
+
+ const processedSubscriptions = [] as SearchResult<
+ SubscriptionPlainModel,
+ { feedId: string }
+ >[]
+ for (const subscription of subscriptions) {
+ const { feedId } = subscription.item
+ if (feedId) {
+ processedSubscriptions.push({ item: subscription.item, feedId })
+ }
+ }
+
+ set({
+ keyword,
+ entries: processedEntries,
+ feeds,
+ subscriptions: processedSubscriptions,
+ searchType: type,
+ })
+
+ return get()
+ },
+ })
+ }
+
+ setSearchType(type: SearchType) {
+ set({ searchType: type })
+ }
+
+ getCurrentKeyword() {
+ return get().keyword
+ }
+}
+
+export const searchActions = new SearchActions()
diff --git a/src/renderer/src/store/search/types.ts b/src/renderer/src/store/search/types.ts
new file mode 100644
index 000000000..93ff7b00f
--- /dev/null
+++ b/src/renderer/src/store/search/types.ts
@@ -0,0 +1,22 @@
+import type { EntryModel, FeedModel } from "@renderer/models"
+
+import type { SubscriptionPlainModel } from "../subscription"
+import type { SearchType } from "./constants"
+
+// @ts-expect-error
+export interface SearchResult
+ extends A {
+ item: T
+}
+
+export interface SearchState {
+ feeds: SearchResult[]
+ entries: SearchResult[]
+ subscriptions: SearchResult[]
+
+ keyword: string
+ searchType: SearchType
+}
+export interface SearchInstance {
+ search: (keyword: string) => SearchState
+}
diff --git a/src/renderer/src/store/unread.ts b/src/renderer/src/store/unread/index.ts
similarity index 97%
rename from src/renderer/src/store/unread.ts
rename to src/renderer/src/store/unread/index.ts
index dbbc5f994..5f37530dd 100644
--- a/src/renderer/src/store/unread.ts
+++ b/src/renderer/src/store/unread/index.ts
@@ -2,7 +2,7 @@ import { apiClient } from "@renderer/lib/api-fetch"
import type { FeedViewType } from "@renderer/lib/enum"
import { FeedUnreadService } from "@renderer/services"
-import { createZustandStore } from "./utils/helper"
+import { createZustandStore } from "../utils/helper"
interface UnreadState {
data: Record