refactor: hydrate service interface

Signed-off-by: Innei <i@innei.in>
This commit is contained in:
Innei 2024-10-03 23:30:36 +08:00
parent 020ea017eb
commit a66a415ad3
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
10 changed files with 112 additions and 88 deletions

View File

@ -55,7 +55,7 @@
"severity": "off"
}
],
"cSpell.words": ["rsshub", "Русский"],
"cSpell.words": ["Hydable", "rsshub", "Русский"],
"editor.foldingImportsByDefault": true,
"commentTranslate.hover.enabled": false,
"typescript.tsdk": "node_modules/typescript/lib",

View File

@ -3,22 +3,10 @@ import { initializeDefaultIntegrationSettings } from "~/atoms/settings/integrati
import { initializeDefaultUISettings } from "~/atoms/settings/ui"
import { appLog } from "~/lib/log"
import { sleep } from "~/lib/utils"
import {
EntryRelatedKey,
EntryRelatedService,
EntryService,
FeedService,
FeedUnreadService,
SubscriptionService,
} from "~/services"
import { EntryService, FeedService, FeedUnreadService, SubscriptionService } from "~/services"
import { InboxService } from "~/services/inbox"
import type { Hydable } from "~/services/interface"
import { ListService } from "~/services/list"
import type { FlatEntryModel } from "~/store/entry"
import { listActions } from "~/store/list"
import { entryActions, useEntryStore } from "../store/entry/store"
import { feedActions, useFeedStore } from "../store/feed"
import { subscriptionActions } from "../store/subscription"
import { feedUnreadActions } from "../store/unread"
export const setHydrated = (v: boolean) => {
window.__dbIsReady = v
@ -27,13 +15,16 @@ export const setHydrated = (v: boolean) => {
export const hydrateDatabaseToStore = async () => {
async function hydrate() {
const now = Date.now()
await Promise.all([
hydrateFeed(),
hydrateSubscription(),
hydrateFeedUnread(),
hydrateEntry(),
hydrateList(),
])
const hydrates: Hydable[] = [
FeedService,
SubscriptionService,
FeedUnreadService,
EntryService,
ListService,
InboxService,
]
await Promise.all(hydrates.map((h) => h.hydrate()))
window.__dbIsReady = true
const costTime = Date.now() - now
@ -48,65 +39,6 @@ export const hydrateDatabaseToStore = async () => {
})
}
async function hydrateFeed() {
const feeds = await FeedService.findAll()
feedActions.upsertMany(feeds)
return useFeedStore.getState().feeds
}
async function hydrateFeedUnread() {
const unread = await FeedUnreadService.getAll()
return feedUnreadActions.hydrate(unread)
}
async function hydrateEntry() {
const [entries, entryRelated, feedEntries, collections] = await Promise.all([
EntryService.findAll(),
EntryRelatedService.findAll(EntryRelatedKey.READ),
EntryRelatedService.findAll(EntryRelatedKey.FEED_ID),
EntryRelatedService.findAll(EntryRelatedKey.COLLECTION),
])
const storeValue = [] as FlatEntryModel[]
for (const entry of entries) {
const entryRelatedFeedId = entry.feedId || feedEntries[entry.id]
if (!entryRelatedFeedId) {
logHydrateError(`Entry ${entry.id} has no related feed id`)
continue
}
storeValue.push({
entries: entry,
feedId: entryRelatedFeedId,
read: entryRelated[entry.id] || false,
collections: collections[entry.id] as {
createdAt: string
},
})
}
entryActions.hydrate(storeValue)
useEntryStore.setState({
starIds: new Set(Object.keys(collections)),
})
}
async function hydrateSubscription() {
const subscriptions = await SubscriptionService.findAll()
subscriptionActions.upsertMany(subscriptions)
}
async function hydrateList() {
const lists = await ListService.findAll()
listActions.upsertMany(lists)
}
const logHydrateError = (message: string) => {
// eslint-disable-next-line no-console
console.debug(`Hydrate error: ${message}, maybe local database data is dirty.`)
}
export const hydrateSettings = () => {
initializeDefaultUISettings()
initializeDefaultGeneralSettings()

View File

@ -80,5 +80,9 @@ class CleanerServiceStatic {
cleanerModel.bulkDelete(data.map((d) => d.refId)),
])
}
async cleanRefById(refIds: string[]) {
return cleanerModel.bulkDelete(refIds)
}
}
export const CleanerService = new CleanerServiceStatic()

View File

@ -1,14 +1,18 @@
import { browserDB } from "~/database"
import { appLog } from "~/lib/log"
import type { EntryModel } from "~/models/types"
import type { FlatEntryModel } from "~/store/entry"
import { entryActions, useEntryStore } from "~/store/entry"
import { BaseService } from "./base"
import { CleanerService } from "./cleaner"
import { EntryRelatedKey, EntryRelatedService } from "./entry-related"
import type { Hydable } from "./interface"
type EntryCollection = {
createdAt: string
}
class EntryServiceStatic extends BaseService<EntryModel> {
class EntryServiceStatic extends BaseService<EntryModel> implements Hydable {
constructor() {
super(browserDB.entries)
}
@ -77,6 +81,7 @@ class EntryServiceStatic extends BaseService<EntryModel> {
this.table.bulkDelete(entryIds),
EntryRelatedService.deleteItems(EntryRelatedKey.READ, entryIds),
EntryRelatedService.deleteItems(EntryRelatedKey.COLLECTION, entryIds),
CleanerService.cleanRefById(entryIds),
])
}
@ -88,6 +93,48 @@ class EntryServiceStatic extends BaseService<EntryModel> {
EntryRelatedService.deleteItems(EntryRelatedKey.COLLECTION, deleteEntryIds),
])
}
async hydrate() {
const [entries, entryRelated, feedEntries, collections] = await Promise.all([
EntryService.findAll(),
EntryRelatedService.findAll(EntryRelatedKey.READ),
EntryRelatedService.findAll(EntryRelatedKey.FEED_ID),
EntryRelatedService.findAll(EntryRelatedKey.COLLECTION),
])
const storeValue = [] as FlatEntryModel[]
const dirtyEntryIds = [] as string[]
for (const entry of entries) {
const entryRelatedFeedId = entry.feedId || feedEntries[entry.id]
if (!entryRelatedFeedId) {
appLog(`[Data hydrate warning]: Entry ${entry.id} has no related feed id`)
dirtyEntryIds.push(entry.id)
continue
}
storeValue.push({
entries: entry,
feedId: entryRelatedFeedId,
read: entryRelated[entry.id] || false,
collections: collections[entry.id] as {
createdAt: string
},
})
}
entryActions.hydrate(storeValue)
useEntryStore.setState({
starIds: new Set(Object.keys(collections)),
})
if (dirtyEntryIds.length > 0) {
// Remove entries that have no related feed id
EntryService.deleteEntries(dirtyEntryIds)
appLog(
`[Data hydrate warning]: Entry ${dirtyEntryIds.join(", ")} has no related feed id, cleanup..`,
)
}
}
}
export const EntryService = new EntryServiceStatic()

View File

@ -1,7 +1,10 @@
import { browserDB } from "~/database"
import { feedUnreadActions } from "~/store/unread"
import type { Hydable } from "./interface"
const feedUnreadModel = browserDB.feedUnreads
class ServiceStatic {
class ServiceStatic implements Hydable {
updateFeedUnread(list: [string, number][]) {
return feedUnreadModel.bulkPut(list.map(([feedId, count]) => ({ id: feedId, count })))
}
@ -22,6 +25,12 @@ class ServiceStatic {
async bulkDelete(ids: string[]) {
return feedUnreadModel.bulkDelete(ids)
}
async hydrate() {
const unread = await FeedUnreadService.getAll()
return feedUnreadActions.hydrate(unread)
}
}
export const FeedUnreadService = new ServiceStatic()

View File

@ -1,11 +1,13 @@
import { browserDB } from "~/database"
import type { FeedModel, FeedOrListModel } from "~/models/types"
import { feedActions } from "~/store/feed"
import { BaseService } from "./base"
import { CleanerService } from "./cleaner"
import type { Hydable } from "./interface"
type FeedModelWithId = FeedModel & { id: string }
class ServiceStatic extends BaseService<FeedModelWithId> {
class ServiceStatic extends BaseService<FeedModelWithId> implements Hydable {
constructor() {
super(browserDB.feeds)
}
@ -27,6 +29,11 @@ class ServiceStatic extends BaseService<FeedModelWithId> {
async bulkDelete(ids: string[]) {
return this.table.bulkDelete(ids)
}
async hydrate() {
const feeds = await FeedService.findAll()
feedActions.upsertMany(feeds)
}
}
export const FeedService = new ServiceStatic()

View File

@ -1,10 +1,12 @@
import { browserDB } from "~/database"
import type { InboxModel } from "~/models/types"
import { inboxActions } from "~/store/inbox"
import { BaseService } from "./base"
import { CleanerService } from "./cleaner"
import type { Hydable } from "./interface"
class ServiceStatic extends BaseService<{ id: string }> {
class ServiceStatic extends BaseService<{ id: string }> implements Hydable {
constructor() {
super(browserDB.inboxes)
}
@ -28,6 +30,11 @@ class ServiceStatic extends BaseService<{ id: string }> {
async bulkDelete(ids: string[]) {
return this.table.bulkDelete(ids)
}
async hydrate() {
const data = await this.findAll()
inboxActions.upsertMany(data)
}
}
export const InboxService = new ServiceStatic()

View File

@ -0,0 +1,3 @@
export interface Hydable {
hydrate: () => Promise<void>
}

View File

@ -2,11 +2,13 @@ import { omit } from "lodash-es"
import { browserDB } from "~/database"
import type { ListModel } from "~/models/types"
import { listActions } from "~/store/list"
import { BaseService } from "./base"
import { CleanerService } from "./cleaner"
import type { Hydable } from "./interface"
class ServiceStatic extends BaseService<{ id: string }> {
class ServiceStatic extends BaseService<{ id: string }> implements Hydable {
constructor() {
super(browserDB.lists)
}
@ -32,6 +34,11 @@ class ServiceStatic extends BaseService<{ id: string }> {
async bulkDelete(ids: string[]) {
return this.table.bulkDelete(ids)
}
async hydrate() {
const lists = await ListService.findAll()
listActions.upsertMany(lists)
}
}
export const ListService = new ServiceStatic()

View File

@ -2,12 +2,14 @@ import { uniq } from "lodash-es"
import { browserDB } from "~/database"
import type { SubscriptionFlatModel } from "~/store/subscription"
import { subscriptionActions } from "~/store/subscription"
import { BaseService } from "./base"
import type { Hydable } from "./interface"
type SubscriptionModelWithId = SubscriptionFlatModel & { id: string }
class SubscriptionServiceStatic extends BaseService<SubscriptionModelWithId> {
class SubscriptionServiceStatic extends BaseService<SubscriptionModelWithId> implements Hydable {
constructor() {
super(browserDB.subscriptions)
}
@ -68,6 +70,12 @@ class SubscriptionServiceStatic extends BaseService<SubscriptionModelWithId> {
.and((item) => feedIdList.includes(item.feedId))
.modify({ category })
}
async hydrate() {
const subscriptions = await SubscriptionService.findAll()
subscriptionActions.upsertMany(subscriptions)
}
}
export const SubscriptionService = new SubscriptionServiceStatic()