From 4c8378c3241b77bcccabb9fd43ad26513636a3d9 Mon Sep 17 00:00:00 2001 From: Shigma Date: Mon, 29 Jan 2024 22:36:46 +0800 Subject: [PATCH] feat(client): support schema-respected useConfig --- packages/client/client/config.ts | 12 ++++--- packages/client/client/context.ts | 56 +++++++++++++++++++++++-------- packages/client/client/index.ts | 4 ++- 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/packages/client/client/config.ts b/packages/client/client/config.ts index 2b345255..8ac7ec30 100644 --- a/packages/client/client/config.ts +++ b/packages/client/client/config.ts @@ -1,5 +1,5 @@ import { RemovableRef, useLocalStorage, usePreferredDark } from '@vueuse/core' -import { computed, reactive, watchEffect } from 'vue' +import { computed, reactive, ref, watchEffect } from 'vue' import { Config } from '.' export let useStorage = (key: string, version: number, fallback?: () => T): RemovableRef => { @@ -32,7 +32,7 @@ export function createStorage(key: string, version: number, fa return reactive(storage.value['data']) } -export const config = useStorage('config', undefined, () => ({ +export const rawConfig = useStorage('config', undefined, () => ({ theme: { mode: 'auto', dark: 'default-dark', @@ -44,17 +44,19 @@ export const config = useStorage('config', undefined, () => ({ const preferDark = usePreferredDark() const mode = computed(() => { - const mode = config.value.theme.mode + const mode = rawConfig.value.theme.mode if (mode !== 'auto') return mode return preferDark.value ? 'dark' : 'light' }) -export const useConfig = () => config +export const resolvedConfig = ref(rawConfig.value) + +export const useConfig = (raw = false) => raw ? rawConfig : resolvedConfig export const useColorMode = () => mode watchEffect(() => { const root = window.document.querySelector('html') - root.setAttribute('theme', config.value.theme[mode.value]) + root.setAttribute('theme', rawConfig.value.theme[mode.value]) if (mode.value === 'dark') { root.classList.add('dark') } else { diff --git a/packages/client/client/context.ts b/packages/client/client/context.ts index cda9c7f8..c312a8a9 100644 --- a/packages/client/client/context.ts +++ b/packages/client/client/context.ts @@ -2,18 +2,17 @@ import * as cordis from 'cordis' import { Schema, SchemaBase } from '@koishijs/components' import { Dict, Intersect, remove } from 'cosmokit' import { - App, Component, createApp, defineComponent, h, inject, markRaw, MaybeRefOrGetter, - onBeforeUnmount, provide, reactive, Ref, resolveComponent, shallowReactive, toValue, + App, Component, computed, createApp, defineComponent, h, inject, markRaw, MaybeRefOrGetter, + onBeforeUnmount, provide, reactive, Ref, resolveComponent, shallowReactive, toValue, watch, } from 'vue' import { activities, Activity } from './activity' import { SlotOptions } from './components' -import { useColorMode, useConfig } from './config' +import { rawConfig, resolvedConfig, useColorMode } from './config' import { extensions, LoadResult } from './loader' import { ActionContext } from '.' export const Service = cordis.Service -const config = useConfig() const mode = useColorMode() // layout api @@ -194,6 +193,35 @@ export class Context extends cordis.Context { action.action(this.internal.createScope()) } }) + + const schema = computed(() => { + const list: Schema[] = [] + for (const settings of Object.values(this.internal.settings)) { + for (const options of settings) { + if (options.schema) { + list.push(options.schema) + } + } + } + return Schema.intersect(list) + }) + + const doWatch = () => watch(resolvedConfig, (value) => { + console.debug('config', value) + rawConfig.value = schema.value.simplify(value) + }, { deep: true }) + + let stop = doWatch() + + watch(schema, () => { + stop?.() + try { + resolvedConfig.value = schema.value(rawConfig.value, { autofix: true }) + } catch (error) { + console.error(error) + } + stop = doWatch() + }) } wrapComponent(component: Component) { @@ -261,16 +289,16 @@ export class Context extends cordis.Context { markRaw(options) options.order ??= 0 options.component = this.wrapComponent(options.component) - const list = this.internal.settings[options.id] ||= [] - insert(list, options) - if (options.schema) { - try { - options.schema(config.value, { autofix: true }) - } catch (error) { - console.error(error) + return this.effect(() => { + const list = this.internal.settings[options.id] ||= [] + insert(list, options) + return () => { + remove(list, options) + if (!list.length) { + delete this.internal.settings[options.id] + } } - } - return this.scope.collect('settings', () => remove(list, options)) + }) } theme(options: ThemeOptions) { @@ -279,7 +307,7 @@ export class Context extends cordis.Context { for (const [type, component] of Object.entries(options.components || {})) { this.slot({ type, - disabled: () => config.value.theme[mode.value] !== options.id, + disabled: () => resolvedConfig.value.theme[mode.value] !== options.id, component, }) } diff --git a/packages/client/client/index.ts b/packages/client/client/index.ts index 22afd6a6..42b68484 100644 --- a/packages/client/client/index.ts +++ b/packages/client/client/index.ts @@ -3,7 +3,7 @@ import { global } from './data' import install, { Dict } from './components' import Overlay from './components/chat/overlay.vue' import { redirectTo } from './activity' -import { config } from './config' +import { useConfig } from './config' import { initTask } from './loader' import { Context, routeCache } from './context' import { createI18n } from 'vue-i18n' @@ -66,6 +66,8 @@ export const i18n = createI18n({ fallbackLocale: 'zh-CN', }) +const config = useConfig() + watchEffect(() => { i18n.global.locale.value = config.value.locale })