Refactoring: moving some template to separate files + classMap fix.
This commit is contained in:
parent
2c3739f633
commit
4976a4f282
@ -4,16 +4,16 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import type { ChannelConfiguration } from 'shared/lib/types'
|
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 { TemplateResult, html, nothing } from 'lit'
|
||||||
import { customElement, property, state } from 'lit/decorators.js'
|
import { customElement, property, state } from 'lit/decorators.js'
|
||||||
import { ptTr } from '../../lib/directives/translation'
|
import { ptTr } from '../../lib/directives/translation'
|
||||||
import { Task } from '@lit/task'
|
import { Task } from '@lit/task'
|
||||||
import { ChannelDetailsService } from '../services/channel-details'
|
|
||||||
import { provide } from '@lit/context'
|
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')
|
@customElement('livechat-channel-configuration')
|
||||||
export class ChannelConfigurationElement extends LivechatElement {
|
export class ChannelConfigurationElement extends LivechatElement {
|
||||||
@ -22,16 +22,16 @@ export class ChannelConfigurationElement extends LivechatElement {
|
|||||||
|
|
||||||
@provide({ context: channelConfigurationContext })
|
@provide({ context: channelConfigurationContext })
|
||||||
@state()
|
@state()
|
||||||
public _channelConfiguration?: ChannelConfiguration
|
public channelConfiguration?: ChannelConfiguration
|
||||||
|
|
||||||
@provide({ context: channelDetailsServiceContext })
|
@provide({ context: channelDetailsServiceContext })
|
||||||
private _channelDetailsService?: ChannelDetailsService
|
private _channelDetailsService?: ChannelDetailsService
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
public _validationError?: ValidationError
|
public validationError?: ValidationError
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private _actionDisabled: boolean = false
|
public actionDisabled: boolean = false
|
||||||
|
|
||||||
private _asyncTaskRender: Task
|
private _asyncTaskRender: Task
|
||||||
|
|
||||||
@ -44,37 +44,44 @@ export class ChannelConfigurationElement extends LivechatElement {
|
|||||||
return new Task(this, {
|
return new Task(this, {
|
||||||
task: async () => {
|
task: async () => {
|
||||||
this._channelDetailsService = new ChannelDetailsService(this.ptOptions)
|
this._channelDetailsService = new ChannelDetailsService(this.ptOptions)
|
||||||
this._channelConfiguration = await this._channelDetailsService.fetchConfiguration(this.channelId ?? 0)
|
this.channelConfiguration = await this._channelDetailsService.fetchConfiguration(this.channelId ?? 0)
|
||||||
this._actionDisabled = false // in case of reset
|
this.actionDisabled = false // in case of reset
|
||||||
},
|
},
|
||||||
args: () => []
|
args: () => []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _reset (event?: Event): Promise<void> {
|
/**
|
||||||
|
* Resets the form by reloading data from backend.
|
||||||
|
*/
|
||||||
|
public async reset (event?: Event): Promise<void> {
|
||||||
event?.preventDefault()
|
event?.preventDefault()
|
||||||
this._actionDisabled = true
|
this.actionDisabled = true
|
||||||
this._asyncTaskRender = this._initTask()
|
this._asyncTaskRender = this._initTask()
|
||||||
this.requestUpdate()
|
this.requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly _saveConfig = async (event?: Event): Promise<void> => {
|
/**
|
||||||
|
* Saves the channel configuration.
|
||||||
|
* @param event event
|
||||||
|
*/
|
||||||
|
public readonly saveConfig = async (event?: Event): Promise<void> => {
|
||||||
event?.preventDefault()
|
event?.preventDefault()
|
||||||
if (this._channelDetailsService && this._channelConfiguration) {
|
if (this._channelDetailsService && this.channelConfiguration) {
|
||||||
this._actionDisabled = true
|
this.actionDisabled = true
|
||||||
this._channelDetailsService.saveOptions(this._channelConfiguration.channel.id,
|
this._channelDetailsService.saveOptions(this.channelConfiguration.channel.id,
|
||||||
this._channelConfiguration.configuration)
|
this.channelConfiguration.configuration)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this._validationError = undefined
|
this.validationError = undefined
|
||||||
this.ptTranslate(LOC_SUCCESSFULLY_SAVED).then((msg) => {
|
this.ptTranslate(LOC_SUCCESSFULLY_SAVED).then((msg) => {
|
||||||
this.ptNotifier.info(msg)
|
this.ptNotifier.info(msg)
|
||||||
}, () => {})
|
}, () => {})
|
||||||
this.requestUpdate('_validationError')
|
this.requestUpdate('_validationError')
|
||||||
})
|
})
|
||||||
.catch(async (error: Error) => {
|
.catch(async (error: Error) => {
|
||||||
this._validationError = undefined
|
this.validationError = undefined
|
||||||
if (error instanceof ValidationError) {
|
if (error instanceof ValidationError) {
|
||||||
this._validationError = error
|
this.validationError = error
|
||||||
}
|
}
|
||||||
console.warn(`A validation error occurred in saving configuration. ${error.name}: ${error.message}`)
|
console.warn(`A validation error occurred in saving configuration. ${error.name}: ${error.message}`)
|
||||||
this.ptNotifier.error(
|
this.ptNotifier.error(
|
||||||
@ -85,22 +92,22 @@ export class ChannelConfigurationElement extends LivechatElement {
|
|||||||
this.requestUpdate('_validationError')
|
this.requestUpdate('_validationError')
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.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 =
|
const validationErrorTypes: ValidationErrorType[] | undefined =
|
||||||
this._validationError?.properties[`${propertyName}`]
|
this.validationError?.properties[`${propertyName}`]
|
||||||
return validationErrorTypes ? (validationErrorTypes.length ? { 'is-invalid': true } : { 'is-valid': true }) : {}
|
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 => {
|
propertyName: string): TemplateResult | typeof nothing => {
|
||||||
const errorMessages: TemplateResult[] = []
|
const errorMessages: TemplateResult[] = []
|
||||||
const validationErrorTypes: ValidationErrorType[] | undefined =
|
const validationErrorTypes: ValidationErrorType[] | undefined =
|
||||||
this._validationError?.properties[`${propertyName}`] ?? undefined
|
this.validationError?.properties[`${propertyName}`] ?? undefined
|
||||||
|
|
||||||
if (validationErrorTypes && validationErrorTypes.length !== 0) {
|
if (validationErrorTypes && validationErrorTypes.length !== 0) {
|
||||||
if (validationErrorTypes.includes(ValidationErrorType.WrongType)) {
|
if (validationErrorTypes.includes(ValidationErrorType.WrongType)) {
|
||||||
@ -120,283 +127,10 @@ export class ChannelConfigurationElement extends LivechatElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override render = (): unknown => {
|
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({
|
return this._asyncTaskRender.render({
|
||||||
pending: () => html`<livechat-spinner></livechat-spinner>`,
|
pending: () => html`<livechat-spinner></livechat-spinner>`,
|
||||||
error: () => html`<livechat-error></livechat-error>`,
|
error: () => html`<livechat-error></livechat-error>`,
|
||||||
complete: () => html`
|
complete: () => tplChannelConfiguration(this)
|
||||||
<div class="margin-content peertube-plugin-livechat-configuration
|
|
||||||
peertube-plugin-livechat-configuration-channel">
|
|
||||||
<h1>
|
|
||||||
<span class="peertube-plugin-livechat-configuration-channel-info">
|
|
||||||
<span>${this._channelConfiguration?.channel.displayName}</span>
|
|
||||||
<span>${this._channelConfiguration?.channel.name}</span>
|
|
||||||
</span>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<livechat-channel-tabs .active=${'configuration'} .channelId=${this.channelId}></livechat-channel-tabs>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_DESC)}
|
|
||||||
<livechat-help-button .page=${'documentation/user/streamers/channel'}>
|
|
||||||
</livechat-help-button>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form livechat-configuration-channel-options role="form" @submit=${this._saveConfig}>
|
|
||||||
<livechat-configuration-section-header
|
|
||||||
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_SLOW_MODE_LABEL)}
|
|
||||||
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_SLOW_MODE_DESC, true)}
|
|
||||||
.helpPage=${'documentation/user/streamers/slow_mode'}>
|
|
||||||
</livechat-configuration-section-header>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>
|
|
||||||
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_SLOW_MODE_LABEL)}
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="slowmode_duration"
|
|
||||||
class="form-control ${classMap(this._getInputValidationClass('slowMode.duration'))}"
|
|
||||||
min="0"
|
|
||||||
max="1000"
|
|
||||||
id="peertube-livechat-slowmode-duration"
|
|
||||||
aria-describedby="peertube-livechat-slowmode-duration-feedback"
|
|
||||||
@input=${(event: InputEvent) => {
|
|
||||||
if (event?.target && this._channelConfiguration) {
|
|
||||||
this._channelConfiguration.configuration.slowMode.duration =
|
|
||||||
Number((event.target as HTMLInputElement).value)
|
|
||||||
}
|
|
||||||
this.requestUpdate('_channelConfiguration')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value="${this._channelConfiguration?.configuration.slowMode.duration ?? ''}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
${this._renderFeedback('peertube-livechat-slowmode-duration-feedback', 'slowMode.duration')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<livechat-configuration-section-header
|
|
||||||
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_BOT_OPTIONS_TITLE)}
|
|
||||||
.description=${''}
|
|
||||||
.helpPage=${'documentation/user/streamers/channel'}>
|
|
||||||
</livechat-configuration-section-header>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
name="bot"
|
|
||||||
id="peertube-livechat-bot"
|
|
||||||
@input=${(event: InputEvent) => {
|
|
||||||
if (event?.target && this._channelConfiguration) {
|
|
||||||
this._channelConfiguration.configuration.bot.enabled =
|
|
||||||
(event.target as HTMLInputElement).checked
|
|
||||||
}
|
|
||||||
this.requestUpdate('_channelConfiguration')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value="1"
|
|
||||||
?checked=${this._channelConfiguration?.configuration.bot.enabled}
|
|
||||||
/>
|
|
||||||
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_ENABLE_BOT_LABEL)}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
${!this._channelConfiguration?.configuration.bot.enabled
|
|
||||||
? ''
|
|
||||||
: html`<div class="form-group">
|
|
||||||
<label for="peertube-livechat-bot-nickname">
|
|
||||||
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_BOT_NICKNAME)}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="bot_nickname"
|
|
||||||
class="form-control ${classMap(this._getInputValidationClass('bot.nickname'))}"
|
|
||||||
id="peertube-livechat-bot-nickname"
|
|
||||||
aria-describedby="peertube-livechat-bot-nickname-feedback"
|
|
||||||
@input=${(event: InputEvent) => {
|
|
||||||
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')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<livechat-configuration-section-header
|
|
||||||
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL)}
|
|
||||||
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC)}
|
|
||||||
.helpPage=${'documentation/user/streamers/bot/forbidden_words'}>
|
|
||||||
</livechat-configuration-section-header>
|
|
||||||
<livechat-dynamic-table-form
|
|
||||||
.header=${tableHeaderList.forbiddenWords}
|
|
||||||
.schema=${tableSchema.forbiddenWords}
|
|
||||||
.validation=${this._validationError?.properties}
|
|
||||||
.validationPrefix=${'bot.forbiddenWords'}
|
|
||||||
.rows=${this._channelConfiguration?.configuration.bot.forbiddenWords}
|
|
||||||
@update=${(e: CustomEvent) => {
|
|
||||||
if (this._channelConfiguration) {
|
|
||||||
this._channelConfiguration.configuration.bot.forbiddenWords = e.detail
|
|
||||||
this.requestUpdate('_channelConfiguration')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.formName=${'forbidden-words'}
|
|
||||||
></livechat-dynamic-table-form>
|
|
||||||
|
|
||||||
<livechat-configuration-section-header
|
|
||||||
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_LABEL)}
|
|
||||||
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_DESC)}
|
|
||||||
.helpPage=${'documentation/user/streamers/bot/quotes'}>
|
|
||||||
</livechat-configuration-section-header>
|
|
||||||
<livechat-dynamic-table-form
|
|
||||||
.header=${tableHeaderList.quotes}
|
|
||||||
.schema=${tableSchema.quotes}
|
|
||||||
.validation=${this._validationError?.properties}
|
|
||||||
.validationPrefix=${'bot.quotes'}
|
|
||||||
.rows=${this._channelConfiguration?.configuration.bot.quotes}
|
|
||||||
@update=${(e: CustomEvent) => {
|
|
||||||
if (this._channelConfiguration) {
|
|
||||||
this._channelConfiguration.configuration.bot.quotes = e.detail
|
|
||||||
this.requestUpdate('_channelConfiguration')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.formName=${'quote'}
|
|
||||||
></livechat-dynamic-table-form>
|
|
||||||
|
|
||||||
<livechat-configuration-section-header
|
|
||||||
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_LABEL)}
|
|
||||||
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_DESC)}
|
|
||||||
.helpPage=${'documentation/user/streamers/bot/commands'}>
|
|
||||||
</livechat-configuration-section-header>
|
|
||||||
<livechat-dynamic-table-form
|
|
||||||
.header=${tableHeaderList.commands}
|
|
||||||
.schema=${tableSchema.commands}
|
|
||||||
.validation=${this._validationError?.properties}
|
|
||||||
.validationPrefix=${'bot.commands'}
|
|
||||||
.rows=${this._channelConfiguration?.configuration.bot.commands}
|
|
||||||
@update=${(e: CustomEvent) => {
|
|
||||||
if (this._channelConfiguration) {
|
|
||||||
this._channelConfiguration.configuration.bot.commands = e.detail
|
|
||||||
this.requestUpdate('_channelConfiguration')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.formName=${'command'}
|
|
||||||
></livechat-dynamic-table-form>
|
|
||||||
`}
|
|
||||||
|
|
||||||
<div class="form-group mt-5">
|
|
||||||
<button type="reset" @click=${this._reset} ?disabled=${this._actionDisabled}>
|
|
||||||
${ptTr(LOC_CANCEL)}
|
|
||||||
</button>
|
|
||||||
<button type="submit" ?disabled=${this._actionDisabled}>
|
|
||||||
${ptTr(LOC_SAVE)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>`
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,11 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import type { ChannelEmojisConfiguration } from 'shared/lib/types'
|
import type { ChannelEmojisConfiguration } from 'shared/lib/types'
|
||||||
import type { DynamicFormHeader, DynamicFormSchema } from '../../lib/elements/dynamic-table-form'
|
|
||||||
import { LivechatElement } from '../../lib/elements/livechat'
|
import { LivechatElement } from '../../lib/elements/livechat'
|
||||||
import { ChannelDetailsService } from '../services/channel-details'
|
import { ChannelDetailsService } from '../services/channel-details'
|
||||||
import { channelDetailsServiceContext } from '../contexts/channel'
|
import { channelDetailsServiceContext } from '../contexts/channel'
|
||||||
import { maxEmojisPerChannel } from 'shared/lib/emojis'
|
|
||||||
import { ptTr } from '../../lib/directives/translation'
|
|
||||||
import { ValidationError } from '../../lib/models/validation'
|
import { ValidationError } from '../../lib/models/validation'
|
||||||
|
import { tplChannelEmojis } from './templates/channel-emojis'
|
||||||
import { Task } from '@lit/task'
|
import { Task } from '@lit/task'
|
||||||
import { customElement, property, state } from 'lit/decorators.js'
|
import { customElement, property, state } from 'lit/decorators.js'
|
||||||
import { provide } from '@lit/context'
|
import { provide } from '@lit/context'
|
||||||
@ -23,16 +21,16 @@ export class ChannelEmojisElement extends LivechatElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public channelId?: number
|
public channelId?: number
|
||||||
|
|
||||||
private _channelEmojisConfiguration?: ChannelEmojisConfiguration
|
public channelEmojisConfiguration?: ChannelEmojisConfiguration
|
||||||
|
|
||||||
@provide({ context: channelDetailsServiceContext })
|
@provide({ context: channelDetailsServiceContext })
|
||||||
private _channelDetailsService?: ChannelDetailsService
|
private _channelDetailsService?: ChannelDetailsService
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private _validationError?: ValidationError
|
public validationError?: ValidationError
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private _actionDisabled: boolean = false
|
public actionDisabled: boolean = false
|
||||||
|
|
||||||
private _asyncTaskRender: Task
|
private _asyncTaskRender: Task
|
||||||
|
|
||||||
@ -42,111 +40,10 @@ export class ChannelEmojisElement extends LivechatElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override render = (): unknown => {
|
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({
|
return this._asyncTaskRender.render({
|
||||||
pending: () => html`<livechat-spinner></livechat-spinner>`,
|
pending: () => html`<livechat-spinner></livechat-spinner>`,
|
||||||
error: () => html`<livechat-error></livechat-error>`,
|
error: () => html`<livechat-error></livechat-error>`,
|
||||||
complete: () => html`
|
complete: () => tplChannelEmojis(this)
|
||||||
<div
|
|
||||||
class="margin-content peertube-plugin-livechat-configuration peertube-plugin-livechat-configuration-channel"
|
|
||||||
>
|
|
||||||
<h1>
|
|
||||||
<span class="peertube-plugin-livechat-configuration-channel-info">
|
|
||||||
<span>${this._channelEmojisConfiguration?.channel.displayName}</span>
|
|
||||||
<span>${this._channelEmojisConfiguration?.channel.name}</span>
|
|
||||||
</span>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<livechat-channel-tabs .active=${'emojis'} .channelId=${this.channelId}></livechat-channel-tabs>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_EMOJIS_DESC)}
|
|
||||||
<livechat-help-button .page=${'documentation/user/streamers/emojis'}>
|
|
||||||
</livechat-help-button>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
<form role="form" @submit=${this._saveEmojis}>
|
|
||||||
<div class="peertube-plugin-livechat-configuration-actions">
|
|
||||||
${
|
|
||||||
this._channelEmojisConfiguration?.emojis?.customEmojis?.length
|
|
||||||
? html`
|
|
||||||
<button
|
|
||||||
@click=${this._exportEmojis}
|
|
||||||
?disabled=${this._actionDisabled}
|
|
||||||
>
|
|
||||||
${ptTr(LOC_ACTION_EXPORT)}
|
|
||||||
</button>`
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
${
|
|
||||||
(this._channelEmojisConfiguration?.emojis?.customEmojis?.length ?? 0) < maxEmojisPerChannel
|
|
||||||
? html`
|
|
||||||
<button
|
|
||||||
@click=${this._importEmojis}
|
|
||||||
?disabled=${this._actionDisabled}
|
|
||||||
>
|
|
||||||
${ptTr(LOC_ACTION_IMPORT)}
|
|
||||||
</button>`
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<livechat-dynamic-table-form
|
|
||||||
.header=${tableHeaderList}
|
|
||||||
.schema=${tableSchema}
|
|
||||||
.maxLines=${maxEmojisPerChannel}
|
|
||||||
.validation=${this._validationError?.properties}
|
|
||||||
.validationPrefix=${'emojis'}
|
|
||||||
.rows=${this._channelEmojisConfiguration?.emojis.customEmojis}
|
|
||||||
@update=${(e: CustomEvent) => {
|
|
||||||
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')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
></livechat-dynamic-table-form>
|
|
||||||
|
|
||||||
<div class="form-group mt-5">
|
|
||||||
<button type="reset" @click=${this._reset} ?disabled=${this._actionDisabled}>
|
|
||||||
${ptTr(LOC_CANCEL)}
|
|
||||||
</button>
|
|
||||||
<button type="submit" ?disabled=${this._actionDisabled}>
|
|
||||||
${ptTr(LOC_SAVE)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,39 +54,46 @@ export class ChannelEmojisElement extends LivechatElement {
|
|||||||
throw new Error('Missing channelId')
|
throw new Error('Missing channelId')
|
||||||
}
|
}
|
||||||
this._channelDetailsService = new ChannelDetailsService(this.ptOptions)
|
this._channelDetailsService = new ChannelDetailsService(this.ptOptions)
|
||||||
this._channelEmojisConfiguration = await this._channelDetailsService.fetchEmojisConfiguration(this.channelId)
|
this.channelEmojisConfiguration = await this._channelDetailsService.fetchEmojisConfiguration(this.channelId)
|
||||||
this._actionDisabled = false // in case of reset
|
this.actionDisabled = false // in case of reset
|
||||||
},
|
},
|
||||||
args: () => []
|
args: () => []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _reset (ev?: Event): Promise<void> {
|
/**
|
||||||
|
* Resets the page, by reloading data from backend.
|
||||||
|
*/
|
||||||
|
public async reset (ev?: Event): Promise<void> {
|
||||||
ev?.preventDefault()
|
ev?.preventDefault()
|
||||||
this._actionDisabled = true
|
this.actionDisabled = true
|
||||||
this._asyncTaskRender = this._initTask()
|
this._asyncTaskRender = this._initTask()
|
||||||
this.requestUpdate()
|
this.requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _saveEmojis (ev?: Event): Promise<void> {
|
/**
|
||||||
|
* Saves the emojis form.
|
||||||
|
* @param ev event
|
||||||
|
*/
|
||||||
|
public async saveEmojis (ev?: Event): Promise<void> {
|
||||||
ev?.preventDefault()
|
ev?.preventDefault()
|
||||||
|
|
||||||
if (!this._channelDetailsService || !this._channelEmojisConfiguration || !this.channelId) {
|
if (!this._channelDetailsService || !this.channelEmojisConfiguration || !this.channelId) {
|
||||||
this.ptNotifier.error(await this.ptTranslate(LOC_ERROR))
|
this.ptNotifier.error(await this.ptTranslate(LOC_ERROR))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._actionDisabled = true
|
this.actionDisabled = true
|
||||||
await this._channelDetailsService.saveEmojisConfiguration(this.channelId, this._channelEmojisConfiguration.emojis)
|
await this._channelDetailsService.saveEmojisConfiguration(this.channelId, this.channelEmojisConfiguration.emojis)
|
||||||
this._validationError = undefined
|
this.validationError = undefined
|
||||||
this.ptNotifier.info(await this.ptTranslate(LOC_SUCCESSFULLY_SAVED))
|
this.ptNotifier.info(await this.ptTranslate(LOC_SUCCESSFULLY_SAVED))
|
||||||
this.requestUpdate('_validationError')
|
this.requestUpdate('_validationError')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._validationError = undefined
|
this.validationError = undefined
|
||||||
let msg: string
|
let msg: string
|
||||||
if ((error instanceof ValidationError)) {
|
if ((error instanceof ValidationError)) {
|
||||||
this._validationError = error
|
this.validationError = error
|
||||||
if (error.message) {
|
if (error.message) {
|
||||||
msg = error.message
|
msg = error.message
|
||||||
}
|
}
|
||||||
@ -198,13 +102,16 @@ export class ChannelEmojisElement extends LivechatElement {
|
|||||||
this.ptNotifier.error(msg)
|
this.ptNotifier.error(msg)
|
||||||
this.requestUpdate('_validationError')
|
this.requestUpdate('_validationError')
|
||||||
} finally {
|
} finally {
|
||||||
this._actionDisabled = false
|
this.actionDisabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _importEmojis (ev: Event): Promise<void> {
|
/**
|
||||||
|
* Import emojis action.
|
||||||
|
*/
|
||||||
|
public async importEmojis (ev: Event): Promise<void> {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
this._actionDisabled = true
|
this.actionDisabled = true
|
||||||
try {
|
try {
|
||||||
// download a json file:
|
// download a json file:
|
||||||
const file = await new Promise<File>((resolve, reject) => {
|
const file = await new Promise<File>((resolve, reject) => {
|
||||||
@ -266,10 +173,10 @@ export class ChannelEmojisElement extends LivechatElement {
|
|||||||
if (entry.isCategoryEmoji === true) {
|
if (entry.isCategoryEmoji === true) {
|
||||||
item.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(
|
this.ptNotifier.info(
|
||||||
await this.ptTranslate(LOC_ACTION_IMPORT_EMOJIS_INFO)
|
await this.ptTranslate(LOC_ACTION_IMPORT_EMOJIS_INFO)
|
||||||
@ -277,16 +184,19 @@ export class ChannelEmojisElement extends LivechatElement {
|
|||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.ptNotifier.error(err.toString())
|
this.ptNotifier.error(err.toString())
|
||||||
} finally {
|
} finally {
|
||||||
this._actionDisabled = false
|
this.actionDisabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _exportEmojis (ev: Event): Promise<void> {
|
/**
|
||||||
|
* Export emojis action.
|
||||||
|
*/
|
||||||
|
public async exportEmojis (ev: Event): Promise<void> {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
this._actionDisabled = true
|
this.actionDisabled = true
|
||||||
try {
|
try {
|
||||||
const result: ChannelEmojisConfiguration['emojis']['customEmojis'] = []
|
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 }
|
if (!ed.sn || !ed.url) { continue }
|
||||||
// Here url can be:
|
// Here url can be:
|
||||||
// * the dataUrl representation of a newly uploaded file
|
// * the dataUrl representation of a newly uploaded file
|
||||||
@ -313,10 +223,15 @@ export class ChannelEmojisElement extends LivechatElement {
|
|||||||
console.error(err)
|
console.error(err)
|
||||||
this.ptNotifier.error(err.toString())
|
this.ptNotifier.error(err.toString())
|
||||||
} finally {
|
} 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<string> {
|
private async _convertImageToDataUrl (url: string): Promise<string> {
|
||||||
if (url.startsWith('data:')) { return url }
|
if (url.startsWith('data:')) { return url }
|
||||||
// There is a trick to convert img to dataUrl: using a canvas.
|
// There is a trick to convert img to dataUrl: using a canvas.
|
||||||
|
@ -20,9 +20,6 @@ export class ChannelHomeElement extends LivechatElement {
|
|||||||
@provide({ context: channelDetailsServiceContext })
|
@provide({ context: channelDetailsServiceContext })
|
||||||
private _channelDetailsService?: ChannelDetailsService
|
private _channelDetailsService?: ChannelDetailsService
|
||||||
|
|
||||||
@state()
|
|
||||||
public _formStatus: boolean | any = undefined
|
|
||||||
|
|
||||||
private readonly _asyncTaskRender = new Task(this, {
|
private readonly _asyncTaskRender = new Task(this, {
|
||||||
task: async () => {
|
task: async () => {
|
||||||
// Getting the current username in localStorage. Don't know any cleaner way to do.
|
// Getting the current username in localStorage. Don't know any cleaner way to do.
|
||||||
|
@ -0,0 +1,296 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
//
|
||||||
|
// 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`
|
||||||
|
<div class="margin-content peertube-plugin-livechat-configuration
|
||||||
|
peertube-plugin-livechat-configuration-channel">
|
||||||
|
<h1>
|
||||||
|
<span class="peertube-plugin-livechat-configuration-channel-info">
|
||||||
|
<span>${el.channelConfiguration?.channel.displayName}</span>
|
||||||
|
<span>${el.channelConfiguration?.channel.name}</span>
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<livechat-channel-tabs .active=${'configuration'} .channelId=${el.channelId}></livechat-channel-tabs>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_DESC)}
|
||||||
|
<livechat-help-button .page=${'documentation/user/streamers/channel'}>
|
||||||
|
</livechat-help-button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form livechat-configuration-channel-options role="form" @submit=${el.saveConfig}>
|
||||||
|
<livechat-configuration-section-header
|
||||||
|
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_SLOW_MODE_LABEL)}
|
||||||
|
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_SLOW_MODE_DESC, true)}
|
||||||
|
.helpPage=${'documentation/user/streamers/slow_mode'}>
|
||||||
|
</livechat-configuration-section-header>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_SLOW_MODE_LABEL)}
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="slowmode_duration"
|
||||||
|
class=${classMap(
|
||||||
|
Object.assign(
|
||||||
|
{ 'form-control': true },
|
||||||
|
el.getInputValidationClass('slowMode.duration')
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
min="0"
|
||||||
|
max="1000"
|
||||||
|
id="peertube-livechat-slowmode-duration"
|
||||||
|
aria-describedby="peertube-livechat-slowmode-duration-feedback"
|
||||||
|
@input=${(event: InputEvent) => {
|
||||||
|
if (event?.target && el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.slowMode.duration =
|
||||||
|
Number((event.target as HTMLInputElement).value)
|
||||||
|
}
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value="${el.channelConfiguration?.configuration.slowMode.duration ?? ''}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
${el.renderFeedback('peertube-livechat-slowmode-duration-feedback', 'slowMode.duration')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<livechat-configuration-section-header
|
||||||
|
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_BOT_OPTIONS_TITLE)}
|
||||||
|
.description=${''}
|
||||||
|
.helpPage=${'documentation/user/streamers/channel'}>
|
||||||
|
</livechat-configuration-section-header>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="bot"
|
||||||
|
id="peertube-livechat-bot"
|
||||||
|
@input=${(event: InputEvent) => {
|
||||||
|
if (event?.target && el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.enabled =
|
||||||
|
(event.target as HTMLInputElement).checked
|
||||||
|
}
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value="1"
|
||||||
|
?checked=${el.channelConfiguration?.configuration.bot.enabled}
|
||||||
|
/>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_ENABLE_BOT_LABEL)}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${!el.channelConfiguration?.configuration.bot.enabled
|
||||||
|
? ''
|
||||||
|
: html`<div class="form-group">
|
||||||
|
<label for="peertube-livechat-bot-nickname">
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_BOT_NICKNAME)}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="bot_nickname"
|
||||||
|
class=${classMap(
|
||||||
|
Object.assign(
|
||||||
|
{ 'form-control': true },
|
||||||
|
el.getInputValidationClass('bot.nickname')
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
id="peertube-livechat-bot-nickname"
|
||||||
|
aria-describedby="peertube-livechat-bot-nickname-feedback"
|
||||||
|
@input=${(event: InputEvent) => {
|
||||||
|
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')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<livechat-configuration-section-header
|
||||||
|
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL)}
|
||||||
|
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC)}
|
||||||
|
.helpPage=${'documentation/user/streamers/bot/forbidden_words'}>
|
||||||
|
</livechat-configuration-section-header>
|
||||||
|
<livechat-dynamic-table-form
|
||||||
|
.header=${tableHeaderList.forbiddenWords}
|
||||||
|
.schema=${tableSchema.forbiddenWords}
|
||||||
|
.validation=${el.validationError?.properties}
|
||||||
|
.validationPrefix=${'bot.forbiddenWords'}
|
||||||
|
.rows=${el.channelConfiguration?.configuration.bot.forbiddenWords}
|
||||||
|
@update=${(e: CustomEvent) => {
|
||||||
|
if (el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.forbiddenWords = e.detail
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.formName=${'forbidden-words'}
|
||||||
|
></livechat-dynamic-table-form>
|
||||||
|
|
||||||
|
<livechat-configuration-section-header
|
||||||
|
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_LABEL)}
|
||||||
|
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_QUOTE_DESC)}
|
||||||
|
.helpPage=${'documentation/user/streamers/bot/quotes'}>
|
||||||
|
</livechat-configuration-section-header>
|
||||||
|
<livechat-dynamic-table-form
|
||||||
|
.header=${tableHeaderList.quotes}
|
||||||
|
.schema=${tableSchema.quotes}
|
||||||
|
.validation=${el.validationError?.properties}
|
||||||
|
.validationPrefix=${'bot.quotes'}
|
||||||
|
.rows=${el.channelConfiguration?.configuration.bot.quotes}
|
||||||
|
@update=${(e: CustomEvent) => {
|
||||||
|
if (el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.quotes = e.detail
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.formName=${'quote'}
|
||||||
|
></livechat-dynamic-table-form>
|
||||||
|
|
||||||
|
<livechat-configuration-section-header
|
||||||
|
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_LABEL)}
|
||||||
|
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_DESC)}
|
||||||
|
.helpPage=${'documentation/user/streamers/bot/commands'}>
|
||||||
|
</livechat-configuration-section-header>
|
||||||
|
<livechat-dynamic-table-form
|
||||||
|
.header=${tableHeaderList.commands}
|
||||||
|
.schema=${tableSchema.commands}
|
||||||
|
.validation=${el.validationError?.properties}
|
||||||
|
.validationPrefix=${'bot.commands'}
|
||||||
|
.rows=${el.channelConfiguration?.configuration.bot.commands}
|
||||||
|
@update=${(e: CustomEvent) => {
|
||||||
|
if (el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.commands = e.detail
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.formName=${'command'}
|
||||||
|
></livechat-dynamic-table-form>
|
||||||
|
`}
|
||||||
|
|
||||||
|
<div class="form-group mt-5">
|
||||||
|
<button type="reset" @click=${el.reset} ?disabled=${el.actionDisabled}>
|
||||||
|
${ptTr(LOC_CANCEL)}
|
||||||
|
</button>
|
||||||
|
<button type="submit" ?disabled=${el.actionDisabled}>
|
||||||
|
${ptTr(LOC_SAVE)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>`
|
||||||
|
}
|
113
client/common/configuration/elements/templates/channel-emojis.ts
Normal file
113
client/common/configuration/elements/templates/channel-emojis.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
//
|
||||||
|
// 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`
|
||||||
|
<div
|
||||||
|
class="margin-content peertube-plugin-livechat-configuration peertube-plugin-livechat-configuration-channel"
|
||||||
|
>
|
||||||
|
<h1>
|
||||||
|
<span class="peertube-plugin-livechat-configuration-channel-info">
|
||||||
|
<span>${el.channelEmojisConfiguration?.channel.displayName}</span>
|
||||||
|
<span>${el.channelEmojisConfiguration?.channel.name}</span>
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<livechat-channel-tabs .active=${'emojis'} .channelId=${el.channelId}></livechat-channel-tabs>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_EMOJIS_DESC)}
|
||||||
|
<livechat-help-button .page=${'documentation/user/streamers/emojis'}>
|
||||||
|
</livechat-help-button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<form role="form" @submit=${el.saveEmojis}>
|
||||||
|
<div class="peertube-plugin-livechat-configuration-actions">
|
||||||
|
${
|
||||||
|
el.channelEmojisConfiguration?.emojis?.customEmojis?.length
|
||||||
|
? html`
|
||||||
|
<button
|
||||||
|
@click=${el.exportEmojis}
|
||||||
|
?disabled=${el.actionDisabled}
|
||||||
|
>
|
||||||
|
${ptTr(LOC_ACTION_EXPORT)}
|
||||||
|
</button>`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
${
|
||||||
|
(el.channelEmojisConfiguration?.emojis?.customEmojis?.length ?? 0) < maxEmojisPerChannel
|
||||||
|
? html`
|
||||||
|
<button
|
||||||
|
@click=${el.importEmojis}
|
||||||
|
?disabled=${el.actionDisabled}
|
||||||
|
>
|
||||||
|
${ptTr(LOC_ACTION_IMPORT)}
|
||||||
|
</button>`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<livechat-dynamic-table-form
|
||||||
|
.header=${tableHeaderList}
|
||||||
|
.schema=${tableSchema}
|
||||||
|
.maxLines=${maxEmojisPerChannel}
|
||||||
|
.validation=${el.validationError?.properties}
|
||||||
|
.validationPrefix=${'emojis'}
|
||||||
|
.rows=${el.channelEmojisConfiguration?.emojis.customEmojis}
|
||||||
|
@update=${(e: CustomEvent) => {
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
></livechat-dynamic-table-form>
|
||||||
|
|
||||||
|
<div class="form-group mt-5">
|
||||||
|
<button type="reset" @click=${el.reset} ?disabled=${el.actionDisabled}>
|
||||||
|
${ptTr(LOC_CANCEL)}
|
||||||
|
</button>
|
||||||
|
<button type="submit" ?disabled=${el.actionDisabled}>
|
||||||
|
${ptTr(LOC_SAVE)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>`
|
||||||
|
}
|
@ -7,7 +7,7 @@ import { customElement, property } from 'lit/decorators.js'
|
|||||||
import { LivechatElement } from './livechat'
|
import { LivechatElement } from './livechat'
|
||||||
|
|
||||||
@customElement('livechat-configuration-section-header')
|
@customElement('livechat-configuration-section-header')
|
||||||
export class ConfigurationRowElement extends LivechatElement {
|
export class ConfigurationSectionHeaderElement extends LivechatElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public override title: string = 'title'
|
public override title: string = 'title'
|
||||||
|
|
||||||
|
@ -522,7 +522,12 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
return html`<input
|
return html`<input
|
||||||
type=${propertySchema.inputType}
|
type=${propertySchema.inputType}
|
||||||
name=${inputName}
|
name=${inputName}
|
||||||
class="form-control ${classMap(this._getInputValidationClass(propertyName, originalIndex))}"
|
class=${classMap(
|
||||||
|
Object.assign(
|
||||||
|
{ 'form-control': true },
|
||||||
|
this._getInputValidationClass(propertyName, originalIndex)
|
||||||
|
)
|
||||||
|
)}
|
||||||
id=${inputId}
|
id=${inputId}
|
||||||
aria-describedby="${inputId}-feedback"
|
aria-describedby="${inputId}-feedback"
|
||||||
list=${(propertySchema.datalist) ? inputId + '-datalist' : nothing}
|
list=${(propertySchema.datalist) ? inputId + '-datalist' : nothing}
|
||||||
@ -550,7 +555,12 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
return html`<livechat-tags-input
|
return html`<livechat-tags-input
|
||||||
.type=${'text'}
|
.type=${'text'}
|
||||||
.name=${inputName}
|
.name=${inputName}
|
||||||
class="form-control ${classMap(this._getInputValidationClass(propertyName, originalIndex))}"
|
class=${classMap(
|
||||||
|
Object.assign(
|
||||||
|
{ 'form-control': true },
|
||||||
|
this._getInputValidationClass(propertyName, originalIndex)
|
||||||
|
)
|
||||||
|
)}
|
||||||
id=${inputId}
|
id=${inputId}
|
||||||
.inputPlaceholder=${ifDefined(propertySchema.label)}
|
.inputPlaceholder=${ifDefined(propertySchema.label)}
|
||||||
aria-describedby="${inputId}-feedback"
|
aria-describedby="${inputId}-feedback"
|
||||||
@ -573,7 +583,12 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
originalIndex: number): TemplateResult => {
|
originalIndex: number): TemplateResult => {
|
||||||
return html`<textarea
|
return html`<textarea
|
||||||
name=${inputName}
|
name=${inputName}
|
||||||
class="form-control ${classMap(this._getInputValidationClass(propertyName, originalIndex))}"
|
class=${classMap(
|
||||||
|
Object.assign(
|
||||||
|
{ 'form-control': true },
|
||||||
|
this._getInputValidationClass(propertyName, originalIndex)
|
||||||
|
)
|
||||||
|
)}
|
||||||
id=${inputId}
|
id=${inputId}
|
||||||
aria-describedby="${inputId}-feedback"
|
aria-describedby="${inputId}-feedback"
|
||||||
min=${ifDefined(propertySchema.min)}
|
min=${ifDefined(propertySchema.min)}
|
||||||
@ -594,7 +609,12 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
return html`<input
|
return html`<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name=${inputName}
|
name=${inputName}
|
||||||
class="form-check-input ${classMap(this._getInputValidationClass(propertyName, originalIndex))}"
|
class=${classMap(
|
||||||
|
Object.assign(
|
||||||
|
{ 'form-check-input': true },
|
||||||
|
this._getInputValidationClass(propertyName, originalIndex)
|
||||||
|
)
|
||||||
|
)}
|
||||||
id=${inputId}
|
id=${inputId}
|
||||||
aria-describedby="${inputId}-feedback"
|
aria-describedby="${inputId}-feedback"
|
||||||
@change=${(event: Event) => this._updatePropertyFromValue(event, propertyName, propertySchema, rowId)}
|
@change=${(event: Event) => this._updatePropertyFromValue(event, propertyName, propertySchema, rowId)}
|
||||||
@ -610,7 +630,12 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
propertyValue: string,
|
propertyValue: string,
|
||||||
originalIndex: number): TemplateResult => {
|
originalIndex: number): TemplateResult => {
|
||||||
return html`<select
|
return html`<select
|
||||||
class="form-select ${classMap(this._getInputValidationClass(propertyName, originalIndex))}"
|
class=${classMap(
|
||||||
|
Object.assign(
|
||||||
|
{ 'form-select': true },
|
||||||
|
this._getInputValidationClass(propertyName, originalIndex)
|
||||||
|
)
|
||||||
|
)}
|
||||||
id=${inputId}
|
id=${inputId}
|
||||||
aria-describedby="${inputId}-feedback"
|
aria-describedby="${inputId}-feedback"
|
||||||
aria-label=${inputName}
|
aria-label=${inputName}
|
||||||
@ -634,7 +659,7 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
): TemplateResult => {
|
): TemplateResult => {
|
||||||
return html`<livechat-image-file-input
|
return html`<livechat-image-file-input
|
||||||
.name=${inputName}
|
.name=${inputName}
|
||||||
class="${classMap(this._getInputValidationClass(propertyName, originalIndex))}"
|
class=${classMap(this._getInputValidationClass(propertyName, originalIndex))}
|
||||||
id=${inputId}
|
id=${inputId}
|
||||||
aria-describedby="${inputId}-feedback"
|
aria-describedby="${inputId}-feedback"
|
||||||
@change=${(event: Event) => this._updatePropertyFromValue(event, propertyName, propertySchema, rowId)}
|
@change=${(event: Event) => this._updatePropertyFromValue(event, propertyName, propertySchema, rowId)}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user