Skip to content

Commit

Permalink
perf(ui/conf): support for watching partial changes in config with `w…
Browse files Browse the repository at this point in the history
…atchConf()` (#755)
  • Loading branch information
qwqcode authored Jan 30, 2024
1 parent a7c3f1b commit 37e0091
Show file tree
Hide file tree
Showing 15 changed files with 66 additions and 42 deletions.
2 changes: 1 addition & 1 deletion ui/artalk-sidebar/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const i18n = createI18n({
// Artalk extension
Artalk.use((ctx) => {
// Sync config from artalk instance to sidebar
ctx.on('conf-loaded', (conf) => {
ctx.watchConf(['locale'], (conf) => {
if (typeof conf.locale === 'string' && conf.locale !== 'auto')
i18n.global.locale.value = conf.locale as any
})
Expand Down
25 changes: 21 additions & 4 deletions ui/artalk/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,7 @@ class Context implements ContextApi {
}

setDarkMode(darkMode: boolean|'auto'): void {
// prevent trigger 'conf-loaded' to improve performance
// this.updateConf({ ...this.conf, darkMode })
this.conf.darkMode = darkMode
this.events.trigger('dark-mode-changed', darkMode)
this.updateConf({ darkMode })
}

updateConf(nConf: Partial<ArtalkConfig>): void {
Expand All @@ -161,6 +158,26 @@ class Context implements ContextApi {
getMarked() {
return marked.getInstance()
}

watchConf<T extends (keyof ArtalkConfig)[]>(keys: T, effect: (conf: Pick<ArtalkConfig, T[number]>) => void): void {
const deepEqual = (a: any, b: any) => JSON.stringify(a) === JSON.stringify(b)
const val = () => {
const conf = this.getConf()
const res: any = {}
keys.forEach((key) => { res[key] = conf[key] })
return res
}
let lastVal = {}
this.on('conf-loaded', () => {
const newVal = val()
const isDiff = !deepEqual(lastVal, newVal)
// only trigger when specific keys changed
if (isDiff) {
lastVal = newVal
effect(newVal)
}
})
}
}

export default Context
4 changes: 2 additions & 2 deletions ui/artalk/src/list/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Paginator } from './paginator'
import ReadMorePaginator from './paginator/read-more'
import UpDownPaginator from './paginator/up-down'

function createPaginatorByConf(conf: ArtalkConfig): Paginator {
function createPaginatorByConf(conf: Pick<ArtalkConfig, 'pagination'>): Paginator {
if (conf.pagination.readMore) return new ReadMorePaginator()
return new UpDownPaginator()
}
Expand All @@ -24,7 +24,7 @@ export const initListPaginatorFunc = (ctx: ContextApi) => {
let paginator: Paginator|null = null

// Init paginator when conf loaded
ctx.on('conf-loaded', (conf) => {
ctx.watchConf(['pagination', 'locale'], (conf) => {
const list = ctx.get('list')

if (paginator) paginator.dispose() // if had been init, dispose it
Expand Down
5 changes: 2 additions & 3 deletions ui/artalk/src/plugins/dark-mode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ArtalkPlugin } from '@/types'

// Notice: Singleton mode needs to be loaded as lazy as possible,
// Notice: Singleton pattern needs to be loaded as lazy as possible,
// because the SSG application does not have a `window` context.
let darkModeMedia: MediaQueryList | undefined

Expand Down Expand Up @@ -48,9 +48,8 @@ export const DarkMode: ArtalkPlugin = (ctx) => {
}
}

ctx.watchConf(['darkMode'], (conf) => sync(conf.darkMode))
ctx.on('inited', () => sync(ctx.conf.darkMode))
ctx.on('conf-loaded', (conf) => sync(conf.darkMode))
ctx.on('dark-mode-changed', (darkMode) => sync(darkMode))
ctx.on('destroy', () => {
// if handler exists, don't forget to remove it, or it will cause memory leak
darkModeAutoHandler && darkModeMedia?.removeEventListener('change', darkModeAutoHandler)
Expand Down
11 changes: 5 additions & 6 deletions ui/artalk/src/plugins/editor-kit.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { EditorApi, ArtalkPlugin } from '@/types'
import EventManager from '@/lib/event-manager'
import { ENABLED_PLUGS, getDisabledPlugByConf } from './editor'
import { getEnabledPlugs } from './editor'
import EditorPlug from './editor/_plug'
import PlugKit from './editor/_kit'

Expand Down Expand Up @@ -43,7 +43,9 @@ export class PlugManager {
public editor: EditorApi
) {
let confLoaded = false // config not loaded at first time
this.editor.ctx.on('conf-loaded', () => {
this.editor.ctx.watchConf([
'imgUpload', 'emoticons', 'preview', 'editorTravel', 'locale'
], (conf) => {
// trigger unmount event will call all plugs' unmount function
// (this will only be called while conf reloaded, not be called at first time)
confLoaded && this.getEvents().trigger('unmounted')
Expand All @@ -52,10 +54,7 @@ export class PlugManager {
this.clear()

// init the all enabled plugs
const DISABLED = getDisabledPlugByConf(this.editor.ctx.conf)

ENABLED_PLUGS
.filter(p => !DISABLED.includes(p)) // 禁用的插件
getEnabledPlugs(conf)
.forEach((Plug) => {
// create the plug instance
const kit = new PlugKit(this)
Expand Down
23 changes: 14 additions & 9 deletions ui/artalk/src/plugins/editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Upload from './upload'
import Preview from './preview'

/** The default enabled plugs */
export const ENABLED_PLUGS: (typeof EditorPlug)[] = [
const EDITOR_PLUGS: (typeof EditorPlug)[] = [
// Core
LocalStorage,
HeaderEvent, HeaderUser, HeaderLink,
Expand All @@ -29,12 +29,17 @@ export const ENABLED_PLUGS: (typeof EditorPlug)[] = [
Emoticons, Upload, Preview
]

/** Get the name list of disabled plugs */
export function getDisabledPlugByConf(conf: ArtalkConfig): (typeof EditorPlug)[] {
return [
{k: Upload, v: conf.imgUpload},
{k: Emoticons, v: conf.emoticons},
{k: Preview, v: conf.preview},
{k: Mover, v: conf.editorTravel},
].filter(n => !n.v).flatMap(n => n.k)
/**
* Get the enabled plugs by config
*/
export function getEnabledPlugs(conf: Pick<ArtalkConfig, 'imgUpload'|'emoticons'|'preview'|'editorTravel'>): (typeof EditorPlug)[] {
// The reference map of config and plugs
// (for check if the plug is enabled)
const confRefs = new Map<typeof EditorPlug, any>()
confRefs.set(Upload, conf.imgUpload)
confRefs.set(Emoticons, conf.emoticons)
confRefs.set(Preview, conf.preview)
confRefs.set(Mover, conf.editorTravel)

return EDITOR_PLUGS.filter(p => !confRefs.has(p) || !!confRefs.get(p))
}
2 changes: 1 addition & 1 deletion ui/artalk/src/plugins/list/copyright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ArtalkPlugin } from '@/types'
import { version as ARTALK_VERSION } from '~/package.json'

export const Copyright: ArtalkPlugin = (ctx) => {
ctx.on('conf-loaded', () => {
ctx.on('inited', () => {
const list = ctx.get('list')

const $copyright = list.$el.querySelector<HTMLElement>('.atk-copyright')
Expand Down
4 changes: 2 additions & 2 deletions ui/artalk/src/plugins/list/dropdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ export const Dropdown: ArtalkPlugin = (ctx) => {
})
}

ctx.on('conf-loaded', () => {
ctx.watchConf(['listSort', 'locale'], (conf) => {
const list = ctx.get('list')

const $count = list.$el.querySelector<HTMLElement>('.atk-comment-count')
if (!$count) return

// 评论列表排序 Dropdown 下拉选择层
if (ctx.conf.listSort) {
if (conf.listSort) {
initDropdown($count)
} else {
removeDropdown({
Expand Down
2 changes: 1 addition & 1 deletion ui/artalk/src/plugins/list/sidebar-btn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const SidebarBtn: ArtalkPlugin = (ctx) => {
}
}

ctx.on('conf-loaded', () => {
ctx.watchConf(['locale'], (conf) => {
const list = ctx.get('list')

$openSidebarBtn = list.$el.querySelector<HTMLElement>('[data-action="open-sidebar"]')
Expand Down
2 changes: 1 addition & 1 deletion ui/artalk/src/plugins/list/unread-badge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const UnreadBadge: ArtalkPlugin = (ctx) => {
}
}

ctx.on('conf-loaded', () => {
ctx.on('inited', () => {
const list = ctx.get('list')

$unreadBadge = list.$el.querySelector<HTMLElement>('.atk-unread-badge')
Expand Down
14 changes: 8 additions & 6 deletions ui/artalk/src/plugins/stat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ContextApi, ArtalkPlugin } from '@/types'
import type { ContextApi, ArtalkPlugin, ArtalkConfig } from '@/types'
import { Api } from '@/api'

export interface CountOptions {
Expand All @@ -14,13 +14,15 @@ export interface CountOptions {
}

export const PvCountWidget: ArtalkPlugin = (ctx: ContextApi) => {
ctx.on('conf-loaded', () => {
ctx.watchConf([
'site', 'pageKey', 'countEl', 'pvEl',
], (conf) => {
initCountWidget({
getApi: () => ctx.getApi(),
siteName: ctx.conf.site,
pageKey: ctx.conf.pageKey,
countEl: ctx.conf.countEl,
pvEl: ctx.conf.pvEl,
siteName: conf.site,
pageKey: conf.pageKey,
countEl: conf.countEl,
pvEl: conf.pvEl,
pvAdd: true,
})
})
Expand Down
6 changes: 3 additions & 3 deletions ui/artalk/src/plugins/version-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import $t from '@/i18n'
let IgnoreVersionCheck = false

export const VersionCheck: ArtalkPlugin = (ctx) => {
ctx.on('conf-loaded', () => {
ctx.watchConf(['apiVersion', 'versionCheck'], (conf) => {
const list = ctx.get('list')
if (ctx.conf.apiVersion && ctx.conf.versionCheck && !IgnoreVersionCheck)
versionCheck(list, ARTALK_VERSION, ctx.conf.apiVersion)
if (conf.apiVersion && conf.versionCheck && !IgnoreVersionCheck)
versionCheck(list, ARTALK_VERSION, conf.apiVersion)
})
}

Expand Down
4 changes: 2 additions & 2 deletions ui/artalk/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ const services = {
i18n(ctx: ContextApi) {
I18n.setLocale(ctx.conf.locale)

ctx.on('conf-loaded', () => {
I18n.setLocale(ctx.conf.locale)
ctx.watchConf(['locale'], (conf) => {
I18n.setLocale(conf.locale)
})
},

Expand Down
3 changes: 3 additions & 0 deletions ui/artalk/src/types/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,7 @@ export interface ContextApi extends EventManagerFuncs<EventPayloadMap> {

/** 更新配置 */
updateConf(conf: Partial<ArtalkConfig>): void

/** 监听配置更新 */
watchConf<T extends (keyof ArtalkConfig)[]>(keys: T, effect: (val: Pick<ArtalkConfig, T[number]>) => void): void
}
1 change: 0 additions & 1 deletion ui/artalk/src/types/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export interface EventPayloadMap {
'editor-submitted': undefined // 编辑器提交后
'user-changed': LocalUser // 本地用户数据变更时
'conf-loaded': ArtalkConfig // Artalk 配置变更时
'dark-mode-changed': boolean|'auto' // 深色模式变更时
'sidebar-show': undefined // 侧边栏显示
'sidebar-hide': undefined // 侧边栏隐藏
}

0 comments on commit 37e0091

Please sign in to comment.