Skip to content

Commit

Permalink
fix(editor): fix duplicate event binding when update conf (#605)
Browse files Browse the repository at this point in the history
  • Loading branch information
qwqcode committed Oct 12, 2023
1 parent 2dc3bc8 commit 25a4f48
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 34 deletions.
4 changes: 3 additions & 1 deletion ui/packages/artalk/src/editor/edit-mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ function cancelEdit(m: EditModeManager) {
}

function initEditModeSubmit(m: EditModeManager) {
m.editor.getSubmitManager()!.registerCustom({
const submitManger = m.editor.getSubmitManager()
if (!submitManger) throw Error("submitManager not initialized")
submitManger.registerCustom({
activeCond: () => !!m.comment, // active this custom submit when edit mode
req: async () => {
const saveData = {
Expand Down
12 changes: 10 additions & 2 deletions ui/packages/artalk/src/editor/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ export default class Editor extends Component {
public setSubmitManager(m: SubmitManager) { this.submitManager = m }
public getSubmitManager() { return this.submitManager }

/** 已加载功能的 unmount 函数 */
private unmountFuncs: (() => void)[] = []

public constructor(ctx: Context) {
super(ctx)

Expand All @@ -52,7 +55,12 @@ export default class Editor extends Component {

// event listen
this.ctx.on('conf-loaded', () => {
initEditorFuncs(this) // init editor funcs
// call unmount funcs
// (this will only be called while conf reloaded, not be called at first time)
this.unmountFuncs?.forEach((unmount) => !!unmount && unmount())

// call init will return unmount funcs and save it
this.unmountFuncs = initEditorFuncs(this) // init editor funcs
})
}

Expand Down Expand Up @@ -170,7 +178,7 @@ export default class Editor extends Component {

/** 点击评论提交按钮事件 */
public async submit() {
if (!this.submitManager) return
if (!this.submitManager) throw Error('submitManger not initialized')
await this.submitManager.do()
}

Expand Down
113 changes: 83 additions & 30 deletions ui/packages/artalk/src/editor/funcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,92 +6,145 @@ import { createReplyManager } from './reply'
import { createEditModeManager } from './edit-mode'
import { createSubmitManager } from './submit'

/** Editor Enabled Functions */
const EditorFuncs = {
localStorage, header, textarea, submitBtn, submitManager,
plugs, mover, replyManager, editModeManager,
localStorage, header, textarea,
submitBtn, submitManager,
mover, reply,
plugs,
}

export default function initEditorFuncs(editor: Editor) {
// storage the unmount funcs
const unmountFuncs: (() => void)[] = []

Object.entries(EditorFuncs).forEach(([k, init]) => {
init(editor)
const unmountFunc = init(editor)
if (unmountFunc) unmountFuncs.push(unmountFunc)
})

return unmountFuncs
}

// ================== Editor Functions ======================

function localStorage(editor: Editor) {
// load editor content from localStorage when init
const localContent = window.localStorage.getItem('ArtalkContent') || ''
if (localContent.trim() !== '') {
editor.showNotify(editor.$t('restoredMsg'), 'i')
editor.setContent(localContent)
}

editor.getUI().$textarea.addEventListener('input', () => (editor.saveToLocalStorage()))
// save editor content to localStorage when input
const onEditorInput = () => {
editor.saveToLocalStorage()
}

// bind the event
editor.getUI().$textarea.addEventListener('input', onEditorInput)

return () => {
// unmount the event
editor.getUI().$textarea.removeEventListener('input', onEditorInput)
}
}

function header(editor: Editor) {
const $inputs = editor.getHeaderInputEls()
Object.entries($inputs).forEach(([key, $input]) => {
$input.value = User.data[key] || ''
$input.addEventListener('input', () => onHeaderInput(editor, key, $input))
const inputFuncs: {[name: string]: () => void} = {}

// the input event
const onInput = ($input: HTMLInputElement, key: string) => () => {
if (editor.isEditMode) return // 评论编辑模式,不修改个人信息

User.update({ [key]: $input.value.trim() })
editor.getPlugs()?.triggerHeaderInputEvt(key, $input)
}

// 设置 Placeholder
// set placeholder and sync header input value
Object.entries($inputs).forEach(([key, $input]) => {
$input.placeholder = `${editor.$t(key as any)}`
$input.value = User.data[key] || ''
})
}

function onHeaderInput(editor: Editor, field: string, $input: HTMLInputElement) {
if (editor.isEditMode) return // 评论编辑模式,不修改个人信息

User.update({
[field]: $input.value.trim()
// bind the event
Object.entries($inputs).forEach(([key, $input]) => {
$input.addEventListener('input', inputFuncs[key] = onInput($input, key))
})

editor.getPlugs()?.triggerHeaderInputEvt(field, $input)
return () => {
// unmount the event
Object.entries($inputs).forEach(([key, $input]) => {
$input.removeEventListener('input', inputFuncs[key])
})
}
}

function textarea(editor: Editor) {
// 占位符
editor.getUI().$textarea.placeholder = editor.ctx.conf.placeholder || editor.$t('placeholder')

// 修复按下 Tab 输入的内容
editor.getUI().$textarea.addEventListener('keydown', (e) => {
// 按下 Tab 输入内容,而不是失去焦距
const onKeydown = (e: KeyboardEvent) => {
const keyCode = e.keyCode || e.which

if (keyCode === 9) {
e.preventDefault()
editor.insertContent('\t')
}
})
}

// 输入框高度随内容而变化
editor.getUI().$textarea.addEventListener('input', () => {
const onInput = () => {
editor.adjustTextareaHeight()
})
}

// bind the event
editor.getUI().$textarea.addEventListener('keydown', onKeydown)
editor.getUI().$textarea.addEventListener('input', onInput)

return () => {
// unmount the event
editor.getUI().$textarea.removeEventListener('keydown', onKeydown)
editor.getUI().$textarea.removeEventListener('input', onInput)
}
}

function submitBtn(editor: Editor) {
editor.refreshSendBtnText()
editor.getUI().$submitBtn.addEventListener('click', () => (editor.submit()))

const onClick = () => {
editor.submit()
}

editor.getUI().$submitBtn.addEventListener('click', onClick)

return () => {
editor.getUI().$submitBtn.removeEventListener('click', onClick)
}
}

function plugs(editor: Editor) {
editor.setPlugs(createPlugManager(editor))
function submitManager(editor: Editor) {
editor.setSubmitManager(createSubmitManager(editor))

// Note:
// the EditModeManger depends on the submitManager,
// so must init the submitManager first.
// createEditModeManager will make a side-effect on submitManager (call `submitManger.registerCustom`)
// side-effect is not a good practice, to be improved.
editor.setEditModeManager(createEditModeManager(editor))
}

function mover(editor: Editor) {
if (!editor.conf.editorTravel) return
editor.setMover(createMover(editor))
}

function replyManager(editor: Editor) {
function reply(editor: Editor) {
editor.setReplyManager(createReplyManager(editor))
}

function editModeManager(editor: Editor) {
editor.setEditModeManager(createEditModeManager(editor))
}

function submitManager(editor: Editor) {
editor.setSubmitManager(createSubmitManager(editor))
function plugs(editor: Editor) {
editor.setPlugs(createPlugManager(editor))
}
2 changes: 1 addition & 1 deletion ui/packages/artalk/src/editor/plug-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function createPlugManager(editor: Editor): PlugManager {
getTransformedContent: (r) => getTransformedContent(ctx, r),
}

// handle ui
// handle ui, clear and reset the plug btns and plug panels
editor.getUI().$plugPanelWrap.innerHTML = ''
editor.getUI().$plugPanelWrap.style.display = 'none'
editor.getUI().$plugBtnWrap.innerHTML = ''
Expand Down

0 comments on commit 25a4f48

Please sign in to comment.