Channel configuration UI: form validation.

This commit is contained in:
John Livingston 2023-09-25 12:51:15 +02:00
parent e2c85af001
commit 06b9417650
No known key found for this signature in database
GPG Key ID: B17B5640CE66CDBC
4 changed files with 85 additions and 7 deletions

View File

@ -69,3 +69,5 @@ declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_DELAY_DESC: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_BANNED_JIDS_LABEL: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_BOT_NICKNAME: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FOR_MORE_INFO: string
declare const LOC_INVALID_VALUE: string

View File

@ -132,8 +132,9 @@ async function vivifyConfigurationChannel (
): Promise<void> {
const form = rootEl.querySelector('form[livechat-configuration-channel-options]') as HTMLFormElement
if (!form) { return }
const labelSaved = await clientOptions.peertubeHelpers.translate(LOC_SUCCESSFULLY_SAVED)
const labelError = await clientOptions.peertubeHelpers.translate(LOC_ERROR)
const translate = clientOptions.peertubeHelpers.translate
const labelSaved = await translate(LOC_SUCCESSFULLY_SAVED)
const labelError = await translate(LOC_ERROR)
const enableBotCB = form.querySelector('input[name=bot]') as HTMLInputElement
const botEnabledEl = form.querySelectorAll('[livechat-configuration-channel-options-bot-enabled]')
@ -147,8 +148,70 @@ async function vivifyConfigurationChannel (
})
}
const removeDisplayedErrors = (): void => {
form.querySelectorAll('.form-error').forEach(el => el.remove())
}
const displayError = async (fieldSelector: string, message: string): Promise<void> => {
form.querySelectorAll(fieldSelector).forEach(el => {
const erEl = document.createElement('div')
erEl.classList.add('form-error')
erEl.textContent = message
el.after(erEl)
})
}
const validateData: Function = async (channelConfigurationOptions: ChannelConfigurationOptions): Promise<boolean> => {
const botConf = channelConfigurationOptions.bot
const errorFieldSelectors = []
if (/[^\p{L}\p{N}\p{Z}_-]/u.test(botConf.nickname ?? '')) {
const selector = '#peertube-livechat-bot-nickname'
errorFieldSelectors.push(selector)
await displayError(selector, await translate(LOC_INVALID_VALUE))
}
for (let iFw = 0; iFw < botConf.forbiddenWords.length; iFw++) {
const fw = botConf.forbiddenWords[iFw]
if (fw.regexp) {
for (const v of fw.entries) {
if (v === '' || /^\s+$/.test(v)) { continue }
try {
// eslint-disable-next-line no-new
new RegExp(v)
} catch (err) {
const selector = '#peertube-livechat-forbidden-words-' + iFw.toString()
errorFieldSelectors.push(selector)
let message = await translate(LOC_INVALID_VALUE)
message += ` "${v}": ${err as string}`
await displayError(selector, message)
}
}
}
}
for (let iCd = 0; iCd < botConf.commands.length; iCd++) {
const cd = botConf.commands[iCd]
if (/\s+/.test(cd.command)) {
const selector = '#peertube-livechat-command-' + iCd.toString()
errorFieldSelectors.push(selector)
const message = await translate(LOC_INVALID_VALUE)
await displayError(selector, message)
}
}
if (errorFieldSelectors.length) {
// Set the focus to the first in-error field:
const el: HTMLInputElement | HTMLTextAreaElement | null = document.querySelector(errorFieldSelectors[0])
el?.focus()
return false
}
return true
}
const submitForm: Function = async () => {
const data = new FormData(form)
removeDisplayedErrors()
const channelConfigurationOptions: ChannelConfigurationOptions = {
bot: {
enabled: data.get('bot') === '1',
@ -160,8 +223,7 @@ async function vivifyConfigurationChannel (
}
}
// TODO: handle form errors.
// Note: but data in order, because validateData assume index are okay to find associated fields.
for (let i = 0; data.has('forbidden_words_' + i.toString()); i++) {
const entries = (data.get('forbidden_words_' + i.toString())?.toString() ?? '')
.split(/\r?\n|\r|\n/g)
@ -180,6 +242,7 @@ async function vivifyConfigurationChannel (
channelConfigurationOptions.bot.forbiddenWords.push(fw)
}
// Note: but data in order, because validateData assume index are okay to find associated fields.
for (let i = 0; data.has('quote_' + i.toString()); i++) {
const messages = (data.get('quote_' + i.toString())?.toString() ?? '')
.split(/\r?\n|\r|\n/g)
@ -196,6 +259,7 @@ async function vivifyConfigurationChannel (
channelConfigurationOptions.bot.quotes.push(q)
}
// Note: but data in order, because validateData assume index are okay to find associated fields.
for (let i = 0; data.has('command_' + i.toString()); i++) {
const command = (data.get('command_' + i.toString())?.toString() ?? '')
const message = (data.get('command_message_' + i.toString())?.toString() ?? '')
@ -206,6 +270,10 @@ async function vivifyConfigurationChannel (
channelConfigurationOptions.bot.commands.push(c)
}
if (!await validateData(channelConfigurationOptions)) {
throw new Error('Invalid form data')
}
const headers: any = clientOptions.peertubeHelpers.getAuthHeader() ?? {}
headers['content-type'] = 'application/json;charset=UTF-8'
@ -235,6 +303,9 @@ async function vivifyConfigurationChannel (
enableBotCB.onclick = () => refresh()
form.onsubmit = () => {
toggleSubmit(true)
if (!form.checkValidity()) {
return false
}
submitForm().then(
() => {
clientOptions.peertubeHelpers.notifier.success(labelSaved)

View File

@ -360,4 +360,6 @@ livechat_configuration_channel_command_message_desc: |
livechat_configuration_channel_for_more_info: |
For more information about how to configure this feature, please refer to the documentation by clicking on the help button.
livechat_configuration_channel_banned_jids_label: "Banned users and patterns"
livechat_configuration_channel_bot_nickname: "Bot nickname"
livechat_configuration_channel_bot_nickname: "Bot nickname"
invalid_value: "Invalid value."

View File

@ -64,7 +64,7 @@ function _readInteger (data: any, f: string, min: number, max: number): number {
return v
}
function _readSimpleInput (data: any, f: string, strict?: boolean): string {
function _readSimpleInput (data: any, f: string, strict?: boolean, noSpace?: boolean): string {
if (!(f in data)) {
return ''
}
@ -78,6 +78,9 @@ function _readSimpleInput (data: any, f: string, strict?: boolean): string {
// Replacing all invalid characters, no need to throw an error..
s = s.replace(/[^\p{L}\p{N}\p{Z}_-]$/gu, '')
}
if (noSpace) {
s = s.replace(/\s+/g, '')
}
return s
}
@ -185,7 +188,7 @@ function _readCommands (botData: any): ChannelConfigurationOptions['bot']['comma
const result: ChannelConfigurationOptions['bot']['commands'] = []
for (const cs of botData.commands) {
const message = _readSimpleInput(cs, 'message')
const command = _readSimpleInput(cs, 'command')
const command = _readSimpleInput(cs, 'command', false, true)
result.push({
message,