diff --git a/packages/comment-widget/src/base-form.ts b/packages/comment-widget/src/base-form.ts index f7e31c3..dcdaaad 100644 --- a/packages/comment-widget/src/base-form.ts +++ b/packages/comment-widget/src/base-form.ts @@ -496,6 +496,14 @@ export class BaseForm extends LitElement { ` )} + ${when( + this.allowNotificationConfigurable, + () => html`
+ + +
` + )} + ${when( this.showCaptcha && !this.useTurnstile && @@ -567,6 +575,10 @@ export class BaseForm extends LitElement { `; } + private get allowNotificationConfigurable() { + return this.configMapData?.basic.allowNotificationConfigurable === true; + } + private get canUploadImages() { if (!this.configMapData?.editor?.enableUpload) { return false; @@ -589,6 +601,9 @@ export class BaseForm extends LitElement { waitUntil: (submission: Promise) => submissions.push(submission), content, + allowNotification: this.allowNotificationConfigurable + ? data.allowNotification === 'on' + : true, uploadIds: uploadedIds(editor), uploadSession: uploadSession(editor), hidden: data.hidden === 'on', diff --git a/packages/comment-widget/src/comment-form.ts b/packages/comment-widget/src/comment-form.ts index 57e8551..b765c02 100644 --- a/packages/comment-widget/src/comment-form.ts +++ b/packages/comment-widget/src/comment-form.ts @@ -70,13 +70,12 @@ export class CommentForm extends LitElement { onSubmit(e: SubmissionEvent) { const data = e.detail; - const { content, hidden } = data || {}; + const { content, hidden, allowNotification } = data || {}; const commentRequest: CommentRequest = { raw: content, content: content, - // TODO: support user input - allowNotification: true, + allowNotification: allowNotification, hidden: hidden || false, subjectRef: { group: this.group, diff --git a/packages/comment-widget/src/generated/locales/es.ts b/packages/comment-widget/src/generated/locales/es.ts index f848c2b..374f5e9 100644 --- a/packages/comment-widget/src/generated/locales/es.ts +++ b/packages/comment-widget/src/generated/locales/es.ts @@ -21,6 +21,7 @@ 's107ccef507b51f2c': `Completa la verificación`, 's15e33945d13c5176': `Se está confirmando tu envío anterior. Inténtalo más tarde; se conservarán tus imágenes.`, 's176fe95698857ad9': `El comentario o la respuesta ha cambiado. Copia tu borrador y vuelve a abrir el editor para cargar la última versión.`, +'s17c8f8524e36987d': `Permitir notificación`, 's19172ed3838854fc': `Copiar enlace`, 's1c6fefb092506753': `Error al cargar la lista de comentarios, por favor intente más tarde`, 's1d468f888124a55e': `Tu borrador ha cambiado. Vuelve a abrirlo antes de reintentar.`, diff --git a/packages/comment-widget/src/generated/locales/zh-CN.ts b/packages/comment-widget/src/generated/locales/zh-CN.ts index 6918843..d365136 100644 --- a/packages/comment-widget/src/generated/locales/zh-CN.ts +++ b/packages/comment-widget/src/generated/locales/zh-CN.ts @@ -21,6 +21,7 @@ 's107ccef507b51f2c': `请完成人机验证`, 's15e33945d13c5176': `上次提交结果正在确认,请稍后重试;图片会被保留。`, 's176fe95698857ad9': `评论或回复已发生变化。请先复制草稿,再重新打开编辑框获取最新版本。`, +'s17c8f8524e36987d': `允许通知`, 's19172ed3838854fc': `复制链接`, 's1c6fefb092506753': `加载评论列表失败,请稍后重试`, 's1d468f888124a55e': `草稿已更改,请重新打开后再重试。`, diff --git a/packages/comment-widget/src/generated/locales/zh-TW.ts b/packages/comment-widget/src/generated/locales/zh-TW.ts index 12ab640..cadaa87 100644 --- a/packages/comment-widget/src/generated/locales/zh-TW.ts +++ b/packages/comment-widget/src/generated/locales/zh-TW.ts @@ -21,6 +21,7 @@ 's107ccef507b51f2c': `請完成人機驗證`, 's15e33945d13c5176': `上次提交結果正在確認,請稍後重試;圖片會被保留。`, 's176fe95698857ad9': `評論或回覆已發生變更。請先複製草稿,再重新開啟編輯框取得最新版本。`, +'s17c8f8524e36987d': `允許通知`, 's19172ed3838854fc': `複製連結`, 's1c6fefb092506753': `載入評論列表失敗,請稍後重試`, 's1d468f888124a55e': `草稿已變更,請重新開啟後再重試。`, diff --git a/packages/comment-widget/src/reply-form.ts b/packages/comment-widget/src/reply-form.ts index a86df13..801089e 100644 --- a/packages/comment-widget/src/reply-form.ts +++ b/packages/comment-widget/src/reply-form.ts @@ -78,13 +78,12 @@ export class ReplyForm extends LitElement { onSubmit(e: SubmissionEvent) { const data = e.detail; - const { content, hidden } = data || {}; + const { content, hidden, allowNotification } = data || {}; const replyRequest: ReplyRequest = { raw: content, content: content, - // TODO: support user input - allowNotification: true, + allowNotification: allowNotification, hidden: hidden || false, }; diff --git a/packages/comment-widget/src/types/index.ts b/packages/comment-widget/src/types/index.ts index 43e2433..bd67f61 100644 --- a/packages/comment-widget/src/types/index.ts +++ b/packages/comment-widget/src/types/index.ts @@ -11,6 +11,7 @@ interface BasicConfig { size: number; withReplySize: number; replySize: number; + allowNotificationConfigurable?: boolean; showCommenterDevice?: boolean; enablePrivateComment?: boolean; showPrivateCommentBadge?: boolean; diff --git a/packages/comment-widget/src/utils/submission.ts b/packages/comment-widget/src/utils/submission.ts index 403ad18..93aed29 100644 --- a/packages/comment-widget/src/utils/submission.ts +++ b/packages/comment-widget/src/utils/submission.ts @@ -29,6 +29,7 @@ export interface SubmissionDetail { turnstileToken?: string; altchaPayload?: string; content: string; + allowNotification: boolean; hidden: boolean; uploadIds: string[]; uploadSession: UploadSession; diff --git a/packages/comment-widget/tests/allow-notification.browser.test.js b/packages/comment-widget/tests/allow-notification.browser.test.js new file mode 100644 index 0000000..f6e274d --- /dev/null +++ b/packages/comment-widget/tests/allow-notification.browser.test.js @@ -0,0 +1,88 @@ +import { assert, test } from 'vitest'; +import { mockApi, until } from './browser-helpers.js'; + +test.each(['comment-form', 'reply-form'])( + '%s submits the selected allow notification state', + async (tag) => { + const requests = []; + mockApi(async (_input, options = {}) => { + requests.push(JSON.parse(options.body)); + return new Response(JSON.stringify({ spec: { approved: true } }), { + headers: { 'Content-Type': 'application/json' }, + }); + }); + await import('../src/index.ts'); + + const form = document.createElement(tag); + form.comment = { metadata: { name: 'comment-1' } }; + form.currentUser = { metadata: { name: 'commenter' }, spec: {} }; + document.body.append(form); + await form.updateComplete; + const base = form.baseFormRef.value; + base.currentUser = form.currentUser; + base.configMapData = { basic: { allowNotificationConfigurable: true } }; + await base.updateComplete; + await until(() => base.editorRef.value?.editor); + + const checkbox = base.shadowRoot.querySelector('#allowNotification'); + assert(checkbox, 'Allow notification must be selectable'); + assert(checkbox.checked, 'Allow notification must be enabled by default'); + + base.editorRef.value.editor.commands.setContent('

Notify me

'); + base.shadowRoot.querySelector('form').requestSubmit(); + await until(() => requests.length === 1 && !form.submitting); + assert( + requests[0].allowNotification === true, + 'Checked must send allowNotification=true' + ); + + checkbox.click(); + assert(!checkbox.checked, 'Allow notification must be toggleable'); + base.editorRef.value.editor.commands.setContent('

Do not notify me

'); + base.shadowRoot.querySelector('form').requestSubmit(); + await until(() => requests.length === 2 && !form.submitting); + assert( + requests[1].allowNotification === false, + 'Unchecked must send allowNotification=false' + ); + form.remove(); + } +); + +test.each(['comment-form', 'reply-form'])( + '%s always allows notifications when the option is disabled', + async (tag) => { + const requests = []; + mockApi(async (_input, options = {}) => { + requests.push(JSON.parse(options.body)); + return new Response(JSON.stringify({ spec: { approved: true } }), { + headers: { 'Content-Type': 'application/json' }, + }); + }); + await import('../src/index.ts'); + + const form = document.createElement(tag); + form.comment = { metadata: { name: 'comment-1' } }; + form.currentUser = { metadata: { name: 'commenter' }, spec: {} }; + document.body.append(form); + await form.updateComplete; + const base = form.baseFormRef.value; + base.currentUser = form.currentUser; + await base.updateComplete; + await until(() => base.editorRef.value?.editor); + + assert( + !base.shadowRoot.querySelector('#allowNotification'), + 'Allow notification must be hidden when the option is disabled' + ); + + base.editorRef.value.editor.commands.setContent('

Notify me

'); + base.shadowRoot.querySelector('form').requestSubmit(); + await until(() => requests.length === 1 && !form.submitting); + assert( + requests[0].allowNotification === true, + 'Disabled option must send allowNotification=true' + ); + form.remove(); + } +); diff --git a/packages/comment-widget/xliff/es.xlf b/packages/comment-widget/xliff/es.xlf index 7fd7ea5..f467db7 100644 --- a/packages/comment-widget/xliff/es.xlf +++ b/packages/comment-widget/xliff/es.xlf @@ -382,6 +382,10 @@ Make private Hacer privado + + Allow notification + Permitir notificación + diff --git a/packages/comment-widget/xliff/zh-CN.xlf b/packages/comment-widget/xliff/zh-CN.xlf index d673d6a..4f16661 100644 --- a/packages/comment-widget/xliff/zh-CN.xlf +++ b/packages/comment-widget/xliff/zh-CN.xlf @@ -382,6 +382,10 @@ Make private 设为私密 + + Allow notification + 允许通知 + diff --git a/packages/comment-widget/xliff/zh-TW.xlf b/packages/comment-widget/xliff/zh-TW.xlf index 8493997..7342c8b 100644 --- a/packages/comment-widget/xliff/zh-TW.xlf +++ b/packages/comment-widget/xliff/zh-TW.xlf @@ -382,6 +382,10 @@ Make private 設為私密 + + Allow notification + 允許通知 + diff --git a/src/main/java/run/halo/comment/widget/SettingConfigGetter.java b/src/main/java/run/halo/comment/widget/SettingConfigGetter.java index 57ae0ee..81d1bb6 100644 --- a/src/main/java/run/halo/comment/widget/SettingConfigGetter.java +++ b/src/main/java/run/halo/comment/widget/SettingConfigGetter.java @@ -130,6 +130,7 @@ class BasicConfig { private int replySize; private boolean withReplies; private int withReplySize; + private boolean allowNotificationConfigurable; } @Data diff --git a/src/main/resources/extensions/settings.yaml b/src/main/resources/extensions/settings.yaml index 4e65ea6..7111dc4 100644 --- a/src/main/resources/extensions/settings.yaml +++ b/src/main/resources/extensions/settings.yaml @@ -43,6 +43,13 @@ spec: label: 显示评论者设备信息 name: showCommenterDevice value: false + - $formkit: checkbox + label: 允许用户选择是否接收通知 + name: allowNotificationConfigurable + id: allowNotificationConfigurable + key: allowNotificationConfigurable + value: false + help: 开启后,评论和回复表单中会显示「允许通知」选项;关闭时始终接收后续回复通知。 - $formkit: checkbox label: 支持私密评论和回复 id: enablePrivateComment