Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: more functions to handle artalk lifecycle #426

Merged
merged 1 commit into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 64 additions & 159 deletions ui/packages/artalk/src/artalk.ts
Original file line number Diff line number Diff line change
@@ -1,174 +1,101 @@
import './style/main.less'

import ArtalkConfig from '~/types/artalk-config'
import { EventPayloadMap, Handler } from '~/types/event'
import ArtalkPlug from '~/types/plug'
import Context from '~/types/context'
import type ArtalkConfig from '~/types/artalk-config'
import type { EventPayloadMap, Handler } from '~/types/event'
import type ArtalkPlug from '~/types/plug'
import type Context from '~/types/context'
import ConcreteContext from './context'
import defaults from './defaults'

import ListLite from './list/list-lite'
import CheckerLauncher from './lib/checker'
import Editor from './editor'
import List from './list'
import SidebarLayer from './layer/sidebar-layer'

import Layer from './layer'
import * as Utils from './lib/utils'
import * as Ui from './lib/ui'
import { handelBaseConf } from './config'
import Services from './service'
import * as Stat from './lib/stat'
import ListLite from './list/list-lite'

/**
* Artalk
*
* @website https://artalk.js.org
* @see https://artalk.js.org
*/
export default class Artalk {
private static instance?: Artalk

public static ListLite = ListLite
public static readonly defaults: ArtalkConfig = defaults

public conf!: ArtalkConfig
public ctx!: Context
public $root!: HTMLElement

/** Plugins (in global scope) */
protected static Plugins: ArtalkPlug[] = [ Stat.PvCountWidget ]

/** Plugins (in a instance scope) */
protected instancePlugins: ArtalkPlug[] = []
/** Plugins */
protected static plugins: ArtalkPlug[] = [ Stat.PvCountWidget ]

/** 禁用的组件 */
public static DisabledComponents: string[] = []

constructor(customConf: Partial<ArtalkConfig>) {
/* 初始化基本配置 */
this.conf = Artalk.HandelBaseConf(customConf)
/** 构造函数 */
constructor(conf: Partial<ArtalkConfig>) {
if (Artalk.instance) return Artalk.instance.update(conf)

// 初始化基本配置
this.conf = handelBaseConf(conf)
if (this.conf.el instanceof HTMLElement) this.$root = this.conf.el

/* 初始化 Context */
// 初始化 Context
this.ctx = new ConcreteContext(this.conf, this.$root)

/* 初始化组件 */
this.initComponents()
}

/** 组件初始化 */
private initComponents() {
this.initLocale()
this.initLayer()
Utils.initMarked(this.ctx)

const Components: { [name: string]: () => void } = {
// CheckerLauncher
checkerLauncher: () => {
const checkerLauncher = new CheckerLauncher(this.ctx)
this.ctx.setCheckerLauncher(checkerLauncher)
},

// 编辑器
editor: () => {
const editor = new Editor(this.ctx)
this.ctx.setEditor(editor)
this.$root.appendChild(editor.$el)
},

// 评论列表
list: () => {
// 评论列表
const list = new List(this.ctx)
this.ctx.setList(list)
this.$root.appendChild(list.$el)

// 评论获取
list.fetchComments(0)
},

// 侧边栏 Layer
sidebarLayer: () => {
const sidebarLayer = new SidebarLayer(this.ctx)
this.ctx.setSidebarLayer(sidebarLayer)
},

// 默认事件绑定
eventsDefault: () => {
this.initEventBind()
}
}

// 组件初始化
Object.entries(Components).forEach(([name, initComponent]) => {
// 内建服务初始化
Object.entries(Services).forEach(([name, initService]) => {
if (Artalk.DisabledComponents.includes(name)) return

initComponent()
initService(this.ctx)
})

// 插件初始化 (global scope)
Artalk.Plugins.forEach(plugin => {
Artalk.plugins.forEach(plugin => {
if (typeof plugin === 'function')
plugin(this.ctx)
})
}

/** 基本配置初始化 */
public static HandelBaseConf(customConf: Partial<ArtalkConfig>): ArtalkConfig {
// 合并默认配置
const conf: ArtalkConfig = Utils.mergeDeep(Artalk.defaults, customConf)

// 绑定元素
if (typeof conf.el === 'string' && !!conf.el) {
try {
const findEl = document.querySelector<HTMLElement>(conf.el)
if (!findEl) throw Error(`Target element "${conf.el}" was not found.`)
conf.el = findEl
} catch (e) {
console.error(e)
throw new Error('Please check your Artalk `el` config.')
}
}

// 服务器配置
conf.server = conf.server.replace(/\/$/, '').replace(/\/api\/?$/, '')

// 默认 pageKey
if (!conf.pageKey) {
// @link http://bl.ocks.org/abernier/3070589
conf.pageKey = `${window.location.pathname}`
}

// 默认 pageTitle
if (!conf.pageTitle) {
conf.pageTitle = `${document.title}`
}

return conf
/** Init Artalk (单例模式) */
public static init(conf: Partial<ArtalkConfig>): Artalk {
if (this.instance) return this.instance.update(conf)
this.instance = new Artalk(conf)
return this.instance
}

/** 事件绑定初始化 */
private initEventBind() {
// 锚点快速跳转评论
window.addEventListener('hashchange', () => {
this.ctx.listHashGotoCheck()
})
/** 设置暗黑模式 */
public setDarkMode(darkMode: boolean) {
this.ctx.setDarkMode(darkMode)
}

// 本地用户数据变更
this.ctx.on('user-changed', () => {
this.ctx.checkAdminShowEl()
this.ctx.listRefreshUI()
})
/** Use Plugin (plugin will be called in instance `use` func) */
public use(plugin: ArtalkPlug) {
Artalk.plugins.push(plugin)
if (typeof plugin === 'function') plugin(this.ctx)
}

/** Use Plugin (static method) */
public static use(plugin: ArtalkPlug) {
this.plugins.push(plugin)
}

/** Update config of Artalk */
public update(conf: Partial<ArtalkConfig>) {
if (!Artalk.instance) throw Error('cannot call `update` function before call `load`')
Artalk.instance.ctx.updateConf(conf)
return Artalk.instance
}

/** 语言初始化 */
private initLocale() {
if (this.conf.locale === 'auto') { // 自适应语言
this.conf.locale = navigator.language
}
/** Reload comment list of Artalk */
public reload() {
this.ctx.listReload()
}

/** Layer 初始化 */
private initLayer() {
// 记录页面原始 Styles
Layer.BodyOrgOverflow = document.body.style.overflow
Layer.BodyOrgPaddingRight = document.body.style.paddingRight
/** Destroy instance of Artalk */
public destroy() {
if (!Artalk.instance) throw Error('cannot call `destroy` function before call `load`')
//...
delete Artalk.instance
}

/** 监听事件 */
Expand All @@ -186,37 +113,15 @@ export default class Artalk {
this.ctx.trigger(name, payload, 'external')
}

/** 重新加载 */
public reload() {
this.ctx.listReload()
}

/** 设置暗黑模式 */
public setDarkMode(darkMode: boolean) {
this.ctx.setDarkMode(darkMode)
}

/** Use Plugin (specific instance) */
public use(plugin: ArtalkPlug) {
this.instancePlugins.push(plugin)
if (typeof plugin === 'function') plugin(this.ctx)
}

/** Use Plugin (static method for global scope) */
public static use(plugin: ArtalkPlug) {
this.Plugins.push(plugin)
}

/** @deprecated Please replace it with lowercase function name `use(...)`. */
public static Use(plugin: ArtalkPlug) {
this.use(plugin)
console.warn('`Use(...)` is deprecated, replace it with lowercase `use(...)`.')
}

/** 装载数量统计元素 */
public static LoadCountWidget(customConf: Partial<ArtalkConfig>) {
const conf = this.HandelBaseConf(customConf)
const ctx = new ConcreteContext(conf)
public static loadCountWidget(conf: Partial<ArtalkConfig>) {
const ctx = new ConcreteContext(handelBaseConf(conf))
Stat.initCountWidget({ ctx, pvAdd: false })
}

/** @deprecated Please use `loadCountWidget` instead */
public static LoadCountWidget(conf: Partial<ArtalkConfig>) {
console.warn('The method `LoadCountWidget` is deprecated, please use `loadCountWidget` instead.')
this.loadCountWidget(conf)
}
}
3 changes: 2 additions & 1 deletion ui/packages/artalk/src/comment/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CommentData } from '~/types/artalk-data'
import Context from '~/types/context'
import Component from '../lib/component'
import * as Utils from '../lib/utils'
import marked from '../lib/marked'
import UADetect from '../lib/detect'
import CommentRender from './render'
import CommentActions from './actions'
Expand Down Expand Up @@ -164,7 +165,7 @@ export default class Comment extends Component {

/** 获取评论 markdown 解析后的内容 */
public getContentMarked() {
return Utils.marked(this.ctx, this.data.content)
return marked(this.ctx, this.data.content)
}

/** 获取格式化后的日期 */
Expand Down
3 changes: 2 additions & 1 deletion ui/packages/artalk/src/comment/render.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as Utils from '../lib/utils'
import * as Ui from '../lib/ui'
import marked from '../lib/marked'
import ActionBtn from '../components/action-btn'
import CommentHTML from './comment.html?raw'
import Comment from './comment'
Expand Down Expand Up @@ -194,7 +195,7 @@ export default class CommentRender {
const $nick = this.$replyTo.querySelector<HTMLElement>('.atk-nick')!
$nick.innerText = `@${this.cConf.replyTo.nick}`
$nick.onclick = () => { this.comment.getActions().goToReplyComment() }
let replyContent = Utils.marked(this.ctx, this.cConf.replyTo.content)
let replyContent = marked(this.ctx, this.cConf.replyTo.content)
if (this.cConf.replyTo.is_collapsed) replyContent = `[${this.ctx.$t('collapsed')}]`
this.$replyTo.querySelector<HTMLElement>('.atk-content')!.innerHTML = replyContent
this.$body.prepend(this.$replyTo)
Expand Down
44 changes: 44 additions & 0 deletions ui/packages/artalk/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type ArtalkConfig from '~/types/artalk-config'
import * as Utils from './lib/utils'
import Defaults from './defaults'

export default { handelBaseConf }

/** 基本配置初始化 */
export function handelBaseConf(customConf: Partial<ArtalkConfig>): ArtalkConfig {
// 合并默认配置
const conf: ArtalkConfig = Utils.mergeDeep(Defaults, customConf)

// 绑定元素
if (typeof conf.el === 'string' && !!conf.el) {
try {
const findEl = document.querySelector<HTMLElement>(conf.el)
if (!findEl) throw Error(`Target element "${conf.el}" was not found.`)
conf.el = findEl
} catch (e) {
console.error(e)
throw new Error('Please check your Artalk `el` config.')
}
}

// 服务器配置
conf.server = conf.server.replace(/\/$/, '').replace(/\/api\/?$/, '')

// 默认 pageKey
if (!conf.pageKey) {
// @link http://bl.ocks.org/abernier/3070589
conf.pageKey = `${window.location.pathname}`
}

// 默认 pageTitle
if (!conf.pageTitle) {
conf.pageTitle = `${document.title}`
}

// 自适应语言
if (conf.locale === 'auto') {
conf.locale = navigator.language
}

return conf
}
Loading