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

refactor(ui/plugin): further divide functionality into plugins #615

Merged
merged 21 commits into from
Oct 20, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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