Skip to content

Commit

Permalink
feat: #773 偏好设置数据持久化
Browse files Browse the repository at this point in the history
  • Loading branch information
terwer committed Oct 11, 2023
1 parent b2ccb55 commit 1b2608d
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { SiyuanDevice } from "zhi-device"
* @version 0.9.0
* @since 0.9.0
*/
class CommonStorage implements StorageLikeAsync {
class CommonStorageAsync implements StorageLikeAsync {
private readonly logger
private readonly storageViaSiyuanApi
private readonly kernelApi
Expand Down Expand Up @@ -112,7 +112,7 @@ class CommonStorage implements StorageLikeAsync {
* @returns 一个 Promise,在设置值后解析。
*/
public async setItem(key: string, value: string): Promise<void> {
// this.logger.debug(`Setting value for '${key}' in CommonStorage to '${value}'.`)
// this.logger.debug(`Setting value for '${key}' in CommonStorageAsync to '${value}'.`)
if (this.storageViaSiyuanApi) {
// 如果当前运行在思源笔记中,则直接返回空字符串
await this.kernelApi.saveTextData(key, value)
Expand All @@ -125,4 +125,4 @@ class CommonStorage implements StorageLikeAsync {
}
}

export default CommonStorage
export default CommonStorageAsync
125 changes: 125 additions & 0 deletions src/stores/common/jsonStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2023, Terwer . All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Terwer designates this
* particular file as subject to the "Classpath" exception as provided
* by Terwer in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com
* or visit www.terwer.space if you need additional information or have any
* questions.
*/

import { StorageLike } from "@vueuse/core"
import { createAppLogger } from "~/src/utils/appLogger.ts"
import { SiyuanDevice } from "zhi-device"
import { JsonUtil } from "zhi-common"

/**
* JSON 同步存储实现,实现了 `StorageLikeAsync` 接口。
* https://github.com/vueuse/vueuse/blob/main/packages/core/ssr-handlers.ts#L11
*
* @author terwer
* @version 1.7.0
* @since 1.7.0
*/
class JsonStorage implements StorageLike {
private readonly logger: any
private readonly fs: any
private readonly path: any
private readonly filePath: string

constructor(filePath: string) {
this.logger = createAppLogger("json-storage")

// 动态导入依赖
const win = SiyuanDevice.siyuanWindow()
this.fs = win.require("fs")
this.path = win.require("path")

// 确保文件存在
const basePath = win?.siyuan.config.system.dataDir
this.filePath = this.path.join(basePath, filePath)
const storeDir = this.path.dirname(this.filePath)
this.logger.info("store dir =>", storeDir)
// 确保有目录
if (!this.fs.existsSync(storeDir)) {
this.fs.mkdirSync(storeDir)
}
// 确保文件已经初始化
if (!this.fs.existsSync(this.filePath)) {
this.writeFile({})
}
this.logger.info("ininted json local storage in =>", this.filePath)
}

/**
* 获取存储中的项目
*
* @param {string} key - 存储项的键名
* @returns {string | null} - 如果找到存储项,将返回其值;否则返回 null
*/
public getItem(key: string): string | null {
// this.logger.info("get item from json")
return this.getStoredItems()[key] || null
}

/**
* 设置存储项的值
*
* @param {string} key - 存储项的键名
* @param {string} value - 要存储的值
*/
public setItem(key: string, value: string): void {
// this.logger.info("set item from json")
const obj = this.getStoredItems()
obj[key] = value
this.writeFile(obj)
}

/**
* 从存储中移除指定的项
*
* @param {string} key - 要移除的存储项的键名
*/
public removeItem(key: string): void {
// this.logger.info("removeItem item from json")
const obj = this.getStoredItems()
delete obj[key]
this.writeFile(obj)
}

// ================
// private methods
// ================
private getStoredItems() {
return JsonUtil.safeParse<any>(this.readFile(), {})
}

private readFile() {
return this.fs.readFileSync(this.filePath, this.getFileOptions())
}

private writeFile(obj) {
return this.fs.writeFileSync(this.filePath, JSON.stringify(obj), this.getFileOptions())
}

private getFileOptions() {
return { encoding: "utf8" } as any
}
}

export default JsonStorage
73 changes: 73 additions & 0 deletions src/stores/common/useCommonLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2023, Terwer . All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Terwer designates this
* particular file as subject to the "Classpath" exception as provided
* by Terwer in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com
* or visit www.terwer.space if you need additional information or have any
* questions.
*/
import type {MaybeRefOrGetter, RemovableRef} from "@vueuse/shared"
import type {StorageLike, UseStorageOptions} from "@vueuse/core"
import {defaultWindow, useStorage} from "@vueuse/core"
import JsonStorage from "~/src/stores/common/jsonStorage.ts"
import {createAppLogger} from "~/src/utils/appLogger.ts"
import {useSiyuanDevice} from "~/src/composables/useSiyuanDevice.ts"

const logger = createAppLogger("use-common-local-storage")

/**
* 通用响应式的 LocalStorage.
*
* @see https://vueuse.org/useCommonLocalStorage
* @param filePath json文件存储位置,可选,浏览器环境忽略此参数
* @param key key
* @param initialValue 初始值
* @param options 选项
*/
const useCommonLocalStorage = <T extends string | number | boolean | object | null>(
filePath: string,
key: string,
initialValue: MaybeRefOrGetter<T>,
options: UseStorageOptions<T> = {}
): RemovableRef<any> => {
const localStorageAdaptor: StorageLike = getLocalStorageAdaptor(filePath, options)
return useStorage(key, initialValue, localStorageAdaptor, options)
}

/**
* Electron环境使用json保存数据,否则使用浏览器存储
*
* @author terwer
* @since 0.6.8
*/
const getLocalStorageAdaptor = <T>(filePath: string, options: UseStorageOptions<T> = {}): StorageLike => {
let localStorageAdaptor: StorageLike
const { isInSiyuanOrSiyuanNewWin } = useSiyuanDevice()
if (isInSiyuanOrSiyuanNewWin()) {
localStorageAdaptor = new JsonStorage(filePath)
logger.info("using JsonStorage for localStorage")
} else {
const { window = defaultWindow } = options
localStorageAdaptor = window?.localStorage
logger.info("using window.localStorage for localStorage")
}
return localStorageAdaptor
}

export default useCommonLocalStorage
4 changes: 2 additions & 2 deletions src/stores/common/useCommonStorageAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
* questions.
*/

import CommonStorage from "~/src/stores/common/commonStorage.ts"
import CommonStorageAsync from "~/src/stores/common/commonStorageAsync.ts"
import { StorageSerializers, toValue } from "@vueuse/core"
import { createAppLogger } from "~/src/utils/appLogger.ts"
import { ObjectUtil } from "zhi-common"
Expand All @@ -39,7 +39,7 @@ export const useCommonStorageAsync = <T extends string | number | boolean | obje
initialValue: T
) => {
const logger = createAppLogger("common-storage-async")
const commonStorage = new CommonStorage(storageKey)
const commonStorage = new CommonStorageAsync(storageKey)

// 获取 initialValue 类型对应的序列化器,如果不存在则使用默认序列化器
const rawInit: T = toValue(initialValue)
Expand Down
10 changes: 6 additions & 4 deletions src/stores/usePublishPreferenceSetting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,19 @@
* questions.
*/

import { RemovableRef, StorageSerializers, useLocalStorage } from "@vueuse/core"
import { RemovableRef, StorageSerializers } from "@vueuse/core"
import { PublishPreferenceCfg } from "~/src/models/publishPreferenceCfg.ts"
import { readonly } from "vue"
import {SiyuanDevice} from "zhi-device";
import {createAppLogger} from "~/src/utils/appLogger.ts";
import { SiyuanDevice } from "zhi-device"
import { createAppLogger } from "~/src/utils/appLogger.ts"
import useCommonLocalStorage from "~/src/stores/common/useCommonLocalStorage.ts"

/**
* 使用发布偏好设置的自定义钩子
*/
const usePublishPreferenceSetting = () => {
// 存储键
const filePath = "storage/syp/publish-preference-cfg.json"
const storageKey = "publish-preference-cfg"
const logger = createAppLogger("use-publish-pref")

Expand All @@ -46,7 +48,7 @@ const usePublishPreferenceSetting = () => {
*/
const getPublishPreferenceSetting = (): RemovableRef<PublishPreferenceCfg> => {
const initialValue = new PublishPreferenceCfg()
const prefConfig = useLocalStorage<PublishPreferenceCfg>(storageKey, initialValue, {
const prefConfig = useCommonLocalStorage<PublishPreferenceCfg>(filePath, storageKey, initialValue, {
serializer: StorageSerializers.object,
})

Expand Down
6 changes: 4 additions & 2 deletions src/stores/useSiyuanSetting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@
*/

import { SiyuanConfig } from "zhi-siyuan-api"
import { RemovableRef, StorageSerializers, useLocalStorage } from "@vueuse/core"
import { RemovableRef, StorageSerializers } from "@vueuse/core"
import { readonly } from "vue"
import { SiyuanDevice } from "zhi-device"
import { useSiyuanDevice } from "~/src/composables/useSiyuanDevice.ts"
import useCommonLocalStorage from "~/src/stores/common/useCommonLocalStorage.ts"

/**
* 思源笔记设置
Expand All @@ -37,6 +38,7 @@ import { useSiyuanDevice } from "~/src/composables/useSiyuanDevice.ts"
* @since 1.8.0
*/
const useSiyuanSetting = () => {
const filePath = "storage/syp/siyuan-cfg.json"
const storageKey = "siyuan-cfg"
const { isInSiyuanOrSiyuanNewWin } = useSiyuanDevice()

Expand All @@ -59,7 +61,7 @@ const useSiyuanSetting = () => {

const initialValue = new SiyuanConfig(origin ?? baseUrl, token)
initialValue.middlewareUrl = middlewareUrl
const siyuanConfig = useLocalStorage<SiyuanConfig>(storageKey, initialValue, {
const siyuanConfig = useCommonLocalStorage<SiyuanConfig>(filePath, storageKey, initialValue, {
serializer: StorageSerializers.object,
})

Expand Down

0 comments on commit 1b2608d

Please sign in to comment.