Skip to content

Commit

Permalink
feat: local db (#97)
Browse files Browse the repository at this point in the history
* feat: local db init v2

Signed-off-by: Innei <i@innei.in>

* feat: hydrate

Signed-off-by: Innei <i@innei.in>

* feat: setting

Signed-off-by: Innei <i@innei.in>

* fix: read color in dark mode

Signed-off-by: Innei <i@innei.in>

* chore: cleanup

Signed-off-by: Innei <i@innei.in>

* update

Signed-off-by: Innei <i@innei.in>

* update

Signed-off-by: Innei <i@innei.in>

* update

Signed-off-by: Innei <i@innei.in>

* feat: persist star data

Signed-off-by: Innei <i@innei.in>

* Update src/renderer/src/store/entry/hooks.ts

---------

Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei authored Jun 28, 2024
1 parent e1ae88f commit be6968c
Show file tree
Hide file tree
Showing 52 changed files with 800 additions and 176 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"clsx": "2.1.1",
"cmdk": "1.0.0",
"dayjs": "1.11.11",
"dexie": "4.0.7",
"dnum": "^2.13.1",
"dotenv": "16.4.5",
"electron-updater": "^6.2.1",
Expand Down Expand Up @@ -96,7 +97,6 @@
"rehype-stringify": "10.0.0",
"shiki": "1.9.1",
"sonner": "^1.5.0",
"superjson": "2.2.1",
"swiper": "11.1.4",
"tailwind-merge": "2.3.0",
"tailwindcss-animate": "1.0.7",
Expand Down
33 changes: 8 additions & 25 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/renderer/src/components/feed-icon.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SiteIcon } from "@renderer/components/site-icon"
import { Image } from "@renderer/components/ui/image"
import { cn } from "@renderer/lib/utils"
import type { EntryModel, FeedModel } from "@renderer/models"
import type { CombinedEntryModel, FeedModel } from "@renderer/models"

export function FeedIcon({
feed,
Expand All @@ -11,7 +11,7 @@ export function FeedIcon({
size = 20,
}: {
feed: FeedModel
entry?: EntryModel["entries"]
entry?: CombinedEntryModel["entries"]
fallbackUrl?: string
className?: string
size?: number
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/database/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const LOCAL_DB_NAME = "FOLLOW_DB"
55 changes: 55 additions & 0 deletions src/renderer/src/database/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import Dexie from "dexie"

import { LOCAL_DB_NAME } from "./constants"
import {
dbSchemaV1,
} from "./db_schema"
import type { DB_Entry } from "./schemas/entry"
import type { DBModel } from "./types"

export interface LobeDBSchemaMap {

entries: DB_Entry
// TODO - Add more schemas here
feeds: DB_Entry
subscriptions: DB_Entry
entryRelated: DB_Entry
feedEntries: DB_Entry
}

// Define a local DB
export class BrowserDB extends Dexie {
public entries: BrowserDBTable<"entries">
public feeds: BrowserDBTable<"feeds">
public subscriptions: BrowserDBTable<"subscriptions">
public entryRelated: BrowserDBTable<"entryRelated">
public feedEntries: BrowserDBTable<"feedEntries">

constructor() {
super(LOCAL_DB_NAME)
this.version(1).stores(dbSchemaV1)

this.entries = this.table("entries")
this.feeds = this.table("feeds")
this.subscriptions = this.table("subscriptions")
this.entryRelated = this.table("entryRelated")
this.feedEntries = this.table("feedEntries")
}
}

export const browserDB = new BrowserDB()

// ================================================ //
// ================================================ //
// ================================================ //
// ================================================ //
// ================================================ //

// types helper
export type BrowserDBSchema = {
[t in keyof LobeDBSchemaMap]: {
model: LobeDBSchemaMap[t]
table: Dexie.Table<DBModel<LobeDBSchemaMap[t]>, string>
};
}
type BrowserDBTable<T extends keyof LobeDBSchemaMap> = BrowserDBSchema[T]["table"]
7 changes: 7 additions & 0 deletions src/renderer/src/database/db_schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const dbSchemaV1 = {
entries: "&id",
feeds: "&id",
subscriptions: "&id",
entryRelated: "&id",
feedEntries: "&feedId",
}
16 changes: 16 additions & 0 deletions src/renderer/src/database/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createAtomHooks, jotaiStore } from "@renderer/lib/jotai"
import { buildStorageNS } from "@renderer/lib/ns"
import { atomWithStorage } from "jotai/utils"

const SHOULD_USE_INDEXED_DB_KEY = buildStorageNS("shouldUseIndexedDB")

export const [
__shouldUseIndexedDBAtom,
useShouldUseIndexedDB,
useShouldUseIndexedDBValue,
useSetShouldUseIndexedDB,
getShouldUseIndexedDB,
setShouldUseIndexedDB,
] = createAtomHooks(atomWithStorage(SHOULD_USE_INDEXED_DB_KEY, false))

export const subscribeShouldUseIndexedDB = (callback: (value: boolean) => void) => jotaiStore.sub(__shouldUseIndexedDBAtom, () => callback(getShouldUseIndexedDB()))
4 changes: 4 additions & 0 deletions src/renderer/src/database/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./db"
export * from "./hooks"
export * from "./models"
export * from "./schemas"
31 changes: 31 additions & 0 deletions src/renderer/src/database/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @see https://github.com/lobehub/lobe-chat/blob/adebf0a92167faad48581b0b0780cf8faeba362f/src/database/client/core/model.ts
*/

import type Dexie from "dexie"
import type { ZodObject } from "zod"

import type { BrowserDB, BrowserDBSchema } from "./db"
import { browserDB } from "./db"

export class BaseModel<
N extends keyof BrowserDBSchema = any,
// T extends { id: string } = any,
// T = BrowserDBSchema[N]["table"],
> {
protected readonly db: BrowserDB
// used to data validation, but use now

private readonly schema: ZodObject<any>
private readonly _tableName: keyof BrowserDBSchema

constructor(table: N, schema: ZodObject<any>, db = browserDB) {
this.db = db
this.schema = schema
this._tableName = table
}

get table() {
return this.db[this._tableName] as Dexie.Table
}
}
10 changes: 10 additions & 0 deletions src/renderer/src/database/models/entry-related.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { BaseModel } from "../model"
import { DB_EntrySchema } from "../schemas"

class ModelStatic extends BaseModel<"entryRelated"> {
constructor() {
super("entryRelated", DB_EntrySchema)
}
}

export const entryRelatedModel = new ModelStatic()
10 changes: 10 additions & 0 deletions src/renderer/src/database/models/entry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { BaseModel } from "../model"
import { DB_EntrySchema } from "../schemas"

class EntryModelStatic extends BaseModel<"entries"> {
constructor() {
super("entries", DB_EntrySchema)
}
}

export const entryModel = new EntryModelStatic()
10 changes: 10 additions & 0 deletions src/renderer/src/database/models/feed-entry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { BaseModel } from "../model"
import { DB_EntrySchema } from "../schemas"

class ModelStatic extends BaseModel<"feedEntries"> {
constructor() {
super("feedEntries", DB_EntrySchema)
}
}

export const feedEntriesModel = new ModelStatic()
10 changes: 10 additions & 0 deletions src/renderer/src/database/models/feed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { BaseModel } from "../model"
import { DB_EntrySchema } from "../schemas"

class ModelStatic extends BaseModel<"feeds"> {
constructor() {
super("feeds", DB_EntrySchema)
}
}

export const feedModel = new ModelStatic()
5 changes: 5 additions & 0 deletions src/renderer/src/database/models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./entry"
export * from "./entry-related"
export * from "./feed"
export * from "./feed-entry"
export * from "./subscription"
10 changes: 10 additions & 0 deletions src/renderer/src/database/models/subscription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { BaseModel } from "../model"
import { DB_EntrySchema } from "../schemas"

class ModelStatic extends BaseModel<"subscriptions"> {
constructor() {
super("subscriptions", DB_EntrySchema)
}
}

export const subscriptionModel = new ModelStatic()
7 changes: 7 additions & 0 deletions src/renderer/src/database/schemas/entry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { z } from "zod"

export const DB_EntrySchema = z.object({
id: z.string(),
})

export type DB_Entry = z.infer<typeof DB_EntrySchema>
1 change: 1 addition & 0 deletions src/renderer/src/database/schemas/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./entry"
5 changes: 5 additions & 0 deletions src/renderer/src/database/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type DBModel<T> = T & {
createdAt: number
id: string
updatedAt: number
}
9 changes: 4 additions & 5 deletions src/renderer/src/hooks/biz/useAsRead.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { FEED_COLLECTION_LIST, levels } from "@renderer/lib/constants"
import type { EntryModel } from "@renderer/models"
import type { CombinedEntryModel } from "@renderer/models"

import { useRouteParamsSelector } from "./useRouteParams"

export function useAsRead(entry?: EntryModel) {
return useRouteParamsSelector(({ feedId, level }) => {
export function useAsRead(entry?: CombinedEntryModel) {
return useRouteParamsSelector(() => {
if (!entry) return false
return entry.read && !(level === levels.folder && feedId === FEED_COLLECTION_LIST)
return entry.read
}, [entry?.read])
}
2 changes: 1 addition & 1 deletion src/renderer/src/hooks/biz/useAuth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { signOut } from "@hono/auth-js/react"
import { QUERY_PERSIST_KEY } from "@renderer/lib/constants"
import { clearLocalPersistStoreData } from "@renderer/store/utils/local"
import { clearLocalPersistStoreData } from "@renderer/store/utils/clear"
import { useCallback } from "react"

export const useSignOut = () =>
Expand Down
Loading

0 comments on commit be6968c

Please sign in to comment.