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

fix(editor): fix duplicate event binding when update conf #605

Merged
merged 1 commit into from
Oct 12, 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
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