Channel configuration UI: form validation.
This commit is contained in:
parent
e2c85af001
commit
06b9417650
2
client/@types/global.d.ts
vendored
2
client/@types/global.d.ts
vendored
@ -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_BANNED_JIDS_LABEL: string
|
||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_BOT_NICKNAME: string
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_BOT_NICKNAME: string
|
||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FOR_MORE_INFO: string
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FOR_MORE_INFO: string
|
||||||
|
|
||||||
|
declare const LOC_INVALID_VALUE: string
|
||||||
|
@ -132,8 +132,9 @@ async function vivifyConfigurationChannel (
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const form = rootEl.querySelector('form[livechat-configuration-channel-options]') as HTMLFormElement
|
const form = rootEl.querySelector('form[livechat-configuration-channel-options]') as HTMLFormElement
|
||||||
if (!form) { return }
|
if (!form) { return }
|
||||||
const labelSaved = await clientOptions.peertubeHelpers.translate(LOC_SUCCESSFULLY_SAVED)
|
const translate = clientOptions.peertubeHelpers.translate
|
||||||
const labelError = await clientOptions.peertubeHelpers.translate(LOC_ERROR)
|
const labelSaved = await translate(LOC_SUCCESSFULLY_SAVED)
|
||||||
|
const labelError = await translate(LOC_ERROR)
|
||||||
const enableBotCB = form.querySelector('input[name=bot]') as HTMLInputElement
|
const enableBotCB = form.querySelector('input[name=bot]') as HTMLInputElement
|
||||||
const botEnabledEl = form.querySelectorAll('[livechat-configuration-channel-options-bot-enabled]')
|
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 submitForm: Function = async () => {
|
||||||
const data = new FormData(form)
|
const data = new FormData(form)
|
||||||
|
removeDisplayedErrors()
|
||||||
const channelConfigurationOptions: ChannelConfigurationOptions = {
|
const channelConfigurationOptions: ChannelConfigurationOptions = {
|
||||||
bot: {
|
bot: {
|
||||||
enabled: data.get('bot') === '1',
|
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++) {
|
for (let i = 0; data.has('forbidden_words_' + i.toString()); i++) {
|
||||||
const entries = (data.get('forbidden_words_' + i.toString())?.toString() ?? '')
|
const entries = (data.get('forbidden_words_' + i.toString())?.toString() ?? '')
|
||||||
.split(/\r?\n|\r|\n/g)
|
.split(/\r?\n|\r|\n/g)
|
||||||
@ -180,6 +242,7 @@ async function vivifyConfigurationChannel (
|
|||||||
channelConfigurationOptions.bot.forbiddenWords.push(fw)
|
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++) {
|
for (let i = 0; data.has('quote_' + i.toString()); i++) {
|
||||||
const messages = (data.get('quote_' + i.toString())?.toString() ?? '')
|
const messages = (data.get('quote_' + i.toString())?.toString() ?? '')
|
||||||
.split(/\r?\n|\r|\n/g)
|
.split(/\r?\n|\r|\n/g)
|
||||||
@ -196,6 +259,7 @@ async function vivifyConfigurationChannel (
|
|||||||
channelConfigurationOptions.bot.quotes.push(q)
|
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++) {
|
for (let i = 0; data.has('command_' + i.toString()); i++) {
|
||||||
const command = (data.get('command_' + i.toString())?.toString() ?? '')
|
const command = (data.get('command_' + i.toString())?.toString() ?? '')
|
||||||
const message = (data.get('command_message_' + i.toString())?.toString() ?? '')
|
const message = (data.get('command_message_' + i.toString())?.toString() ?? '')
|
||||||
@ -206,6 +270,10 @@ async function vivifyConfigurationChannel (
|
|||||||
channelConfigurationOptions.bot.commands.push(c)
|
channelConfigurationOptions.bot.commands.push(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!await validateData(channelConfigurationOptions)) {
|
||||||
|
throw new Error('Invalid form data')
|
||||||
|
}
|
||||||
|
|
||||||
const headers: any = clientOptions.peertubeHelpers.getAuthHeader() ?? {}
|
const headers: any = clientOptions.peertubeHelpers.getAuthHeader() ?? {}
|
||||||
headers['content-type'] = 'application/json;charset=UTF-8'
|
headers['content-type'] = 'application/json;charset=UTF-8'
|
||||||
|
|
||||||
@ -235,6 +303,9 @@ async function vivifyConfigurationChannel (
|
|||||||
enableBotCB.onclick = () => refresh()
|
enableBotCB.onclick = () => refresh()
|
||||||
form.onsubmit = () => {
|
form.onsubmit = () => {
|
||||||
toggleSubmit(true)
|
toggleSubmit(true)
|
||||||
|
if (!form.checkValidity()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
submitForm().then(
|
submitForm().then(
|
||||||
() => {
|
() => {
|
||||||
clientOptions.peertubeHelpers.notifier.success(labelSaved)
|
clientOptions.peertubeHelpers.notifier.success(labelSaved)
|
||||||
|
@ -360,4 +360,6 @@ livechat_configuration_channel_command_message_desc: |
|
|||||||
livechat_configuration_channel_for_more_info: |
|
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.
|
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_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."
|
||||||
|
@ -64,7 +64,7 @@ function _readInteger (data: any, f: string, min: number, max: number): number {
|
|||||||
return v
|
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)) {
|
if (!(f in data)) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
@ -78,6 +78,9 @@ function _readSimpleInput (data: any, f: string, strict?: boolean): string {
|
|||||||
// Replacing all invalid characters, no need to throw an error..
|
// Replacing all invalid characters, no need to throw an error..
|
||||||
s = s.replace(/[^\p{L}\p{N}\p{Z}_-]$/gu, '')
|
s = s.replace(/[^\p{L}\p{N}\p{Z}_-]$/gu, '')
|
||||||
}
|
}
|
||||||
|
if (noSpace) {
|
||||||
|
s = s.replace(/\s+/g, '')
|
||||||
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +188,7 @@ function _readCommands (botData: any): ChannelConfigurationOptions['bot']['comma
|
|||||||
const result: ChannelConfigurationOptions['bot']['commands'] = []
|
const result: ChannelConfigurationOptions['bot']['commands'] = []
|
||||||
for (const cs of botData.commands) {
|
for (const cs of botData.commands) {
|
||||||
const message = _readSimpleInput(cs, 'message')
|
const message = _readSimpleInput(cs, 'message')
|
||||||
const command = _readSimpleInput(cs, 'command')
|
const command = _readSimpleInput(cs, 'command', false, true)
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
message,
|
message,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user