Skip to content

Commit

Permalink
refactor(ui/plugin): further divide functionality into plugins (#615)
Browse files Browse the repository at this point in the history
* move stat to plugins folder

* move editor plugs to common plugins folder

* implement list-close-editor standalone plugin

* impl StateManager for Loose Coupling of Reply, Edit, and Submit Components

* rename

* remove Mover dep in editor edit state plugin

* rename editor state plugins

* refactor header plugin of editor

* separate header plugins

* add and remove event binding

* fix plugin btn ui refresh when conf updating

* rename editor plugins and remove plug classname suffix

* editor kit reload plugin when conf-loaded

* impl clear method in editor-kit class

* separate version-check from list

* separate unread from list

* make context maintain globally accessible `page` data

* rename plug to plugin

* wrap the options of List up

* separate functionalities of list

* rename
  • Loading branch information
qwqcode committed Oct 20, 2023
1 parent 0f35532 commit e6a9748
Show file tree
Hide file tree
Showing 71 changed files with 1,839 additions and 1,472 deletions.
1 change: 1 addition & 0 deletions ui/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports = {
'no-lonely-if': 0,
'prefer-destructuring': 0,
'import/no-cycle': 0,
'import/prefer-default-export': 0,
'@typescript-eslint/lines-between-class-members': 0,
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-use-before-define': 0,
Expand Down
41 changes: 22 additions & 19 deletions ui/packages/artalk-sidebar/src/pages/comments.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,27 +41,30 @@ onMounted(() => {
})
// 初始化评论列表
const list = new Artalk.ListLite(artalk!.ctx)
const list = new Artalk.List(artalk!.ctx, {
liteMode: true,
flatMode: true,
unreadHighlight: true,
scrollListenerAt: wrapEl.value,
pageMode: 'pagination',
// pageSize: 20 // TODO consider fixed pageSize value in sidebar
noCommentText: `<div class="atk-sidebar-no-content">${t('noContent')}</div>`,
renderComment: (comment) => {
const pageURL = comment.getData().page_url
comment.getRender().setOpenURL(`${pageURL}#atk-comment-${comment.getID()}`)
comment.getConf().onReplyBtnClick = () => {
artalk!.ctx.replyComment(comment.getData(), comment.getEl())
}
},
paramsEditor: (params) => {
params.type = curtTab.value // 列表数据类型
params.site_name = curtSite.value // 站点名
if (search.value) params.search = search.value
}
})
artalk!.ctx.inject('list', list)
list.flatMode = true
list.unreadHighlight = true
list.scrollListenerAt = wrapEl.value
list.pageMode = 'pagination'
// list.pageSize = 20 // TODO consider fixed pageSize value in sidebar
list.noCommentText = `<div class="atk-sidebar-no-content">${t('noContent')}</div>`
list.renderComment = (comment) => {
const pageURL = comment.getData().page_url
comment.getRender().setOpenURL(`${pageURL}#atk-comment-${comment.getID()}`)
comment.getConf().onReplyBtnClick = () => {
artalk!.ctx.replyComment(comment.getData(), comment.getEl(), true)
}
}
list.paramsEditor = (params) => {
params.type = curtTab.value // 列表数据类型
params.site_name = curtSite.value // 站点名
if (search.value) params.search = search.value
}
artalk!.on('list-inserted', (data) => {
wrapEl.value!.scrollTo(0, 0)
})
Expand Down
18 changes: 11 additions & 7 deletions ui/packages/artalk/src/artalk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import './style/main.scss'

import type ArtalkConfig from '~/types/artalk-config'
import type { EventPayloadMap } from '~/types/event'
import type ArtalkPlug from '~/types/plug'
import type ArtalkPlugin from '~/types/plugin'
import type Context from '~/types/context'
import type { EventHandler } from './lib/event-manager'
import ConcreteContext from './context'
import defaults from './defaults'
import { handelBaseConf } from './config'
import Services from './service'
import * as Stat from './lib/stat'
import ListLite from './list/list-lite'
import { DefaultPlugins } from './plugins'
import * as Stat from './plugins/stat'
import List from './list/list'
import Api from './api'

/**
Expand All @@ -21,15 +22,15 @@ import Api from './api'
export default class Artalk {
private static instance?: Artalk

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

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

/** Plugins */
protected static plugins: ArtalkPlug[] = [ Stat.PvCountWidget ]
protected static plugins: ArtalkPlugin[] = [ ...DefaultPlugins ]
public static DisabledComponents: string[] = []

constructor(conf: Partial<ArtalkConfig>) {
Expand All @@ -56,6 +57,8 @@ export default class Artalk {
if (typeof plugin === 'function')
plugin(this.ctx)
})

this.ctx.trigger('inited')
}

/** Init Artalk */
Expand All @@ -66,7 +69,7 @@ export default class Artalk {
}

/** Use Plugin (plugin will be called in instance `use` func) */
public use(plugin: ArtalkPlug) {
public use(plugin: ArtalkPlugin) {
Artalk.plugins.push(plugin)
if (typeof plugin === 'function') plugin(this.ctx)
}
Expand All @@ -86,6 +89,7 @@ export default class Artalk {
/** Destroy instance of Artalk */
public destroy() {
if (!Artalk.instance) throw Error('cannot call `destroy` function before call `load`')
this.ctx.trigger('destroy')
Artalk.instance.$root.remove()
delete Artalk.instance
}
Expand Down Expand Up @@ -115,7 +119,7 @@ export default class Artalk {
// ===========================

/** Use Plugin (static method) */
public static use(plugin: ArtalkPlug) {
public static use(plugin: ArtalkPlugin) {
this.plugins.push(plugin)
if (this.instance && typeof plugin === 'function') plugin(this.instance.ctx)
}
Expand Down
3 changes: 0 additions & 3 deletions ui/packages/artalk/src/comment/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ export default class CommentActions {

// 刷新当前 Comment UI
this.comment.setData(data)

// 刷新 List UI
this.ctx.listRefreshUI()
}).catch((err) => {
console.error(err)
btnElem.setError(this.ctx.$t('editFail'))
Expand Down
6 changes: 3 additions & 3 deletions ui/packages/artalk/src/comment/renders/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ export default function renderContent(ctx: RenderCtx) {
</div>`)
ctx.$body.insertAdjacentElement('beforeend', collapsedInfoEl)

const contentShowBtn = collapsedInfoEl.querySelector('.atk-show-btn')!
const contentShowBtn = collapsedInfoEl.querySelector<HTMLElement>('.atk-show-btn')!
contentShowBtn.addEventListener('click', (e) => {
e.stopPropagation() // 防止穿透

if (ctx.$content.classList.contains('atk-hide')) {
ctx.$content.innerHTML = ctx.comment.getContentMarked()
ctx.$content.classList.remove('atk-hide')
Ui.playFadeInAnim(ctx.$content)
contentShowBtn.innerHTML = ctx.ctx.$t('collapse')
contentShowBtn.innerText = ctx.ctx.$t('collapse')
} else {
ctx.$content.innerHTML = ''
ctx.$content.classList.add('atk-hide')
contentShowBtn.innerHTML = ctx.ctx.$t('expand')
contentShowBtn.innerText = ctx.ctx.$t('expand')
}
})
}
2 changes: 1 addition & 1 deletion ui/packages/artalk/src/comment/renders/reply-to.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function renderReplyTo(ctx: RenderCtx) {
$nick.innerText = `@${ctx.cConf.replyTo.nick}`
$nick.onclick = () => { ctx.comment.getActions().goToReplyComment() }
let replyContent = marked(ctx.ctx, ctx.cConf.replyTo.content)
if (ctx.cConf.replyTo.is_collapsed) replyContent = `[${ctx.ctx.$t('collapsed')}]`
if (ctx.cConf.replyTo.is_collapsed) replyContent = `[${Utils.htmlEncode(ctx.ctx.$t('collapsed'))}]`
ctx.$replyTo.querySelector<HTMLElement>('.atk-content')!.innerHTML = replyContent
ctx.$body.prepend(ctx.$replyTo)
}
88 changes: 30 additions & 58 deletions ui/packages/artalk/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type ArtalkConfig from '~/types/artalk-config'
import type { CommentData, NotifyData } from '~/types/artalk-data'
import type { CommentData, NotifyData, PageData } from '~/types/artalk-data'
import type { EventPayloadMap } from '~/types/event'
import type ContextApi from '~/types/context'
import type { TInjectedServices } from './service'
Expand All @@ -13,8 +13,6 @@ import * as I18n from './i18n'
import { getLayerWrap } from './layer'
import { SidebarShowPayload } from './layer/sidebar-layer'
import Comment from './comment'
import Api from './api'
import List from './list'
import EventManager from './lib/event-manager'

// Auto dependency injection
Expand All @@ -30,6 +28,8 @@ class Context implements ContextApi {
public markedReplacers: ((raw: string) => string)[] = []

private commentList: Comment[] = [] // Note: 无层级结构 + 无须排列
private page?: PageData
private unreadList: NotifyData[] = []

/* Event Manager */
private events = new EventManager<EventPayloadMap>()
Expand Down Expand Up @@ -59,6 +59,10 @@ class Context implements ContextApi {
return this.commentList
}

public clearCommentList() {
this.commentList = []
}

public getCommentDataList() {
return this.commentList.map(c => c.getData())
}
Expand All @@ -67,32 +71,12 @@ class Context implements ContextApi {
return this.commentList.find(c => c.getData().id === id)
}

public deleteComment(_comment: number|Comment) {
let comment: Comment
if (typeof _comment === 'number') {
const findComment = this.findComment(_comment)
if (!findComment) throw Error(`Comment ${_comment} cannot be found`)
comment = findComment
} else comment = _comment

comment.getEl().remove()
this.commentList.splice(this.commentList.indexOf(comment), 1)

if (this.list) {
const listData = this.list.getData()
if (listData) listData.total -= 1 // 评论数减 1

this.list.refreshUI()
}
public deleteComment(id: number) {
this.list?.deleteComment(id)
}

public clearAllComments() {
if (this.list) {
this.list.getCommentsWrapEl().innerHTML = ''
this.list.clearData()
}

this.commentList = []
this.list?.clearAllComments()
}

public insertComment(commentData: CommentData) {
Expand All @@ -103,24 +87,32 @@ class Context implements ContextApi {
this.list?.updateComment(commentData)
}

public replyComment(commentData: CommentData, $comment: HTMLElement, scroll?: boolean): void {
this.editor.setReply(commentData, $comment, scroll)
}

public cancelReplyComment(): void {
this.editor.cancelReply()
public replyComment(commentData: CommentData, $comment: HTMLElement): void {
this.editor.setReply(commentData, $comment)
}

public editComment(commentData: CommentData, $comment: HTMLElement): void {
this.editor.setEditComment(commentData, $comment)
}

public cancelEditComment(): void {
this.editor.cancelEditComment()
/** 未读通知 */
public getUnreadList() {
return this.unreadList
}

public updateUnreadList(notifies: NotifyData[]): void {
this.unreadList = notifies
this.trigger('unread-updated', notifies)
}

public updateNotifies(notifies: NotifyData[]): void {
this.list?.updateUnread(notifies)
/** 页面数据 */
getPage(): PageData|undefined {
return this.page
}

updatePage(pageData: PageData): void {
this.page = pageData
this.trigger('page-loaded', pageData)
}

/* 评论列表 */
Expand All @@ -132,27 +124,7 @@ class Context implements ContextApi {
this.listReload()
}

public listRefreshUI(): void {
this.list?.refreshUI()
}

public listHashGotoCheck(): void {
if (!this.list || !(this.list instanceof List)) return
const list = this.list as List

list.goToCommentDelay = false
list.checkGoToCommentByUrlHash()
}

/* 编辑器 */
public editorOpen(): void {
this.editor.open()
}

public editorClose(): void {
this.editor.close()
}

public editorShowLoading(): void {
this.editor.showLoading()
}
Expand All @@ -165,8 +137,8 @@ class Context implements ContextApi {
this.editor.showNotify(msg, type)
}

public editorResetUI(): void {
this.editor.resetUI()
public editorResetState(): void {
this.editor.resetState()
}

/* 侧边栏 */
Expand Down

0 comments on commit e6a9748

Please sign in to comment.