diff --git a/client/common/configuration/elements/channel-configuration.ts b/client/common/configuration/elements/channel-configuration.ts index 20d864e7..71e72c94 100644 --- a/client/common/configuration/elements/channel-configuration.ts +++ b/client/common/configuration/elements/channel-configuration.ts @@ -4,16 +4,16 @@ // SPDX-License-Identifier: AGPL-3.0-only import type { ChannelConfiguration } from 'shared/lib/types' +import { ChannelDetailsService } from '../services/channel-details' +import { channelConfigurationContext, channelDetailsServiceContext } from '../contexts/channel' +import { LivechatElement } from '../../lib/elements/livechat' +import { ValidationError, ValidationErrorType } from '../../lib/models/validation' +import { tplChannelConfiguration } from './templates/channel-configuration' import { TemplateResult, html, nothing } from 'lit' import { customElement, property, state } from 'lit/decorators.js' import { ptTr } from '../../lib/directives/translation' import { Task } from '@lit/task' -import { ChannelDetailsService } from '../services/channel-details' import { provide } from '@lit/context' -import { channelConfigurationContext, channelDetailsServiceContext } from '../contexts/channel' -import { LivechatElement } from '../../lib/elements/livechat' -import { ValidationError, ValidationErrorType } from '../../lib/models/validation' -import { classMap } from 'lit/directives/class-map.js' @customElement('livechat-channel-configuration') export class ChannelConfigurationElement extends LivechatElement { @@ -22,16 +22,16 @@ export class ChannelConfigurationElement extends LivechatElement { @provide({ context: channelConfigurationContext }) @state() - public _channelConfiguration?: ChannelConfiguration + public channelConfiguration?: ChannelConfiguration @provide({ context: channelDetailsServiceContext }) private _channelDetailsService?: ChannelDetailsService @state() - public _validationError?: ValidationError + public validationError?: ValidationError @state() - private _actionDisabled: boolean = false + public actionDisabled: boolean = false private _asyncTaskRender: Task @@ -44,37 +44,44 @@ export class ChannelConfigurationElement extends LivechatElement { return new Task(this, { task: async () => { this._channelDetailsService = new ChannelDetailsService(this.ptOptions) - this._channelConfiguration = await this._channelDetailsService.fetchConfiguration(this.channelId ?? 0) - this._actionDisabled = false // in case of reset + this.channelConfiguration = await this._channelDetailsService.fetchConfiguration(this.channelId ?? 0) + this.actionDisabled = false // in case of reset }, args: () => [] }) } - private async _reset (event?: Event): Promise { + /** + * Resets the form by reloading data from backend. + */ + public async reset (event?: Event): Promise { event?.preventDefault() - this._actionDisabled = true + this.actionDisabled = true this._asyncTaskRender = this._initTask() this.requestUpdate() } - private readonly _saveConfig = async (event?: Event): Promise => { + /** + * Saves the channel configuration. + * @param event event + */ + public readonly saveConfig = async (event?: Event): Promise => { event?.preventDefault() - if (this._channelDetailsService && this._channelConfiguration) { - this._actionDisabled = true - this._channelDetailsService.saveOptions(this._channelConfiguration.channel.id, - this._channelConfiguration.configuration) + if (this._channelDetailsService && this.channelConfiguration) { + this.actionDisabled = true + this._channelDetailsService.saveOptions(this.channelConfiguration.channel.id, + this.channelConfiguration.configuration) .then(() => { - this._validationError = undefined + this.validationError = undefined this.ptTranslate(LOC_SUCCESSFULLY_SAVED).then((msg) => { this.ptNotifier.info(msg) }, () => {}) this.requestUpdate('_validationError') }) .catch(async (error: Error) => { - this._validationError = undefined + this.validationError = undefined if (error instanceof ValidationError) { - this._validationError = error + this.validationError = error } console.warn(`A validation error occurred in saving configuration. ${error.name}: ${error.message}`) this.ptNotifier.error( @@ -85,22 +92,22 @@ export class ChannelConfigurationElement extends LivechatElement { this.requestUpdate('_validationError') }) .finally(() => { - this._actionDisabled = false + this.actionDisabled = false }) } } - private readonly _getInputValidationClass = (propertyName: string): { [key: string]: boolean } => { + public readonly getInputValidationClass = (propertyName: string): { [key: string]: boolean } => { const validationErrorTypes: ValidationErrorType[] | undefined = - this._validationError?.properties[`${propertyName}`] + this.validationError?.properties[`${propertyName}`] return validationErrorTypes ? (validationErrorTypes.length ? { 'is-invalid': true } : { 'is-valid': true }) : {} } - private readonly _renderFeedback = (feedbackId: string, + public readonly renderFeedback = (feedbackId: string, propertyName: string): TemplateResult | typeof nothing => { const errorMessages: TemplateResult[] = [] const validationErrorTypes: ValidationErrorType[] | undefined = - this._validationError?.properties[`${propertyName}`] ?? undefined + this.validationError?.properties[`${propertyName}`] ?? undefined if (validationErrorTypes && validationErrorTypes.length !== 0) { if (validationErrorTypes.includes(ValidationErrorType.WrongType)) { @@ -120,283 +127,10 @@ export class ChannelConfigurationElement extends LivechatElement { } protected override render = (): unknown => { - const tableHeaderList = { - forbiddenWords: { - entries: { - colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL), - description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC2) - }, - regexp: { - colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REGEXP_LABEL), - description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REGEXP_DESC) - }, - applyToModerators: { - colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_LABEL), - description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_DESC) - }, - label: { - colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_LABEL), - description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_DESC) - }, - reason: { - colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REASON_LABEL), - description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REASON_DESC) - }, - comments: { - colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_COMMENTS_LABEL), - description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_COMMENTS_DESC) - } - }, - quotes: { - messages: { - colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_LABEL2), - description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_DESC2) - }, - delay: { - colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_DELAY_LABEL), - description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_DELAY_DESC) - } - }, - commands: { - command: { - colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_CMD_LABEL), - description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_CMD_DESC) - }, - message: { - colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_MESSAGE_LABEL), - description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_MESSAGE_DESC) - } - } - } - const tableSchema = { - forbiddenWords: { - entries: { - inputType: 'tags', - default: [], - separators: ['\n', '\t', ';'] - }, - regexp: { - inputType: 'checkbox', - default: false - }, - applyToModerators: { - inputType: 'checkbox', - default: false - }, - label: { - inputType: 'text', - default: '' - }, - reason: { - inputType: 'text', - default: '', - datalist: [] - }, - comments: { - inputType: 'textarea', - default: '' - } - }, - quotes: { - messages: { - inputType: 'tags', - default: [], - separators: ['\n', '\t', ';'] - }, - delay: { - inputType: 'number', - default: 10 - } - }, - commands: { - command: { - inputType: 'text', - default: '' - }, - message: { - inputType: 'text', - default: '' - } - } - } - return this._asyncTaskRender.render({ pending: () => html``, error: () => html``, - complete: () => html` -
-

- - ${this._channelConfiguration?.channel.displayName} - ${this._channelConfiguration?.channel.name} - -

- - - -

- ${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_DESC)} - - -

- -
- - -
- - ${this._renderFeedback('peertube-livechat-slowmode-duration-feedback', 'slowMode.duration')} -
- - - -
- -
- - ${!this._channelConfiguration?.configuration.bot.enabled - ? '' - : html`
- - { - if (event?.target && this._channelConfiguration) { - this._channelConfiguration.configuration.bot.nickname = - (event.target as HTMLInputElement).value - } - this.requestUpdate('_channelConfiguration') - } - } - value="${this._channelConfiguration?.configuration.bot.nickname ?? ''}" - /> - ${this._renderFeedback('peertube-livechat-bot-nickname-feedback', 'bot.nickname')} -
- - - - { - if (this._channelConfiguration) { - this._channelConfiguration.configuration.bot.forbiddenWords = e.detail - this.requestUpdate('_channelConfiguration') - } - } - } - .formName=${'forbidden-words'} - > - - - - { - if (this._channelConfiguration) { - this._channelConfiguration.configuration.bot.quotes = e.detail - this.requestUpdate('_channelConfiguration') - } - } - } - .formName=${'quote'} - > - - - - { - if (this._channelConfiguration) { - this._channelConfiguration.configuration.bot.commands = e.detail - this.requestUpdate('_channelConfiguration') - } - } - } - .formName=${'command'} - > - `} - -
- - -
-
-
` + complete: () => tplChannelConfiguration(this) }) } } diff --git a/client/common/configuration/elements/channel-emojis.ts b/client/common/configuration/elements/channel-emojis.ts index 25e656d7..e881d077 100644 --- a/client/common/configuration/elements/channel-emojis.ts +++ b/client/common/configuration/elements/channel-emojis.ts @@ -3,13 +3,11 @@ // SPDX-License-Identifier: AGPL-3.0-only import type { ChannelEmojisConfiguration } from 'shared/lib/types' -import type { DynamicFormHeader, DynamicFormSchema } from '../../lib/elements/dynamic-table-form' import { LivechatElement } from '../../lib/elements/livechat' import { ChannelDetailsService } from '../services/channel-details' import { channelDetailsServiceContext } from '../contexts/channel' -import { maxEmojisPerChannel } from 'shared/lib/emojis' -import { ptTr } from '../../lib/directives/translation' import { ValidationError } from '../../lib/models/validation' +import { tplChannelEmojis } from './templates/channel-emojis' import { Task } from '@lit/task' import { customElement, property, state } from 'lit/decorators.js' import { provide } from '@lit/context' @@ -23,16 +21,16 @@ export class ChannelEmojisElement extends LivechatElement { @property({ attribute: false }) public channelId?: number - private _channelEmojisConfiguration?: ChannelEmojisConfiguration + public channelEmojisConfiguration?: ChannelEmojisConfiguration @provide({ context: channelDetailsServiceContext }) private _channelDetailsService?: ChannelDetailsService @state() - private _validationError?: ValidationError + public validationError?: ValidationError @state() - private _actionDisabled: boolean = false + public actionDisabled: boolean = false private _asyncTaskRender: Task @@ -42,111 +40,10 @@ export class ChannelEmojisElement extends LivechatElement { } protected override render = (): unknown => { - const tableHeaderList: DynamicFormHeader = { - sn: { - colName: ptTr(LOC_LIVECHAT_EMOJIS_SHORTNAME), - description: ptTr(LOC_LIVECHAT_EMOJIS_SHORTNAME_DESC), - headerClassList: ['peertube-livechat-emojis-col-sn'] - }, - url: { - colName: ptTr(LOC_LIVECHAT_EMOJIS_FILE), - description: ptTr(LOC_LIVECHAT_EMOJIS_FILE_DESC), - headerClassList: ['peertube-livechat-emojis-col-file'] - } - } - const tableSchema: DynamicFormSchema = { - sn: { - inputType: 'text', - default: '' - }, - url: { - inputType: 'image-file', - default: '', - colClassList: ['peertube-livechat-emojis-col-file'] - } - } return this._asyncTaskRender.render({ pending: () => html``, error: () => html``, - complete: () => html` -
-

- - ${this._channelEmojisConfiguration?.channel.displayName} - ${this._channelEmojisConfiguration?.channel.name} - -

- - - -

- ${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_EMOJIS_DESC)} - - -

- - -
-
- ${ - this._channelEmojisConfiguration?.emojis?.customEmojis?.length - ? html` - ` - : '' - } - ${ - (this._channelEmojisConfiguration?.emojis?.customEmojis?.length ?? 0) < maxEmojisPerChannel - ? html` - ` - : '' - } -
- - { - if (this._channelEmojisConfiguration) { - this._channelEmojisConfiguration.emojis.customEmojis = e.detail - // Fixing missing ':' for shortnames: - for (const desc of this._channelEmojisConfiguration.emojis.customEmojis) { - if (desc.sn === '') { continue } - if (!desc.sn.startsWith(':')) { desc.sn = ':' + desc.sn } - if (!desc.sn.endsWith(':')) { desc.sn += ':' } - } - this.requestUpdate('_channelEmojisConfiguration') - } - } - } - > - -
- - -
-
-
- ` + complete: () => tplChannelEmojis(this) }) } @@ -157,39 +54,46 @@ export class ChannelEmojisElement extends LivechatElement { throw new Error('Missing channelId') } this._channelDetailsService = new ChannelDetailsService(this.ptOptions) - this._channelEmojisConfiguration = await this._channelDetailsService.fetchEmojisConfiguration(this.channelId) - this._actionDisabled = false // in case of reset + this.channelEmojisConfiguration = await this._channelDetailsService.fetchEmojisConfiguration(this.channelId) + this.actionDisabled = false // in case of reset }, args: () => [] }) } - private async _reset (ev?: Event): Promise { + /** + * Resets the page, by reloading data from backend. + */ + public async reset (ev?: Event): Promise { ev?.preventDefault() - this._actionDisabled = true + this.actionDisabled = true this._asyncTaskRender = this._initTask() this.requestUpdate() } - private async _saveEmojis (ev?: Event): Promise { + /** + * Saves the emojis form. + * @param ev event + */ + public async saveEmojis (ev?: Event): Promise { ev?.preventDefault() - if (!this._channelDetailsService || !this._channelEmojisConfiguration || !this.channelId) { + if (!this._channelDetailsService || !this.channelEmojisConfiguration || !this.channelId) { this.ptNotifier.error(await this.ptTranslate(LOC_ERROR)) return } try { - this._actionDisabled = true - await this._channelDetailsService.saveEmojisConfiguration(this.channelId, this._channelEmojisConfiguration.emojis) - this._validationError = undefined + this.actionDisabled = true + await this._channelDetailsService.saveEmojisConfiguration(this.channelId, this.channelEmojisConfiguration.emojis) + this.validationError = undefined this.ptNotifier.info(await this.ptTranslate(LOC_SUCCESSFULLY_SAVED)) this.requestUpdate('_validationError') } catch (error) { - this._validationError = undefined + this.validationError = undefined let msg: string if ((error instanceof ValidationError)) { - this._validationError = error + this.validationError = error if (error.message) { msg = error.message } @@ -198,13 +102,16 @@ export class ChannelEmojisElement extends LivechatElement { this.ptNotifier.error(msg) this.requestUpdate('_validationError') } finally { - this._actionDisabled = false + this.actionDisabled = false } } - private async _importEmojis (ev: Event): Promise { + /** + * Import emojis action. + */ + public async importEmojis (ev: Event): Promise { ev.preventDefault() - this._actionDisabled = true + this.actionDisabled = true try { // download a json file: const file = await new Promise((resolve, reject) => { @@ -266,10 +173,10 @@ export class ChannelEmojisElement extends LivechatElement { if (entry.isCategoryEmoji === true) { item.isCategoryEmoji = true } - this._channelEmojisConfiguration?.emojis.customEmojis.push(item) + this.channelEmojisConfiguration?.emojis.customEmojis.push(item) } - this.requestUpdate('_channelEmojisConfiguration') + this.requestUpdate('channelEmojisConfiguration') this.ptNotifier.info( await this.ptTranslate(LOC_ACTION_IMPORT_EMOJIS_INFO) @@ -277,16 +184,19 @@ export class ChannelEmojisElement extends LivechatElement { } catch (err: any) { this.ptNotifier.error(err.toString()) } finally { - this._actionDisabled = false + this.actionDisabled = false } } - private async _exportEmojis (ev: Event): Promise { + /** + * Export emojis action. + */ + public async exportEmojis (ev: Event): Promise { ev.preventDefault() - this._actionDisabled = true + this.actionDisabled = true try { const result: ChannelEmojisConfiguration['emojis']['customEmojis'] = [] - for (const ed of this._channelEmojisConfiguration?.emojis?.customEmojis ?? []) { + for (const ed of this.channelEmojisConfiguration?.emojis?.customEmojis ?? []) { if (!ed.sn || !ed.url) { continue } // Here url can be: // * the dataUrl representation of a newly uploaded file @@ -313,10 +223,15 @@ export class ChannelEmojisElement extends LivechatElement { console.error(err) this.ptNotifier.error(err.toString()) } finally { - this._actionDisabled = false + this.actionDisabled = false } } + /** + * Takes an url (or dataUrl), download the image, and converts to dataUrl. + * @param url the url + * @returns A dataUrl representation of the image. + */ private async _convertImageToDataUrl (url: string): Promise { if (url.startsWith('data:')) { return url } // There is a trick to convert img to dataUrl: using a canvas. diff --git a/client/common/configuration/elements/channel-home.ts b/client/common/configuration/elements/channel-home.ts index f912adbe..6d70459a 100644 --- a/client/common/configuration/elements/channel-home.ts +++ b/client/common/configuration/elements/channel-home.ts @@ -20,9 +20,6 @@ export class ChannelHomeElement extends LivechatElement { @provide({ context: channelDetailsServiceContext }) private _channelDetailsService?: ChannelDetailsService - @state() - public _formStatus: boolean | any = undefined - private readonly _asyncTaskRender = new Task(this, { task: async () => { // Getting the current username in localStorage. Don't know any cleaner way to do. diff --git a/client/common/configuration/elements/templates/channel-configuration.ts b/client/common/configuration/elements/templates/channel-configuration.ts new file mode 100644 index 00000000..d9fd69ff --- /dev/null +++ b/client/common/configuration/elements/templates/channel-configuration.ts @@ -0,0 +1,296 @@ +// SPDX-FileCopyrightText: 2024 John Livingston +// +// SPDX-License-Identifier: AGPL-3.0-only + +import type { ChannelConfigurationElement } from '../channel-configuration' +import type { DynamicFormHeader, DynamicFormSchema } from '../../../lib/elements/dynamic-table-form' +import { ptTr } from '../../../lib/directives/translation' +import { html, TemplateResult } from 'lit' +import { classMap } from 'lit/directives/class-map.js' + +export function tplChannelConfiguration (el: ChannelConfigurationElement): TemplateResult { + const tableHeaderList: {[key: string]: DynamicFormHeader} = { + forbiddenWords: { + entries: { + colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL), + description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC2) + }, + regexp: { + colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REGEXP_LABEL), + description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REGEXP_DESC) + }, + applyToModerators: { + colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_LABEL), + description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_DESC) + }, + label: { + colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_LABEL), + description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_DESC) + }, + reason: { + colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REASON_LABEL), + description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REASON_DESC) + }, + comments: { + colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_COMMENTS_LABEL), + description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_COMMENTS_DESC) + } + }, + quotes: { + messages: { + colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_LABEL2), + description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_DESC2) + }, + delay: { + colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_DELAY_LABEL), + description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_DELAY_DESC) + } + }, + commands: { + command: { + colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_CMD_LABEL), + description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_CMD_DESC) + }, + message: { + colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_MESSAGE_LABEL), + description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_MESSAGE_DESC) + } + } + } + const tableSchema: {[key: string]: DynamicFormSchema} = { + forbiddenWords: { + entries: { + inputType: 'tags', + default: [], + separators: ['\n', '\t', ';'] + }, + regexp: { + inputType: 'checkbox', + default: false + }, + applyToModerators: { + inputType: 'checkbox', + default: false + }, + label: { + inputType: 'text', + default: '' + }, + reason: { + inputType: 'text', + default: '', + datalist: [] + }, + comments: { + inputType: 'textarea', + default: '' + } + }, + quotes: { + messages: { + inputType: 'tags', + default: [], + separators: ['\n', '\t', ';'] + }, + delay: { + inputType: 'number', + default: 10 + } + }, + commands: { + command: { + inputType: 'text', + default: '' + }, + message: { + inputType: 'text', + default: '' + } + } + } + + return html` +
+

+ + ${el.channelConfiguration?.channel.displayName} + ${el.channelConfiguration?.channel.name} + +

+ + + +

+ ${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_DESC)} + + +

+ +
+ + +
+ + ${el.renderFeedback('peertube-livechat-slowmode-duration-feedback', 'slowMode.duration')} +
+ + + +
+ +
+ + ${!el.channelConfiguration?.configuration.bot.enabled + ? '' + : html`
+ + { + if (event?.target && el.channelConfiguration) { + el.channelConfiguration.configuration.bot.nickname = + (event.target as HTMLInputElement).value + } + el.requestUpdate('channelConfiguration') + } + } + value="${el.channelConfiguration?.configuration.bot.nickname ?? ''}" + /> + ${el.renderFeedback('peertube-livechat-bot-nickname-feedback', 'bot.nickname')} +
+ + + + { + if (el.channelConfiguration) { + el.channelConfiguration.configuration.bot.forbiddenWords = e.detail + el.requestUpdate('channelConfiguration') + } + } + } + .formName=${'forbidden-words'} + > + + + + { + if (el.channelConfiguration) { + el.channelConfiguration.configuration.bot.quotes = e.detail + el.requestUpdate('channelConfiguration') + } + } + } + .formName=${'quote'} + > + + + + { + if (el.channelConfiguration) { + el.channelConfiguration.configuration.bot.commands = e.detail + el.requestUpdate('channelConfiguration') + } + } + } + .formName=${'command'} + > + `} + +
+ + +
+
+
` +} diff --git a/client/common/configuration/elements/templates/channel-emojis.ts b/client/common/configuration/elements/templates/channel-emojis.ts new file mode 100644 index 00000000..d26397fc --- /dev/null +++ b/client/common/configuration/elements/templates/channel-emojis.ts @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2024 John Livingston +// +// SPDX-License-Identifier: AGPL-3.0-only + +import type { ChannelEmojisElement } from '../channel-emojis' +import type { DynamicFormHeader, DynamicFormSchema } from '../../../lib/elements/dynamic-table-form' +import { maxEmojisPerChannel } from 'shared/lib/emojis' +import { ptTr } from '../../../lib/directives/translation' +import { html, TemplateResult } from 'lit' + +export function tplChannelEmojis (el: ChannelEmojisElement): TemplateResult { + const tableHeaderList: DynamicFormHeader = { + sn: { + colName: ptTr(LOC_LIVECHAT_EMOJIS_SHORTNAME), + description: ptTr(LOC_LIVECHAT_EMOJIS_SHORTNAME_DESC), + headerClassList: ['peertube-livechat-emojis-col-sn'] + }, + url: { + colName: ptTr(LOC_LIVECHAT_EMOJIS_FILE), + description: ptTr(LOC_LIVECHAT_EMOJIS_FILE_DESC), + headerClassList: ['peertube-livechat-emojis-col-file'] + } + } + const tableSchema: DynamicFormSchema = { + sn: { + inputType: 'text', + default: '' + }, + url: { + inputType: 'image-file', + default: '', + colClassList: ['peertube-livechat-emojis-col-file'] + } + } + return html` +
+

+ + ${el.channelEmojisConfiguration?.channel.displayName} + ${el.channelEmojisConfiguration?.channel.name} + +

+ + + +

+ ${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_EMOJIS_DESC)} + + +

+ + +
+
+ ${ + el.channelEmojisConfiguration?.emojis?.customEmojis?.length + ? html` + ` + : '' + } + ${ + (el.channelEmojisConfiguration?.emojis?.customEmojis?.length ?? 0) < maxEmojisPerChannel + ? html` + ` + : '' + } +
+ + { + if (el.channelEmojisConfiguration) { + el.channelEmojisConfiguration.emojis.customEmojis = e.detail + // Fixing missing ':' for shortnames: + for (const desc of el.channelEmojisConfiguration.emojis.customEmojis) { + if (desc.sn === '') { continue } + if (!desc.sn.startsWith(':')) { desc.sn = ':' + desc.sn } + if (!desc.sn.endsWith(':')) { desc.sn += ':' } + } + el.requestUpdate('channelEmojisConfiguration') + } + } + } + > + +
+ + +
+
+
` +} diff --git a/client/common/lib/elements/configuration-section-header.ts b/client/common/lib/elements/configuration-section-header.ts index e8357342..a0ac92da 100644 --- a/client/common/lib/elements/configuration-section-header.ts +++ b/client/common/lib/elements/configuration-section-header.ts @@ -7,7 +7,7 @@ import { customElement, property } from 'lit/decorators.js' import { LivechatElement } from './livechat' @customElement('livechat-configuration-section-header') -export class ConfigurationRowElement extends LivechatElement { +export class ConfigurationSectionHeaderElement extends LivechatElement { @property({ attribute: false }) public override title: string = 'title' diff --git a/client/common/lib/elements/dynamic-table-form.ts b/client/common/lib/elements/dynamic-table-form.ts index 9f1975dc..b7999124 100644 --- a/client/common/lib/elements/dynamic-table-form.ts +++ b/client/common/lib/elements/dynamic-table-form.ts @@ -522,7 +522,12 @@ export class DynamicTableFormElement extends LivechatElement { return html` { return html`