diff --git a/apps/desktop/layer/renderer/package.json b/apps/desktop/layer/renderer/package.json index f3d87cecf..9ecd93221 100644 --- a/apps/desktop/layer/renderer/package.json +++ b/apps/desktop/layer/renderer/package.json @@ -48,7 +48,6 @@ "clsx": "2.1.1", "cmdk": "1.1.1", "dayjs": "1.11.13", - "dexie": "4.0.11", "dnum": "2.15.0", "embla-carousel-react": "8.6.0", "embla-carousel-wheel-gestures": "8.0.2", diff --git a/apps/desktop/layer/renderer/src/database/constants.ts b/apps/desktop/layer/renderer/src/database/constants.ts deleted file mode 100644 index c6ed27cd4..000000000 --- a/apps/desktop/layer/renderer/src/database/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const LOCAL_DB_NAME = "FOLLOW_DB" diff --git a/apps/desktop/layer/renderer/src/database/db.test.ts b/apps/desktop/layer/renderer/src/database/db.test.ts deleted file mode 100644 index 25b8ea1d9..000000000 --- a/apps/desktop/layer/renderer/src/database/db.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { afterEach } from "node:test" - -import { describe, expect, it } from "vitest" - -import { browserDB } from "./db" - -describe("upgradeToV8", () => { - afterEach(async () => { - await browserDB.delete() - }) - - it("should set tipUsers to an empty array if tipUsers is not an array", async () => { - const insertFeeds = [ - { id: 1, tipUsers: {} }, - { id: 2, tipUsers: null }, - { id: 3, tipUsers: [{ name: "user1" }] }, - ] - // @ts-expect-error - await browserDB.feeds.bulkAdd(insertFeeds) - - const feeds = await browserDB.feeds.toArray() - expect(feeds.length).toEqual(3) - expect(feeds[0]!.tipUsers).toEqual(insertFeeds[0]!.tipUsers) - expect(feeds[1]!.tipUsers).toEqual(insertFeeds[1]!.tipUsers) - expect(feeds[2]!.tipUsers).toEqual(insertFeeds[2]!.tipUsers) - - await browserDB.transaction("rw", [browserDB.feeds], async (tx) => { - await browserDB.upgradeToV8(tx) - }) - const feedsAfterMigrate = await browserDB.feeds.toArray() - expect(feedsAfterMigrate.length).toEqual(3) - expect(feedsAfterMigrate[0]!.tipUsers).toEqual([]) - expect(feedsAfterMigrate[1]!.tipUsers).toEqual(null) - expect(feedsAfterMigrate[2]!.tipUsers).toEqual([{ name: "user1" }]) - }) -}) diff --git a/apps/desktop/layer/renderer/src/database/db.ts b/apps/desktop/layer/renderer/src/database/db.ts deleted file mode 100644 index 6e62fb12c..000000000 --- a/apps/desktop/layer/renderer/src/database/db.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { Transaction } from "dexie" -import Dexie from "dexie" - -import { LOCAL_DB_NAME } from "./constants" -import { - dbSchemaV1, - dbSchemaV2, - dbSchemaV3, - dbSchemaV4, - dbSchemaV5, - dbSchemaV6, - dbSchemaV7, - dbSchemaV8, -} from "./db_schema" -import type { DB_Cleaner } from "./schemas/cleaner" -import type { DB_Entry, DB_EntryRelated } from "./schemas/entry" -import type { DB_Feed, DB_FeedUnread } from "./schemas/feed" -import type { DB_Inbox } from "./schemas/inbox" -import type { DB_List } from "./schemas/list" -import type { DB_Subscription } from "./schemas/subscription" - -export interface LocalDBSchemaMap { - entries: DB_Entry - feeds: DB_Feed - subscriptions: DB_Subscription - entryRelated: DB_EntryRelated - feedUnreads: DB_FeedUnread - cleaner: DB_Cleaner - lists: DB_List - inboxes: DB_Inbox -} - -// Define a local DB -class BrowserDB extends Dexie { - public entries: BrowserDBTable<"entries"> - public feeds: BrowserDBTable<"feeds"> - public subscriptions: BrowserDBTable<"subscriptions"> - public entryRelated: BrowserDBTable<"entryRelated"> - public feedUnreads: BrowserDBTable<"feedUnreads"> - public lists: BrowserDBTable<"lists"> - public inboxes: BrowserDBTable<"inboxes"> - public cleaner: BrowserDBTable<"cleaner"> - - constructor() { - super(LOCAL_DB_NAME) - this.version(1).stores(dbSchemaV1) - this.version(2).stores(dbSchemaV2).upgrade(this.upgradeToV2) - this.version(3).stores(dbSchemaV3) - this.version(4).stores(dbSchemaV4) - this.version(5).stores(dbSchemaV5) - this.version(6).stores(dbSchemaV6) - this.version(7).stores(dbSchemaV7) - this.version(8).stores(dbSchemaV8).upgrade(this.upgradeToV8) - - this.entries = this.table("entries") - this.feeds = this.table("feeds") - this.subscriptions = this.table("subscriptions") - this.entryRelated = this.table("entryRelated") - this.feedUnreads = this.table("feedUnreads") - this.cleaner = this.table("cleaner") - this.lists = this.table("lists") - this.inboxes = this.table("inboxes") - } - - async upgradeToV2(trans: Transaction) { - const session = trans.table("feedUnreads") - session.delete("feedId") - } - - async upgradeToV8(trans: Transaction) { - // Fix https://github.com/RSSNext/Follow/issues/1308 - const session = trans.table("feeds") - return session.toCollection().modify((feed) => { - if (!feed.tipUsers || Array.isArray(feed.tipUsers)) return - feed.tipUsers = [] - }) - } -} - -export const browserDB = new BrowserDB() - -// ================================================ // -// ================================================ // -// ================================================ // -// ================================================ // -// ================================================ // - -// types helper -export type BrowserDBSchema = { - [t in keyof LocalDBSchemaMap]: { - model: LocalDBSchemaMap[t] - table: Dexie.Table - } -} -type BrowserDBTable = BrowserDBSchema[T]["table"] diff --git a/apps/desktop/layer/renderer/src/database/db_schema.ts b/apps/desktop/layer/renderer/src/database/db_schema.ts deleted file mode 100644 index 49ff4cc50..000000000 --- a/apps/desktop/layer/renderer/src/database/db_schema.ts +++ /dev/null @@ -1,42 +0,0 @@ -export const dbSchemaV1 = { - entries: "&id", - feeds: "&id", - subscriptions: "&id", - entryRelated: "&id", - feedUnreads: "&id", -} - -export const dbSchemaV2 = { - ...dbSchemaV1, - subscriptions: "&id, userId, feedId", -} - -export const dbSchemaV3 = { - ...dbSchemaV2, - feedEntries: null, - - subscriptions: "&id, userId, &feedId", -} - -export const dbSchemaV4 = { - ...dbSchemaV3, - entries: "&id, feedId", - subscriptions: "&id, userId, feedId", -} - -export const dbSchemaV5 = { - ...dbSchemaV4, - cleaner: "&refId, visitedAt", -} - -export const dbSchemaV6 = { - ...dbSchemaV5, - lists: "&id, title", -} - -export const dbSchemaV7 = { - ...dbSchemaV6, - inboxes: "&id", -} - -export const dbSchemaV8 = dbSchemaV7 diff --git a/apps/desktop/layer/renderer/src/database/global.d.ts b/apps/desktop/layer/renderer/src/database/global.d.ts deleted file mode 100644 index ade209fd0..000000000 --- a/apps/desktop/layer/renderer/src/database/global.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -declare global { - // This flag controls write data in indexedDB, if it's false, pass data insert to db - // When app not ready, it's false, after hydrate data, it's true - // Or set is false when disable indexedDB in setting - - export let __dbIsReady: boolean - - interface Window { - __dbIsReady: boolean - } -} - -export {} diff --git a/apps/desktop/layer/renderer/src/database/index.ts b/apps/desktop/layer/renderer/src/database/index.ts deleted file mode 100644 index f6ec508ce..000000000 --- a/apps/desktop/layer/renderer/src/database/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { browserDB } from "./db" - -export * from "./db" -export * from "./schemas" - -export const DB_NOT_READY_OR_DISABLED = "Database is not ready or disabled" -/** - * @description Check if database is ready - * If users disabled data persist, it's always false, that means you can't do operation with database. - * - */ - -export const runTransactionInScope = ( - fn: (db: typeof browserDB) => T, -): T | typeof DB_NOT_READY_OR_DISABLED => { - if (!window.__dbIsReady) { - // Or, push to waiting queue - return DB_NOT_READY_OR_DISABLED - } - - return fn(browserDB) -} diff --git a/apps/desktop/layer/renderer/src/database/schemas/base.ts b/apps/desktop/layer/renderer/src/database/schemas/base.ts deleted file mode 100644 index 659d41fef..000000000 --- a/apps/desktop/layer/renderer/src/database/schemas/base.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { z } from "zod" - -export const DB_BaseSchema = z.object({ - id: z.string(), -}) - -export type DB_Base = z.infer diff --git a/apps/desktop/layer/renderer/src/database/schemas/cleaner.ts b/apps/desktop/layer/renderer/src/database/schemas/cleaner.ts deleted file mode 100644 index 49aa13596..000000000 --- a/apps/desktop/layer/renderer/src/database/schemas/cleaner.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type CleanerType = "feed" | "entry" | "list" | "inbox" -export type DB_Cleaner = { - refId: string - visitedAt: number - type: CleanerType -} diff --git a/apps/desktop/layer/renderer/src/database/schemas/entry.ts b/apps/desktop/layer/renderer/src/database/schemas/entry.ts deleted file mode 100644 index 52cb37ff4..000000000 --- a/apps/desktop/layer/renderer/src/database/schemas/entry.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { EntryModel } from "@follow/models/types" - -export type DB_Entry = EntryModel & { feedId: string } - -export type DB_EntryRelated = { - id: string - data: any -} diff --git a/apps/desktop/layer/renderer/src/database/schemas/feed.ts b/apps/desktop/layer/renderer/src/database/schemas/feed.ts deleted file mode 100644 index 4afdec175..000000000 --- a/apps/desktop/layer/renderer/src/database/schemas/feed.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { FeedModel } from "@follow/models/types" - -export type DB_FeedUnread = { - id: string - count: number -} - -export type DB_Feed = FeedModel & { id: string } diff --git a/apps/desktop/layer/renderer/src/database/schemas/inbox.ts b/apps/desktop/layer/renderer/src/database/schemas/inbox.ts deleted file mode 100644 index bf51acce1..000000000 --- a/apps/desktop/layer/renderer/src/database/schemas/inbox.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type DB_Inbox = { - id: string - title: string -} diff --git a/apps/desktop/layer/renderer/src/database/schemas/index.ts b/apps/desktop/layer/renderer/src/database/schemas/index.ts deleted file mode 100644 index 3fd6fdbf2..000000000 --- a/apps/desktop/layer/renderer/src/database/schemas/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./base" -export * from "./feed" diff --git a/apps/desktop/layer/renderer/src/database/schemas/list.ts b/apps/desktop/layer/renderer/src/database/schemas/list.ts deleted file mode 100644 index d489fcadb..000000000 --- a/apps/desktop/layer/renderer/src/database/schemas/list.ts +++ /dev/null @@ -1,12 +0,0 @@ -export type DB_List = { - id: string - title: string - createdAt: number - updatedAt: number - description: string - fee: number - image: string - ownerUserId: string - timelineUpdatedAt: string - feedIds: string[] -} diff --git a/apps/desktop/layer/renderer/src/database/schemas/subscription.ts b/apps/desktop/layer/renderer/src/database/schemas/subscription.ts deleted file mode 100644 index 61d60cd1a..000000000 --- a/apps/desktop/layer/renderer/src/database/schemas/subscription.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type DB_Subscription = any & { - id: string -} diff --git a/apps/desktop/layer/renderer/src/initialize/hydrate.ts b/apps/desktop/layer/renderer/src/initialize/hydrate.ts index b7b3fc88e..5e3a150c4 100644 --- a/apps/desktop/layer/renderer/src/initialize/hydrate.ts +++ b/apps/desktop/layer/renderer/src/initialize/hydrate.ts @@ -1,44 +1,6 @@ -import { sleep } from "@follow/utils/utils" - import { initializeDefaultGeneralSettings } from "~/atoms/settings/general" import { initializeDefaultIntegrationSettings } from "~/atoms/settings/integration" import { initializeDefaultUISettings } from "~/atoms/settings/ui" -import { appLog } from "~/lib/log" -import { EntryService, FeedService, SubscriptionService, UnreadService } from "~/services" -import { InboxService } from "~/services/inbox" -import type { Hydratable } from "~/services/interface" -import { ListService } from "~/services/list" - -export const setHydrated = (v: boolean) => { - window.__dbIsReady = v -} - -export const hydrateDatabaseToStore = async () => { - async function hydrate() { - const now = Date.now() - - const hydrates: Hydratable[] = [ - FeedService, - SubscriptionService, - UnreadService, - EntryService, - ListService, - InboxService, - ] - await Promise.all(hydrates.map((h) => h.hydrate())) - - window.__dbIsReady = true - const costTime = Date.now() - now - - return costTime - } - return Promise.race([hydrate(), sleep(1000).then(() => 10e10)]).then((result) => { - if (result === 10e10) { - appLog("Hydrate data timeout") - } - return result - }) -} export const hydrateSettings = () => { initializeDefaultUISettings() diff --git a/apps/desktop/layer/renderer/src/initialize/index.ts b/apps/desktop/layer/renderer/src/initialize/index.ts index 72bddc045..51deb7b7f 100644 --- a/apps/desktop/layer/renderer/src/initialize/index.ts +++ b/apps/desktop/layer/renderer/src/initialize/index.ts @@ -6,32 +6,19 @@ import { tracker } from "@follow/tracker" import { repository } from "@pkg" import { enableMapSet } from "immer" -import { browserDB } from "~/database" import { initI18n } from "~/i18n" import { settingSyncQueue } from "~/modules/settings/helper/sync-queue" import { ElectronCloseEvent, ElectronShowEvent } from "~/providers/invalidate-query-provider" -import { CleanerService } from "~/services/cleaner" import { subscribeNetworkStatus } from "../atoms/network" -import { getGeneralSettings, subscribeShouldUseIndexedDB } from "../atoms/settings/general" +import { getGeneralSettings } from "../atoms/settings/general" import { appLog } from "../lib/log" import { initAnalytics } from "./analytics" import { registerHistoryStack } from "./history" -import { hydrateSettings, setHydrated } from "./hydrate" +import { hydrateSettings } from "./hydrate" import { doMigration } from "./migrates" import { initSentry } from "./sentry" -const cleanup = subscribeShouldUseIndexedDB((value) => { - if (!value) { - browserDB.tables.forEach((table) => { - table.clear() - }) - setHydrated(false) - return - } - setHydrated(true) -}) - declare global { interface Window { version: string @@ -109,8 +96,6 @@ export const initializeApp = async () => { migrateDatabase: true, }) }) - - CleanerService.cleanOutdatedData() } await apm("initAnalytics", initAnalytics) @@ -128,8 +113,6 @@ export const initializeApp = async () => { }) } -import.meta.hot?.dispose(cleanup) - const apm = async (label: string, fn: () => Promise | any) => { const start = Date.now() const result = await fn() diff --git a/apps/desktop/layer/renderer/src/providers/user-provider.tsx b/apps/desktop/layer/renderer/src/providers/user-provider.tsx index a960b4b80..4908db84e 100644 --- a/apps/desktop/layer/renderer/src/providers/user-provider.tsx +++ b/apps/desktop/layer/renderer/src/providers/user-provider.tsx @@ -4,7 +4,6 @@ import { useEffect } from "react" import { setUserRole, setWhoami } from "~/atoms/user" import { setIntegrationIdentify } from "~/initialize/helper" import { useSession } from "~/queries/auth" -import { CleanerService } from "~/services/cleaner" export const UserProvider = () => { const { session } = useSession() @@ -15,9 +14,8 @@ export const UserProvider = () => { if (session.role) { setUserRole(session.role as UserRole) } + // @ts-expect-error FIXME setIntegrationIdentify(session.user) - - CleanerService.cleanRemainingData(session.user.id) }, [session?.role, session?.user]) return null diff --git a/apps/desktop/layer/renderer/src/services/__mock__data__/entries.json b/apps/desktop/layer/renderer/src/services/__mock__data__/entries.json deleted file mode 100644 index 472d5922d..000000000 --- a/apps/desktop/layer/renderer/src/services/__mock__data__/entries.json +++ /dev/null @@ -1,228 +0,0 @@ -[ - { - "read": true, - "view": 0, - "entries": { - "id": "46877798792098816", - "title": "DWMBlurGlass – 为 Windows 10/11 启用窗口透明/玻璃/模糊效果", - "url": "https://www.appinn.com/dwmblurglass/", - "description": "DWMBlurGlass 是一款为 Win10、Win11 全局系统标题栏添加自定义效果的开源工具,主要有模糊、混合颜色、文本颜色、Aero 效果等。@Appinn 来自发现频道,@suntrise 同学的推荐:https://meta.appinn.net/t/topic/59864\n\nDWMBlurGlass:为 Windows 10/11 启用窗口透明/玻璃/模糊效果\n\n今年,又有一款能让窗口标题栏拥有透明效果的应用 DWMBlurGlass 出现,不仅支持 Win10 2004+ 和 Win11,而且还是免费开源的。…", - "guid": "https://www.appinn.com/?p=48489", - "author": "青小蛙", - "authorUrl": null, - "authorAvatar": null, - "insertedAt": "2024-08-15T11:59:12.267Z", - "publishedAt": "2024-08-15T11:34:12.839Z", - "media": [ - { - "url": "https://www.appinn.com/wp-content/uploads/2024/08/Appinn-feature-images-2024-08-15T185350.869.jpg", - "type": "photo", - "width": 1608, - "height": 700 - }, - { - "url": "https://www.appinn.com/wp-content/uploads/2024/08/001701-1.avif", - "type": "photo" - }, - { - "url": "https://www.appinn.com/wp-content/uploads/2024/08/013521.avif", - "type": "photo" - }, - { - "url": "https://www.appinn.com/wp-content/uploads/2024/08/e3c13f90a0c5d7ca478d7.avif", - "type": "photo" - } - ], - "categories": ["Windows", "美化主题", "模糊", "玻璃", "透明"], - "attachments": null - }, - "feeds": { - "id": "41370691926515712", - "url": "https://www.appinn.com/feed/", - "title": "小众软件", - "description": "分享免费、小巧、实用、有趣、绿色的软件", - "siteUrl": "https://www.appinn.com/", - "image": null, - "checkedAt": "2024-08-15T11:59:12.267Z", - "lastModifiedHeader": "Thu, 15 Aug 2024 11:34:18 GMT", - "etagHeader": "W/\"74dc42ccaecdc9834948c29b13261d87\"", - "ttl": 30, - "errorMessage": null, - "errorAt": null, - "ownerUserId": null - }, - "collections": null, - "settings": {} - }, - { - "read": true, - "view": 0, - "entries": { - "id": "46871301655897088", - "title": "开放麒麟 openKylin 新增 LTS 长期支持版,采用“创新版本 + LTS 版本”双轨并行策略", - "url": "https://www.ithome.com/0/788/779.htm", - "description": "IT 之家 8 月 15 日消息,开放原子开源基金会上周刚刚发布了最新的 openKylin 2.0 版本!该版本汇聚了超过 6500 + 开发者的智慧与汗水,并得到了 110+ SIG 和 520 + 企业的鼎力支持。经过社区投票决议,openKylin 今天宣布将推出长期支持版本(LTS),未来,社区将采用“创新版本 + LTS 版本”双轨并行策略:\n\n创新版本:每年发布 1 个版本,提供 1 年被动更新支持\n\nLTS 版本:每 3 年发布 1 个版本,提供 1 年主动更新支持 + 1 年被动更新支持\n\nopenKylin 介绍称,LTS…", - "guid": "https://www.ithome.com/0/788/779.htm", - "author": null, - "authorUrl": null, - "authorAvatar": null, - "insertedAt": "2024-08-15T11:33:04.823Z", - "publishedAt": "2024-08-15T11:11:47.914Z", - "media": [ - { - "url": "https://img.ithome.com/newsuploadfiles/2024/8/1145ab09-45b1-4af4-a6e4-7bbec2548cfe.png?x-bce-process=image/format,f_auto", - "type": "photo", - "width": 1080, - "height": 448 - } - ], - "categories": null, - "attachments": null - }, - "feeds": { - "id": "41397633064960000", - "url": "https://www.ithome.com/rss/", - "title": "IT 之家", - "description": "IT 之家 - 软媒旗下网站", - "siteUrl": "https://www.ithome.com/", - "image": null, - "checkedAt": "2024-08-15T11:33:04.823Z", - "lastModifiedHeader": null, - "etagHeader": null, - "ttl": 30, - "errorMessage": null, - "errorAt": null, - "ownerUserId": null - }, - "collections": null, - "settings": {} - }, - { - "read": true, - "view": 0, - "entries": { - "id": "46871301655897089", - "title": "微星海外推出 2024 款 Pro DP21 系列迷你主机:升级 14 代酷睿处理器、自带“古老”COM 串口", - "url": "https://www.ithome.com/0/788/778.htm", - "description": "IT 之家 8 月 15 日消息,据微星官网,微星今天在海外推出 2024 款 Pro DP21 系列迷你主机,该迷你主机主要升级 14 代酷睿处理器,适用于商务应用场景,截至 IT 之家发稿,官网还未显示具体售价信息。外观方面,这款迷你主机尺寸为 204 x 208 x 54.8mm,重 1.52 千克,整体有点类似 CD 光驱,正面采用格栅式设计,侧面和顶部配备进气孔,支持通过 VESA 支架壁挂。\n\n规格方面,该机基于英特尔 Q670 主板,可选 20 核心 28 线程酷睿 i7-14700 或 14 核心 20 线程酷睿 i5-14500 处理器,至高可选…", - "guid": "https://www.ithome.com/0/788/778.htm", - "author": null, - "authorUrl": null, - "authorAvatar": null, - "insertedAt": "2024-08-15T11:33:04.823Z", - "publishedAt": "2024-08-15T11:06:25.894Z", - "media": [ - { - "url": "https://img.ithome.com/newsuploadfiles/2024/8/dc1110c9-483a-4b6d-84cc-b229ec92a7b4.png", - "type": "photo", - "width": 1440, - "height": 668 - }, - { - "url": "https://img.ithome.com/newsuploadfiles/2024/8/df476b81-144a-4fbd-b0bb-6d81c9734b76.png", - "type": "photo", - "width": 1210, - "height": 862 - }, - { - "url": "https://img.ithome.com/newsuploadfiles/2024/8/568aaf73-5501-4562-9f11-ab77d654c733.png", - "type": "photo", - "width": 1440, - "height": 611 - }, - { - "url": "https://img.ithome.com/newsuploadfiles/2024/8/6120d871-2337-42d3-acb9-f8694362f63a.png", - "type": "photo", - "width": 1440, - "height": 575 - }, - { - "url": "https://img.ithome.com/newsuploadfiles/2024/8/e0dffc94-9746-4fc8-bbde-175a13af4115.png", - "type": "photo", - "width": 1440, - "height": 866 - } - ], - "categories": null, - "attachments": null - }, - "feeds": { - "id": "41397633064960000", - "url": "https://www.ithome.com/rss/", - "title": "IT 之家", - "description": "IT 之家 - 软媒旗下网站", - "siteUrl": "https://www.ithome.com/", - "image": null, - "checkedAt": "2024-08-15T11:33:04.823Z", - "lastModifiedHeader": null, - "etagHeader": null, - "ttl": 30, - "errorMessage": null, - "errorAt": null, - "ownerUserId": null - }, - "collections": null, - "settings": {} - }, - { - "read": true, - "view": 0, - "entries": { - "id": "46871301655897090", - "title": "半年低至 88 元:百度网盘 SVIP 官方预售 0 点开始", - "url": "https://www.ithome.com/0/788/777.htm", - "description": "百度网盘超级会员年卡官方售价 298 元,今日半年卡预告将在 8 月 16 日 0 点开启 88 元预售狂促。约合 14.66 元 / 月、176 元 / 年好价:\n\n天猫百度网盘 超级会员 半年卡填登录手机号 16 日 0 点 88 元直达链接\n\n注:现购买权益时长 + 已有权益时长,不能超过 5 年,否则会充值失败。\n\n百度网盘 SVIP 特权:\n\n天猫百度网盘 超级会员 半年卡填登录手机号 16 日 0 点 88 元直达链接", - "guid": "https://www.ithome.com/0/788/777.htm", - "author": null, - "authorUrl": null, - "authorAvatar": null, - "insertedAt": "2024-08-15T11:33:04.823Z", - "publishedAt": "2024-08-15T11:04:27.697Z", - "media": [ - { - "url": "https://img.alicdn.com/bao/uploaded/i4/2201501708471/O1CN01JbgDBP2CRm3TqFHQs_!!0-item_pic.jpg", - "type": "photo", - "width": 800, - "height": 800 - }, - { - "url": "https://img.alicdn.com/imgextra/i4/2201501708471/O1CN01rpvXMz2CRltEbTc4P_!!2201501708471.jpg", - "type": "photo", - "width": 1500, - "height": 2322 - }, - { - "url": "https://img.alicdn.com/imgextra/i4/2201501708471/O1CN01W0kow62CRlt2y0KYf_!!2201501708471.jpg", - "type": "photo", - "width": 1500, - "height": 762 - }, - { - "url": "https://img.alicdn.com/bao/uploaded/i4/2201501708471/O1CN01JbgDBP2CRm3TqFHQs_!!0-item_pic.jpg", - "type": "photo", - "width": 800, - "height": 800 - } - ], - "categories": null, - "attachments": null - }, - "feeds": { - "id": "41397633064960000", - "url": "https://www.ithome.com/rss/", - "title": "IT 之家", - "description": "IT 之家 - 软媒旗下网站", - "siteUrl": "https://www.ithome.com/", - "image": null, - "checkedAt": "2024-08-15T11:33:04.823Z", - "lastModifiedHeader": null, - "etagHeader": null, - "ttl": 30, - "errorMessage": null, - "errorAt": null, - "ownerUserId": null - }, - "collections": null, - "settings": {} - } -] diff --git a/apps/desktop/layer/renderer/src/services/__snapshots__/cleaner.spec.ts.snap b/apps/desktop/layer/renderer/src/services/__snapshots__/cleaner.spec.ts.snap deleted file mode 100644 index d1f1af8a0..000000000 --- a/apps/desktop/layer/renderer/src/services/__snapshots__/cleaner.spec.ts.snap +++ /dev/null @@ -1,85 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`test db cleaner > data should be set up correctly 1`] = ` -[ - { - "id": "feed-id-1", - }, - { - "id": "feed-id-2", - }, - { - "id": "feed-id-3", - }, - { - "id": "feed-id-4", - }, - { - "id": "feed-id-5", - }, -] -`; - -exports[`test db cleaner > data should be set up correctly 2`] = ` -[ - { - "feedId": "feed-id-1", - "id": "entry-id-1", - }, - { - "feedId": "feed-id-2", - "id": "entry-id-2", - }, - { - "feedId": "feed-id-3", - "id": "entry-id-3", - }, - { - "feedId": "feed-id-4", - "id": "entry-id-4", - }, - { - "feedId": "feed-id-5", - "id": "entry-id-5", - }, - { - "feedId": "feed-id-1", - "id": "entry-id-6", - }, -] -`; - -exports[`test db cleaner > data should be set up correctly 3`] = ` -[ - { - "feedId": "feed-id-1", - "id": "test-user-id-1/feed-id-1", - "userId": "test-user-id-1", - }, - { - "feedId": "feed-id-3", - "id": "test-user-id-1/feed-id-3", - "userId": "test-user-id-1", - }, - { - "feedId": "feed-id-4", - "id": "test-user-id-2/feed-id-4", - "userId": "test-user-id-2", - }, - { - "feedId": "feed-id-5", - "id": "test-user-id-3/feed-id-5", - "userId": "test-user-id-3", - }, - { - "feedId": "feed-id-1", - "id": "test-user-id/feed-id-1", - "userId": "test-user-id", - }, - { - "feedId": "feed-id-2", - "id": "test-user-id/feed-id-2", - "userId": "test-user-id", - }, -] -`; diff --git a/apps/desktop/layer/renderer/src/services/base.ts b/apps/desktop/layer/renderer/src/services/base.ts deleted file mode 100644 index 7a51d1233..000000000 --- a/apps/desktop/layer/renderer/src/services/base.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type Dexie from "dexie" -import type { UpdateSpec } from "dexie" - -export abstract class BaseService { - constructor(public readonly table: Dexie.Table) {} - - async upsert(data: T): Promise { - return this.table.put(data) - } - - async upsertMany(data: T[]) { - return this.table.bulkPut(data) - } - - async findAll() { - return this.table.toArray() - } - - async patch(id: string, data: Partial) { - const oldData = await this.table.get(id) - if (!oldData) return - await this.table.update(id, { - ...oldData, - ...data, - } as unknown as UpdateSpec) - } -} diff --git a/apps/desktop/layer/renderer/src/services/cleaner.spec.ts b/apps/desktop/layer/renderer/src/services/cleaner.spec.ts deleted file mode 100644 index 7ad13f567..000000000 --- a/apps/desktop/layer/renderer/src/services/cleaner.spec.ts +++ /dev/null @@ -1,158 +0,0 @@ -// @ts-nocheck -import type { EntryModel } from "@follow/models/types" -import type { TargetModel } from "@follow/shared/hono" -import { beforeAll, describe, expect, test } from "vitest" - -import { browserDB } from "~/database" -import type { SubscriptionFlatModel } from "~/store/subscription" - -import { CleanerService } from "./cleaner" -import { EntryService } from "./entry" -import { FeedService } from "./feed" -import { SubscriptionService } from "./subscription" - -const currentUserID = "test-user-id" -const otherUserIDs = ["test-user-id-1", "test-user-id-2", "test-user-id-3"] - -const subscriptions: SubscriptionFlatModel[] = [ - { - feedId: "feed-id-1", - userId: currentUserID, - }, - { - feedId: "feed-id-2", - userId: currentUserID, - }, - { - feedId: "feed-id-3", - userId: otherUserIDs[0], - }, - { - feedId: "feed-id-4", - userId: otherUserIDs[1], - }, - { - feedId: "feed-id-5", - userId: otherUserIDs[2], - }, - // ==== - { - feedId: "feed-id-1", - userId: otherUserIDs[0], - }, -] - -const feeds: TargetModel[] = [ - { - id: "feed-id-1", - }, - { - id: "feed-id-2", - }, - { - id: "feed-id-3", - }, - { - id: "feed-id-4", - }, - { - id: "feed-id-5", - }, -] - -const entries: EntryModel[] = [ - { - id: "entry-id-1", - }, - { - id: "entry-id-2", - }, - { - id: "entry-id-3", - }, - { - id: "entry-id-4", - }, - { - id: "entry-id-5", - }, - { - id: "entry-id-6", - }, -] - -const entryFeedIdMap = { - "entry-id-1": "feed-id-1", - "entry-id-2": "feed-id-2", - "entry-id-3": "feed-id-3", - "entry-id-4": "feed-id-4", - "entry-id-5": "feed-id-5", - "entry-id-6": "feed-id-1", -} -describe("test db cleaner", () => { - beforeAll(async () => { - await browserDB.delete() - }) - beforeAll(async () => { - await browserDB.open() - await SubscriptionService.upsertMany(subscriptions) - await FeedService.upsertMany(feeds) - await EntryService.upsertMany(entries, entryFeedIdMap) - }) - - test("data should be set up correctly", async () => { - const feeds = await FeedService.findAll() - const entries = await EntryService.findAll() - const subscriptions = await SubscriptionService.findAll() - expect(feeds).toMatchSnapshot() - expect(entries).toMatchSnapshot() - expect(subscriptions).toMatchSnapshot() - }) - test("should clean remaining data", async () => { - await CleanerService.cleanRemainingData(currentUserID) - - const feeds = await FeedService.findAll() - expect(feeds).toMatchInlineSnapshot(` - [ - { - "id": "feed-id-1", - }, - { - "id": "feed-id-2", - }, - ] - `) - const subscripions = await SubscriptionService.findAll() - expect(subscripions).toMatchInlineSnapshot(` - [ - { - "feedId": "feed-id-1", - "id": "test-user-id/feed-id-1", - "userId": "test-user-id", - }, - { - "feedId": "feed-id-2", - "id": "test-user-id/feed-id-2", - "userId": "test-user-id", - }, - ] - `) - const entries = await EntryService.findAll() - expect(entries).toMatchInlineSnapshot(` - [ - { - "feedId": "feed-id-1", - "id": "entry-id-1", - }, - { - "feedId": "feed-id-2", - "id": "entry-id-2", - }, - { - "feedId": "feed-id-1", - "id": "entry-id-6", - }, - ] - `) - }) -}) diff --git a/apps/desktop/layer/renderer/src/services/cleaner.ts b/apps/desktop/layer/renderer/src/services/cleaner.ts deleted file mode 100644 index 029466eda..000000000 --- a/apps/desktop/layer/renderer/src/services/cleaner.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { browserDB } from "~/database" -import type { CleanerType } from "~/database/schemas/cleaner" -import { appLog } from "~/lib/log" - -import { EntryService } from "./entry" -import { FeedService } from "./feed" -import { UnreadService } from "./feed-unread" -import { InboxService } from "./inbox" -import { ListService } from "./list" -import { SubscriptionService } from "./subscription" - -const cleanerModel = browserDB.cleaner -class CleanerServiceStatic { - // Clean other user subscriptions, should call this after login - async cleanRemainingData(currentUserId: string) { - const dbUserIds = await SubscriptionService.getUserIds() - - const otherUserIds = dbUserIds.filter((id) => id !== currentUserId) - const remainingSubscriptions = await SubscriptionService.getUserSubscriptions(otherUserIds) - const currentSubscription = await SubscriptionService.getUserSubscriptions([currentUserId]) - const currentSubscriptionFeedsSet = new Set(currentSubscription.map((s) => s.feedId)) - // Finds a subscripion that does not exist for the current user, but a feedId that exists in the db - - const toRemoveSubscription = remainingSubscriptions.filter( - (s) => !currentSubscriptionFeedsSet.has(s.feedId), - ) - - const toRemoveFeedIds = toRemoveSubscription.map((s) => s.feedId) - - await Promise.allSettled([ - otherUserIds.map((id) => SubscriptionService.removeSubscription(id)), - FeedService.bulkDelete(toRemoveFeedIds), - UnreadService.bulkDelete(toRemoveFeedIds), - EntryService.deleteEntriesByFeedIds(toRemoveFeedIds), - ]) - } - - /** - * Mark the which data recently used - */ - reset(list: { type: CleanerType; id: string }[]) { - const now = Date.now() - return cleanerModel.bulkPut( - list.map((item) => ({ - refId: item.id, - visitedAt: now, - type: item.type, - })), - ) - } - - /** - * Remove the data that not used for a long time - */ - async cleanOutdatedData() { - const now = Date.now() - const expiredTime = now - 1000 * 60 * 60 * 24 * 30 // 30 days - const data = await cleanerModel.where("visitedAt").below(expiredTime).toArray() - - if (data.length === 0) { - return - } - const deleteEntries = [] as string[] - const deleteFeeds = [] as string[] - const deleteLists = [] as string[] - const deleteInboxes = [] as string[] - for (const item of data) { - switch (item.type) { - case "feed": { - deleteFeeds.push(item.refId) - break - } - case "entry": { - deleteEntries.push(item.refId) - break - } - case "list": { - deleteLists.push(item.refId) - // TODO delete entries related to the list - break - } - case "inbox": { - deleteInboxes.push(item.refId) - break - } - } - } - - appLog("Clean outdated data...", "feeds:", deleteFeeds.length, "entries:", deleteEntries.length) - await Promise.allSettled([ - FeedService.bulkDelete(deleteFeeds), - EntryService.deleteEntries(deleteEntries), - EntryService.deleteEntriesByFeedIds(deleteFeeds), - cleanerModel.bulkDelete(data.map((d) => d.refId)), - ListService.bulkDelete(deleteLists), - InboxService.bulkDelete(deleteInboxes), - ]) - } - - async cleanRefById(refIds: string[]) { - return cleanerModel.bulkDelete(refIds) - } -} -export const CleanerService = new CleanerServiceStatic() diff --git a/apps/desktop/layer/renderer/src/services/entry-related.ts b/apps/desktop/layer/renderer/src/services/entry-related.ts deleted file mode 100644 index f829a16a9..000000000 --- a/apps/desktop/layer/renderer/src/services/entry-related.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { browserDB } from "~/database" - -export enum EntryRelatedKey { - READ = "READ", - /** @deprecated */ - FEED_ID = "FEED_ID", - COLLECTION = "COLLECTION", -} - -const taskQueue = new Map>( - [EntryRelatedKey.READ, EntryRelatedKey.FEED_ID, EntryRelatedKey.COLLECTION].map((key) => [ - key, - Promise.resolve(), - ]), -) - -type IdToIdRecord = Record -type IdToBooleanRecord = Record -type IdToAnyObjectRecord = Record> -const entryRelatedModel = browserDB.entryRelated -class ServiceStatic { - 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.get(type) - return data ? data.data : {} - } - - /** - * - * @param data key is entryId, value is read status - * @returns - */ - 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 getPreviousTask = taskQueue.get(type) || Promise.resolve() - - const task = getPreviousTask.finally(async () => { - const oldData = await this.findAll(type) - - entryRelatedModel.put({ - data: { ...oldData, ...data }, - id: type, - }) - }) - taskQueue.set(type, task) - - return task - } - - async deleteItems(type: EntryRelatedKey, keys: string[]) { - const oldData = await this.findAll(type as any) - keys.forEach((key) => { - delete oldData[key] - }) - - return entryRelatedModel.put({ - data: oldData, - id: type, - }) - } - - async clear() { - return entryRelatedModel.clear() - } -} - -export const EntryRelatedService = new ServiceStatic() diff --git a/apps/desktop/layer/renderer/src/services/entry.ts b/apps/desktop/layer/renderer/src/services/entry.ts deleted file mode 100644 index 3864def10..000000000 --- a/apps/desktop/layer/renderer/src/services/entry.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { EntryModel } from "@follow/models/types" - -import { browserDB } from "~/database" - -import { BaseService } from "./base" -import { CleanerService } from "./cleaner" -import { EntryRelatedKey, EntryRelatedService } from "./entry-related" -import type { Hydratable } from "./interface" - -type EntryCollection = { - createdAt: string -} -class EntryServiceStatic extends BaseService implements Hydratable { - constructor() { - super(browserDB.entries) - } - - // @ts-expect-error - override async upsertMany(data: EntryModel[], entryFeedMap: Record) { - const renewList = [] as { type: "entry"; id: string }[] - const nextData = [] as (EntryModel & { feedId?: string; inboxId?: string })[] - - for (const entry of data) { - const feedId = entryFeedMap[entry.id] - if (!feedId) { - console.error("EntryService.upsertMany: feedId not found", entry) - continue - } - renewList.push({ type: "entry", id: entry.id }) - nextData.push(Object.assign({}, entry, feedId ? { feedId } : { inboxId: feedId })) - } - - CleanerService.reset(renewList) - - return super.upsertMany(nextData) - } - - // @ts-ignore - override async upsert(feedId: string, data: EntryModel): Promise { - CleanerService.reset([ - { - type: "entry", - id: data.id, - }, - ]) - return super.upsert({ - ...data, - // @ts-expect-error - feedId, - }) - } - - async bulkPatch(data: { key: string; changes: Partial }[]) { - await this.table.bulkUpdate(data) - CleanerService.reset(data.map((d) => ({ type: "entry", id: d.key }))) - } - - override async findAll() { - return super.findAll() as Promise<(EntryModel & { feedId: string; inboxId: string })[]> - } - - bulkStoreReadStatus(record: Record) { - return EntryRelatedService.upsert(EntryRelatedKey.READ, record) - } - - async bulkStoreCollection(record: Record) { - return EntryRelatedService.upsert(EntryRelatedKey.COLLECTION, record) - } - - async deleteCollection(entryId: string) { - return EntryRelatedService.deleteItems(EntryRelatedKey.COLLECTION, [entryId]) - } - - async deleteEntries(entryIds: string[]) { - await Promise.all([ - this.table.bulkDelete(entryIds), - EntryRelatedService.deleteItems(EntryRelatedKey.READ, entryIds), - EntryRelatedService.deleteItems(EntryRelatedKey.COLLECTION, entryIds), - CleanerService.cleanRefById(entryIds), - ]) - } - - async deleteEntriesByFeedIds(feedIds: string[]) { - const deleteEntryIds = await this.table.where("feedId").anyOf(feedIds).primaryKeys() - await Promise.all([ - this.table.where("feedId").anyOf(feedIds).delete(), - EntryRelatedService.deleteItems(EntryRelatedKey.READ, deleteEntryIds), - EntryRelatedService.deleteItems(EntryRelatedKey.COLLECTION, deleteEntryIds), - ]) - } - - async hydrate() {} -} - -export const EntryService = new EntryServiceStatic() diff --git a/apps/desktop/layer/renderer/src/services/feed-unread.ts b/apps/desktop/layer/renderer/src/services/feed-unread.ts deleted file mode 100644 index ab97f2f60..000000000 --- a/apps/desktop/layer/renderer/src/services/feed-unread.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { browserDB } from "~/database" - -import type { Hydratable } from "./interface" - -const unreadModel = browserDB.feedUnreads -class ServiceStatic implements Hydratable { - updateUnread(list: [string, number][]) { - return unreadModel.bulkPut(list.map(([id, count]) => ({ id, count }))) - } - - getAll() { - return unreadModel.toArray() as Promise< - { - id: string - count: number - }[] - > - } - - clear() { - return unreadModel.clear() - } - - async bulkDelete(ids: string[]) { - return unreadModel.bulkDelete(ids) - } - - async hydrate() {} -} - -export const UnreadService = new ServiceStatic() diff --git a/apps/desktop/layer/renderer/src/services/feed.ts b/apps/desktop/layer/renderer/src/services/feed.ts deleted file mode 100644 index f24668661..000000000 --- a/apps/desktop/layer/renderer/src/services/feed.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { FeedModel, FeedOrListModel } from "@follow/models/types" - -import { browserDB } from "~/database" - -import { BaseService } from "./base" -import { CleanerService } from "./cleaner" -import type { Hydratable } from "./interface" - -type FeedModelWithId = FeedModel & { id: string } -class ServiceStatic extends BaseService implements Hydratable { - constructor() { - super(browserDB.feeds) - } - - override async upsertMany(data: FeedOrListModel[]) { - const filterData = data.filter((d) => d.id) - - CleanerService.reset(filterData.map((d) => ({ type: "feed", id: d.id! }))) - - return this.table.bulkPut(filterData as FeedModelWithId[]) - } - - override async upsert(data: FeedOrListModel): Promise { - if (!data.id) return null - CleanerService.reset([{ type: "feed", id: data.id }]) - return this.table.put(data as FeedModelWithId) - } - - async bulkDelete(ids: string[]) { - return this.table.bulkDelete(ids) - } - - async hydrate() {} -} - -export const FeedService = new ServiceStatic() diff --git a/apps/desktop/layer/renderer/src/services/inbox.ts b/apps/desktop/layer/renderer/src/services/inbox.ts deleted file mode 100644 index 4cbb4e39c..000000000 --- a/apps/desktop/layer/renderer/src/services/inbox.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { InboxModel } from "@follow/models/types" - -import { browserDB } from "~/database" - -import { BaseService } from "./base" -import { CleanerService } from "./cleaner" -import type { Hydratable } from "./interface" - -class ServiceStatic extends BaseService<{ id: string }> implements Hydratable { - constructor() { - super(browserDB.inboxes) - } - - override async upsertMany(data: InboxModel[]) { - CleanerService.reset(data.map((d) => ({ type: "inbox", id: d.id! }))) - - return this.table.bulkPut(data) - } - - override async findAll() { - return super.findAll() as unknown as InboxModel[] - } - - override async upsert(data: InboxModel): Promise { - if (!data.id) return null - CleanerService.reset([{ type: "inbox", id: data.id }]) - return this.table.put(data) - } - - async bulkDelete(ids: string[]) { - return this.table.bulkDelete(ids) - } - - async hydrate() {} -} - -export const InboxService = new ServiceStatic() diff --git a/apps/desktop/layer/renderer/src/services/index.ts b/apps/desktop/layer/renderer/src/services/index.ts deleted file mode 100644 index 835beafbb..000000000 --- a/apps/desktop/layer/renderer/src/services/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./entry" -export * from "./entry-related" -export * from "./feed" -export * from "./feed-unread" -export * from "./subscription" diff --git a/apps/desktop/layer/renderer/src/services/interface.ts b/apps/desktop/layer/renderer/src/services/interface.ts deleted file mode 100644 index cb58aae60..000000000 --- a/apps/desktop/layer/renderer/src/services/interface.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface Hydratable { - hydrate: () => Promise -} diff --git a/apps/desktop/layer/renderer/src/services/list.ts b/apps/desktop/layer/renderer/src/services/list.ts deleted file mode 100644 index 14f5b8dab..000000000 --- a/apps/desktop/layer/renderer/src/services/list.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { ListModel } from "@follow/models/types" - -import { browserDB } from "~/database" - -import { BaseService } from "./base" -import { CleanerService } from "./cleaner" -import type { Hydratable } from "./interface" - -class ServiceStatic extends BaseService<{ id: string }> implements Hydratable { - constructor() { - super(browserDB.lists) - } - - override async upsertMany(data: ListModel[]) { - CleanerService.reset(data.map((d) => ({ type: "list", id: d.id! }))) - - // FIXME The backend should not pass these computed attributes, and these need to be removed here. - // Subsequent refactoring of the backend data flow should not nest computed attributes - return this.table.bulkPut(data.map(({ owner, ...d }) => d) as ListModel[]) - } - - override async findAll() { - return super.findAll() as unknown as ListModel[] - } - - override async upsert({ owner, ...data }: ListModel): Promise { - if (!data.id) return null - CleanerService.reset([{ type: "list", id: data.id }]) - return this.table.put(data as ListModel) - } - - async bulkDelete(ids: string[]) { - return this.table.bulkDelete(ids) - } - - async findAndUpdate(id: string, data: Partial) { - const list = await this.table.get(id) - if (!list) return - return this.table.put({ ...list, ...data }) - } - - async hydrate() {} -} - -export const ListService = new ServiceStatic() diff --git a/apps/desktop/layer/renderer/src/services/subscription.ts b/apps/desktop/layer/renderer/src/services/subscription.ts deleted file mode 100644 index e7c8ede92..000000000 --- a/apps/desktop/layer/renderer/src/services/subscription.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { uniq } from "es-toolkit/compat" - -import { browserDB } from "~/database" - -import { BaseService } from "./base" -import type { Hydratable } from "./interface" - -type SubscriptionModelWithId = any & { id: string } - -class SubscriptionServiceStatic extends BaseService implements Hydratable { - constructor() { - super(browserDB.subscriptions) - } - - public getUserSubscriptions(userIds: string[]) { - return this.table.where("userId").anyOf(userIds).toArray() - } - - public async getUserIds() { - return uniq( - (await this.table - .toCollection() - .uniqueKeys() - .then((keys) => keys.map((k) => k.toString().split("/")[0]))) as string[], - ) - } - - override async upsertMany(data: any[]) { - return this.table.bulkPut( - data.map(({ feeds, lists, inboxes, ...d }: any) => ({ - ...d, - id: this.uniqueId(d.userId, d.feedId), - })), - ) - } - - override upsert(data: any) { - return this.table.put({ - ...data, - id: this.uniqueId(data.userId, data.feedId), - }) - } - - private uniqueId(userId: string, feedId: string) { - return `${userId}/${feedId}` - } - - async changeView(feedId: string, view: number) { - return this.table.where("feedId").equals(feedId).modify({ view }) - } - async changeViews(feedIdList: string[], view: number) { - return this.table.where("feedId").anyOf(feedIdList).modify({ view }) - } - - async updateCategory(feedId: string, category?: string | null) { - return this.table.where("feedId").equals(feedId).modify({ category }) - } - async updateCategories(feedIdList: string[], category?: string | null) { - return this.table.where("feedId").anyOf(feedIdList).modify({ category }) - } - - async removeSubscription(userId: string, feedId: string): Promise - // @ts-expect-error - async removeSubscription(userId: string): Promise - async removeSubscription(userId: string, feedId: string) { - if (feedId && userId) { - return this.table.delete(this.uniqueId(userId, feedId)) - } - if (!feedId && userId) { - return this.table.where("userId").equals(userId).delete() - } - } - - async removeSubscriptionMany(userId: string, feedIdList: string[]) { - return this.table.bulkDelete(feedIdList.map((feedId) => this.uniqueId(userId, feedId))) - } - - async renameCategory(userId: string, feedIdList: string[], category: string) { - return this.table - .where("userId") - .equals(userId) - .and((item) => feedIdList.includes(item.feedId)) - .modify({ category }) - } - - async hydrate() {} -} - -export const SubscriptionService = new SubscriptionServiceStatic() diff --git a/apps/desktop/layer/renderer/src/store/search/index.ts b/apps/desktop/layer/renderer/src/store/search/index.ts index e08df135d..39fc48112 100644 --- a/apps/desktop/layer/renderer/src/store/search/index.ts +++ b/apps/desktop/layer/renderer/src/store/search/index.ts @@ -1,4 +1,7 @@ -import type { EntryModel } from "@follow/models/types" +import { EntryService } from "@follow/database/services/entry" +import { FeedService } from "@follow/database/services/feed" +import { SubscriptionService } from "@follow/database/services/subscription" +import type { EntryModel } from "@follow/store/entry/types" import type { SubscriptionModel } from "@follow/store/subscription/types" import { getStorageNS } from "@follow/utils/ns" import type { IFuseOptions } from "fuse.js" @@ -7,7 +10,6 @@ import { useAtomValue } from "jotai" import { atomWithStorage } from "jotai/utils" import { jotaiStore } from "~/lib/jotai" -import { EntryService, FeedService, SubscriptionService } from "~/services" import { createZustandStore } from "../utils/helper" import { SearchType } from "./constants" @@ -45,9 +47,12 @@ class SearchActions { async createLocalDbSearch() { const [entries, feeds, subscriptions] = await Promise.all([ - EntryService.findAll(), - FeedService.findAll(), - SubscriptionService.findAll(), + EntryService.getEntryAll(), + (await FeedService.getFeedAll()).map((feed) => ({ + ...feed, + type: "feed" as const, + })), + SubscriptionService.getSubscriptionAll(), ]) const feedsMap = new Map(feeds.map((feed) => [feed.id, feed])) @@ -72,7 +77,7 @@ class SearchActions { const processedEntries = [] as SearchResult[] for (const entry of entries) { - const feedId = feedsMap.get(entry.item.feedId)?.id + const feedId = entry.item.feedId ? feedsMap.get(entry.item.feedId)?.id : undefined if (feedId) { processedEntries.push({ item: entry.item, feedId }) } diff --git a/apps/desktop/layer/renderer/src/store/search/types.ts b/apps/desktop/layer/renderer/src/store/search/types.ts index cb4455289..892e01428 100644 --- a/apps/desktop/layer/renderer/src/store/search/types.ts +++ b/apps/desktop/layer/renderer/src/store/search/types.ts @@ -1,4 +1,5 @@ -import type { EntryModel, FeedOrListRespModel } from "@follow/models/types" +import type { EntryModel } from "@follow/store/entry/types" +import type { FeedModel } from "@follow/store/feed/types" import type { SubscriptionModel } from "@follow/store/subscription/types" // @ts-expect-error @@ -7,7 +8,7 @@ export interface SearchResult exten } export interface SearchState { - feeds: SearchResult[] + feeds: SearchResult[] entries: SearchResult[] subscriptions: SearchResult[] diff --git a/apps/desktop/layer/renderer/src/store/utils/clear.ts b/apps/desktop/layer/renderer/src/store/utils/clear.ts index 9d1b6d8f8..3aba51ce4 100644 --- a/apps/desktop/layer/renderer/src/store/utils/clear.ts +++ b/apps/desktop/layer/renderer/src/store/utils/clear.ts @@ -7,7 +7,6 @@ import { unreadActions } from "@follow/store/unread/store" import { getStorageNS } from "@follow/utils/ns" import { clearUISettings } from "~/atoms/settings/ui" -import { browserDB } from "~/database" import { clearImageDimensionsDb } from "../image/db" @@ -27,7 +26,6 @@ export const clearLocalPersistStoreData = async () => { clearUISettings() await clearImageDimensionsDb() - await Promise.all(browserDB.tables.map((table) => table.clear())) } const storedUserId = getStorageNS("user_id") diff --git a/apps/desktop/layer/renderer/src/store/utils/helper.ts b/apps/desktop/layer/renderer/src/store/utils/helper.ts index 0079620b6..216abdb30 100644 --- a/apps/desktop/layer/renderer/src/store/utils/helper.ts +++ b/apps/desktop/layer/renderer/src/store/utils/helper.ts @@ -6,8 +6,6 @@ import { shallow } from "zustand/shallow" import type { UseBoundStoreWithEqualityFn } from "zustand/traditional" import { createWithEqualityFn } from "zustand/traditional" -import { runTransactionInScope } from "~/database" - declare const window: any export const localStorage: PersistStorage = { getItem: (name: string) => { @@ -172,12 +170,10 @@ class Transaction { } if (this.onPersist) { - await runTransactionInScope(() => - Promise.resolve(this.onPersist!(this._snapshot, this._ctx)).catch((err) => { - console.error(err) - throw err - }), - ) + await Promise.resolve(this.onPersist!(this._snapshot, this._ctx)).catch((err) => { + console.error(err) + throw err + }) } } } diff --git a/apps/desktop/vite.config.ts b/apps/desktop/vite.config.ts index b4ab761ca..3c385d283 100644 --- a/apps/desktop/vite.config.ts +++ b/apps/desktop/vite.config.ts @@ -220,7 +220,7 @@ export default ({ mode }) => { ["react", "react-dom"], ["react-error-boundary", "react-dom/server", "react-router"], // Data Statement - ["zustand", "jotai", "use-context-selector", "immer", "dexie"], + ["zustand", "jotai", "use-context-selector", "immer"], // Remark [ "remark-directive", diff --git a/apps/ssr/client/pages/(login)/layout.tsx b/apps/ssr/client/pages/(login)/layout.tsx index 90545743c..2234e12fe 100644 --- a/apps/ssr/client/pages/(login)/layout.tsx +++ b/apps/ssr/client/pages/(login)/layout.tsx @@ -18,6 +18,7 @@ const UserProvider = () => { useEffect(() => { if (!session?.user) return + // @ts-expect-error FIXME setWhoami(session.user) setIntegrationIdentify(session.user) diff --git a/apps/ssr/client/providers/user-provider.tsx b/apps/ssr/client/providers/user-provider.tsx index 8818936b1..ba055bbb0 100644 --- a/apps/ssr/client/providers/user-provider.tsx +++ b/apps/ssr/client/providers/user-provider.tsx @@ -8,6 +8,7 @@ export const UserProvider = () => { useEffect(() => { if (!session?.user) return + // @ts-expect-error FIXME setWhoami(session.user) setIntegrationIdentify(session.user) diff --git a/packages/internal/store/src/unread/store.ts b/packages/internal/store/src/unread/store.ts index a91b68937..d5d9460e7 100644 --- a/packages/internal/store/src/unread/store.ts +++ b/packages/internal/store/src/unread/store.ts @@ -195,8 +195,8 @@ class UnreadSyncService { } }) - tx.request(() => { - return apiClient().reads.$post({ + tx.request(async () => { + await apiClient().reads.$post({ json: { entryIds: [entryId] }, }) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 301ed3063..5b484c297 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -550,9 +550,6 @@ importers: dayjs: specifier: 1.11.13 version: 1.11.13 - dexie: - specifier: 4.0.11 - version: 4.0.11 dnum: specifier: 2.15.0 version: 2.15.0 @@ -8072,9 +8069,6 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - dexie@4.0.11: - resolution: {integrity: sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A==} - didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -24208,8 +24202,6 @@ snapshots: dependencies: dequal: 2.0.3 - dexie@4.0.11: {} - didyoumean@1.2.2: {} diff@4.0.2: {}