diff --git a/client/@types/global.d.ts b/client/@types/global.d.ts
index d76c493e..c5a0f177 100644
--- a/client/@types/global.d.ts
+++ b/client/@types/global.d.ts
@@ -61,6 +61,8 @@ declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REGEXP_LABEL: s
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REGEXP_DESC: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_LABEL: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_DESC: string
+declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_LABEL: string
+declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_DESC: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_COMMENTS_LABEL: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_COMMENTS_DESC: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_COMMAND_LABEL: string
diff --git a/client/common/configuration/register.ts b/client/common/configuration/register.ts
index f408a07c..d3b09cf5 100644
--- a/client/common/configuration/register.ts
+++ b/client/common/configuration/register.ts
@@ -4,8 +4,8 @@
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
import { renderConfigurationHome } from './templates/home'
-import { renderConfigurationChannel } from './templates/channel'
-import { render } from 'lit'
+import './templates/ChannelConfigurationElement'
+import { html, render } from 'lit'
/**
* Registers stuff related to the user's configuration pages.
@@ -29,7 +29,7 @@ async function registerConfiguration (clientOptions: RegisterClientOptions): Pro
onMount: async ({ rootEl }) => {
const urlParams = new URLSearchParams(window.location.search)
const channelId = urlParams.get('channelId') ?? ''
- render(await renderConfigurationChannel(clientOptions, channelId, rootEl), rootEl)
+ render(html` `, rootEl)
}
})
diff --git a/client/common/configuration/templates/ChannelConfigurationElement.ts b/client/common/configuration/templates/ChannelConfigurationElement.ts
index 9343ef6d..1cc5ea95 100644
--- a/client/common/configuration/templates/ChannelConfigurationElement.ts
+++ b/client/common/configuration/templates/ChannelConfigurationElement.ts
@@ -1,42 +1,146 @@
+import { RegisterClientOptions } from '@peertube/peertube-types/client'
import { html, LitElement } from 'lit'
import { repeat } from 'lit-html/directives/repeat.js'
import { customElement, property } from 'lit/decorators.js'
+import { ptTr } from './TranslationDirective'
+import { localizedHelpUrl } from '../../../utils/help'
+import './DynamicTableFormElement'
+import './PluginConfigurationRow'
+import { until } from 'async'
+import { Task } from '@lit/task';
@customElement('channel-configuration')
export class ChannelConfigurationElement extends LitElement {
- @property()
- public list: string[] = ["foo", "bar", "baz"]
-
- @property()
- public newEl: string = 'change_me'
+ @property({ attribute: false })
+ public registerClientOptions: RegisterClientOptions | undefined
createRenderRoot = () => {
return this
}
- render() {
- return html`
-
- `
- }
+ private _asyncTaskRender = new Task(this, {
- private _addToList(newEl: string) {
- return () => {
- this.list.push(newEl)
- this.requestUpdate('list')
- }
- }
+ task: async ([registerClientOptions], {signal}) => {
+ let link = registerClientOptions ? await localizedHelpUrl(registerClientOptions, { page: 'documentation/user/streamers/bot/forbidden_words' }) : '';
- private _removeFromList(index: number) {
- return () => {
- this.list.splice(index, 1)
- this.requestUpdate('list')
+ return {
+ url : new URL(link),
+ title: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC)
+ }
+ },
+
+ args: () => [this.registerClientOptions]
+
+ });
+
+ render = () => {
+ let tableHeader = {
+ words: {
+ colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL),
+ description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC2)
+ },
+ regex: {
+ 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)
+ }
}
+ let tableSchema = {
+ words: {
+ inputType: 'text',
+ default: 'helloqwesad'
+ },
+ regex: {
+ inputType: 'text',
+ default: 'helloaxzca'
+ },
+ applyToModerators: {
+ inputType: 'checkbox',
+ default: true
+ },
+ label: {
+ inputType: 'text',
+ default: 'helloasx'
+ },
+ reason: {
+ inputType: 'select',
+ default: 'transphobia',
+ label: 'choose your poison',
+ options: {'racism': 'Racism', 'sexism': 'Sexism', 'transphobia': 'Transphobia', 'bigotry': 'Bigotry'}
+ },
+ comments: {
+ inputType: 'textarea',
+ default: `Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
+ nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
+ reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
+ culpa qui officia deserunt mollit anim id est laborum.`
+ },
+ }
+ let tableRows = [
+ {
+ words: 'teweqwst',
+ regex: 'tesdgst',
+ applyToModerators: false,
+ label: 'teswet',
+ reason: 'sexism',
+ comments: 'tsdaswest',
+ },
+ {
+ words: 'tedsadst',
+ regex: 'tezxccst',
+ applyToModerators: true,
+ label: 'tewest',
+ reason: 'racism',
+ comments: 'tesxzct',
+ },
+ {
+ words: 'tesadsdxst',
+ regex: 'dsfsdf',
+ applyToModerators: false,
+ label: 'tesdadst',
+ reason: 'bigotry',
+ comments: 'tsadest',
+ },
+ ]
+
+ return this._asyncTaskRender.render({
+ complete: (helpLink) => html`
+ `
+ })
}
}
diff --git a/client/common/configuration/templates/DynamicTableFormElement.ts b/client/common/configuration/templates/DynamicTableFormElement.ts
index 80c32cff..4b7bed0b 100644
--- a/client/common/configuration/templates/DynamicTableFormElement.ts
+++ b/client/common/configuration/templates/DynamicTableFormElement.ts
@@ -1,6 +1,9 @@
import { html, LitElement, TemplateResult } from 'lit'
import { repeat } from 'lit/directives/repeat.js'
import { customElement, property, state } from 'lit/decorators.js'
+import { unsafeHTML } from 'lit/directives/unsafe-html.js'
+import { ifDefined } from 'lit/directives/if-defined.js'
+import { StaticValue, unsafeStatic } from 'lit/static-html.js'
type DynamicTableAcceptedTypes = number | string | boolean | Date
@@ -41,7 +44,7 @@ interface CellDataSchema {
export class DynamicTableFormElement extends LitElement {
@property({ attribute: false })
- public header: { [key : string]: TemplateResult<1> } = {}
+ public header: { [key : string]: { colName: TemplateResult, description: TemplateResult } } = {}
@property({ attribute: false })
@@ -87,55 +90,51 @@ export class DynamicTableFormElement extends LitElement {
render = () => {
const inputId = `peertube-livechat-${this.formName.replaceAll('_','-')}-table`
+ for(let row of this.rows) {
+ if (!row._id) {
+ row._id = this._lastRowId++
+ }
+ }
return html`
-
-
-
Bot command #1
-
You can configure the bot to respond to commands. A command is a message starting with a "!", like for example "!help" that calls the "help" command. For more information about how to configure this feature, please refer to the documentation by clicking on the help button.
-
-
-
-
-
-
-
-
-
- ${JSON.stringify(this.rows)}
+
`
}
private _renderHeader = () => {
- return html`# ${Object.values(this.header).map(this._renderHeaderCell)}Remove Row `
+ return html`
+
+
+ ${Object.values(this.header).map(this._renderHeaderCell)}
+ Remove Row
+
+ `
}
- private _renderHeaderCell = (headerCellData: TemplateResult<1> | any) => {
- return html`${headerCellData} `
-
+ private _renderHeaderCell = (headerCellData: { colName: TemplateResult, description: TemplateResult }) => {
+ return html`
+ ${headerCellData.colName}
+ `
}
private _renderDataRow = (rowData: { _id: number; [key : string]: DynamicTableAcceptedTypes }) => {
- if (!rowData._id) {
- rowData._id = this._lastRowId++
-
- }
-
const inputId = `peertube-livechat-${this.formName.replaceAll('_','-')}-row-${rowData._id}`
- return html`${rowData._id} ${repeat(Object.entries(rowData).filter(([k,v]) => k != '_id'), (data) => this.renderDataCell(data, rowData._id))} this._removeRow(rowData._id)}>Remove `
+ return html`
+
+ ${Object.entries(rowData).filter(([k,v]) => k != '_id').map((data) => this.renderDataCell(data, rowData._id))}
+ this._removeRow(rowData._id)}>Remove
+ `
}
@@ -175,12 +174,12 @@ export class DynamicTableFormElement extends LitElement {
name=${inputName}
class="form-control"
id=${inputId}
- min=${propertySchema?.min}
- max=${propertySchema?.max}
- minlength=${propertySchema?.minlength}
- maxlength=${propertySchema?.maxlength}
- @oninput=${(event: InputEvent) => this._updatePropertyFromValue(event, propertyName, rowId)}
- .value=${propertyValue}
+ min=${ifDefined(propertySchema?.min)}
+ max=${ifDefined(propertySchema?.max)}
+ minlength=${ifDefined(propertySchema?.minlength)}
+ maxlength=${ifDefined(propertySchema?.maxlength)}
+ @input=${(event: InputEvent) => this._updatePropertyFromValue(event, propertyName, rowId)}
+ .value=${propertyValue as string}
/>`
break
@@ -189,19 +188,26 @@ export class DynamicTableFormElement extends LitElement {
name=${inputName}
class="form-control"
id=${inputId}
- min=${propertySchema?.min}
- max=${propertySchema?.max}
- minlength=${propertySchema?.minlength}
- maxlength=${propertySchema?.maxlength}
- @oninput=${(event: InputEvent) => this._updatePropertyFromValue(event, propertyName, rowId)}
- .value=${propertyValue}
+ min=${ifDefined(propertySchema?.min)}
+ max=${ifDefined(propertySchema?.max)}
+ minlength=${ifDefined(propertySchema?.minlength)}
+ maxlength=${ifDefined(propertySchema?.maxlength)}
+ @input=${(event: InputEvent) => this._updatePropertyFromValue(event, propertyName, rowId)}
+ .value=${propertyValue as string}
>`
break
case 'select':
- formElement = html`
+ formElement = html` this._updatePropertyFromValue(event, propertyName, rowId)}
+ >
${propertySchema?.label ?? 'Choose your option'}
- ${Object.entries(propertySchema?.options ?? {})?.map(([value,name]) => html`${name} `)}
+ ${Object.entries(propertySchema?.options ?? {})
+ ?.map(([value,name]) =>
+ html`${name} `
+ )}
`
break
@@ -222,12 +228,12 @@ export class DynamicTableFormElement extends LitElement {
name=${inputName}
class="form-control"
id=${inputId}
- min=${propertySchema?.min}
- max=${propertySchema?.max}
- minlength=${propertySchema?.minlength}
- maxlength=${propertySchema?.maxlength}
- @oninput=${(event: InputEvent) => this._updatePropertyFromValue(event, propertyName, rowId)}
- .value=${propertyValue}
+ min=${ifDefined(propertySchema?.min)}
+ max=${ifDefined(propertySchema?.max)}
+ minlength=${ifDefined(propertySchema?.minlength)}
+ maxlength=${ifDefined(propertySchema?.maxlength)}
+ @input=${(event: InputEvent) => this._updatePropertyFromValue(event, propertyName, rowId)}
+ .value=${(propertyValue as Date).toISOString()}
/>`
break
@@ -246,12 +252,12 @@ export class DynamicTableFormElement extends LitElement {
name=${inputName}
class="form-control"
id=${inputId}
- min=${propertySchema?.min}
- max=${propertySchema?.max}
- minlength=${propertySchema?.minlength}
- maxlength=${propertySchema?.maxlength}
- @oninput=${(event: InputEvent) => this._updatePropertyFromValue(event, propertyName, rowId)}
- .value=${propertyValue}
+ min=${ifDefined(propertySchema?.min)}
+ max=${ifDefined(propertySchema?.max)}
+ minlength=${ifDefined(propertySchema?.minlength)}
+ maxlength=${ifDefined(propertySchema?.maxlength)}
+ @input=${(event: InputEvent) => this._updatePropertyFromValue(event, propertyName, rowId)}
+ .value=${propertyValue as String}
/>`
break
@@ -269,9 +275,9 @@ export class DynamicTableFormElement extends LitElement {
name=${inputName}
class="form-check-input"
id=${inputId}
- @oninput=${(event: InputEvent) => this._updatePropertyFromValue(event, propertyName, rowId)}
- value=""
- ?checked=${propertyValue}
+ @input=${(event: InputEvent) => this._updatePropertyFromValue(event, propertyName, rowId)}
+ .value=${propertyValue as String}
+ ?checked=${propertyValue as Boolean}
/>`
break
@@ -281,24 +287,26 @@ export class DynamicTableFormElement extends LitElement {
}
if (!formElement) {
- console.warn(`value type '${propertyValue.constructor}' is incompatible with field type '${propertySchema.inputType}' for form entry '${propertyName.toString()}'.`)
+ console.warn(`value type '${propertyValue.constructor}' is incompatible`
+ + `with field type '${propertySchema.inputType}' for form entry '${propertyName.toString()}'.`)
}
- console.log
-
return html`${formElement} `
}
- _updatePropertyFromValue(event: InputEvent, propertyName: string, rowId : number) {
+ _updatePropertyFromValue(event: Event, propertyName: string, rowId : number) {
let target = event?.target as (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)
+ let value = (target && target instanceof HTMLInputElement && target.type == "checkbox") ? !!(target?.checked) : target?.value
- if(target?.value) {
+ if(value !== undefined) {
for(let row of this.rows) {
if(row._id === rowId) {
- row[propertyName] = target?.value
+ row[propertyName] = value
+
+ this.requestUpdate('rows')
return
}
diff --git a/client/common/configuration/templates/PluginConfigurationRow.ts b/client/common/configuration/templates/PluginConfigurationRow.ts
new file mode 100644
index 00000000..b275d21b
--- /dev/null
+++ b/client/common/configuration/templates/PluginConfigurationRow.ts
@@ -0,0 +1,41 @@
+import { html, LitElement } from 'lit'
+import { customElement, property } from 'lit/decorators.js'
+import { unsafeSVG } from 'lit/directives/unsafe-svg.js'
+import { StaticValue } from 'lit/static-html.js'
+import { helpButtonSVG } from '../../../videowatch/buttons'
+
+@customElement('plugin-configuration-row')
+export class PLuginConfigurationRow extends LitElement {
+
+ @property({ attribute: false })
+ public title: string = `title`
+
+ @property({ attribute: false })
+ public description: string = `Here's a description`
+
+ @property({ attribute: false })
+ public helpLink: { url: URL, title: string } = { url : new URL('https://lmddgtfy.net/'), title: 'Online Help'}
+
+ createRenderRoot = () => {
+ return this
+ }
+
+ render() {
+ return html`
+ `
+ }
+}
diff --git a/client/common/configuration/templates/TranslationDirective.ts b/client/common/configuration/templates/TranslationDirective.ts
new file mode 100644
index 00000000..94644a03
--- /dev/null
+++ b/client/common/configuration/templates/TranslationDirective.ts
@@ -0,0 +1,41 @@
+import { PartInfo, directive } from 'lit/directive.js'
+import { AsyncDirective } from 'lit/async-directive.js'
+import { RegisterClientHelpers } from '@peertube/peertube-types/client';
+
+export class TranslationDirective extends AsyncDirective {
+
+ private _peertubeHelpers?: RegisterClientHelpers
+
+ private _translatedValue : string = ''
+ private _localizationId : string = ''
+
+ constructor(partInfo: PartInfo) {
+ super(partInfo);
+
+ //_peertubeOptionsPromise.then((options) => this._peertubeHelpers = options.peertubeHelpers)
+ }
+
+ override render = (locId: string) => {
+ this._localizationId = locId // TODO Check current component for context (to infer the prefix)
+
+ if (this._translatedValue === '') {
+ this._translatedValue = locId
+ }
+
+ this._asyncUpdateTranslation()
+
+ return this._translatedValue
+ }
+
+ _asyncUpdateTranslation = async () => {
+ let newValue = await this._peertubeHelpers?.translate(this._localizationId) ?? ''
+
+ if (newValue !== '' && newValue !== this._translatedValue) {
+ this._translatedValue = newValue
+ this.setValue(newValue)
+ }
+ }
+}
+
+export const ptTr = directive(TranslationDirective)
+
diff --git a/client/common/configuration/templates/channel.ts b/client/common/configuration/templates/channel.ts
index 744d0b1f..dc1ca49a 100644
--- a/client/common/configuration/templates/channel.ts
+++ b/client/common/configuration/templates/channel.ts
@@ -2,16 +2,20 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
-import type { RegisterClientOptions } from '@peertube/peertube-types/client'
+import type { RegisterClientHelpers, RegisterClientOptions } from '@peertube/peertube-types/client'
import { localizedHelpUrl } from '../../../utils/help'
import { helpButtonSVG } from '../../../videowatch/buttons'
import { getConfigurationChannelViewData } from './logic/channel'
import { TemplateResult, html } from 'lit'
import { unsafeHTML } from 'lit/directives/unsafe-html.js'
+import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
// Must use require for mustache, import seems buggy.
const Mustache = require('mustache')
+
import './DynamicTableFormElement'
import './ChannelConfigurationElement'
+import './PluginConfigurationRow'
+import { ptTr } from './TranslationDirective'
/**
* Renders the configuration settings page for a given channel,
@@ -26,6 +30,8 @@ async function renderConfigurationChannel (
channelId: string,
rootEl: HTMLElement
): Promise {
+ const peertubeHelpers = registerClientOptions.peertubeHelpers
+
try {
const view : {[key: string] : any} = await getConfigurationChannelViewData(registerClientOptions, channelId)
await fillViewHelpButtons(registerClientOptions, view)
@@ -34,12 +40,30 @@ async function renderConfigurationChannel (
//await vivifyConfigurationChannel(registerClientOptions, rootEl, channelId)
let tableHeader = {
- words: html`${view.forbiddenWords}
`,
- regex: html`${view.forbiddenWordsRegexp}
`,
- applyToModerators: html`${view.forbiddenWordsApplyToModerators}
`,
- label: html`${view.forbiddenWordsLabel}
`,
- reason: html`${view.forbiddenWordsReason}
`,
- comments: html`${view.forbiddenWordsComments}
`,
+ words: {
+ colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL),
+ description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC2)
+ },
+ regex: {
+ 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)
+ }
}
let tableSchema = {
words: {
@@ -102,19 +126,30 @@ async function renderConfigurationChannel (
},
]
- return html`${unsafeHTML(Mustache.render(MUSTACHE_CONFIGURATION_CHANNEL, view))}
+ let helpLink = {
+ url : new URL(await localizedHelpUrl(registerClientOptions, { page: 'documentation/user/streamers/bot/forbidden_words' })),
+ title: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC)
+ }
+
+ return html`
-
-
-
${JSON.stringify(tableRows)}`
+
+
+
+ `
} catch (err: any) {
- registerClientOptions.peertubeHelpers.notifier.error(err.toString())
+ peertubeHelpers.notifier.error(err.toString())
return html``
}
}
@@ -150,7 +185,8 @@ async function fillLabels (
registerClientOptions: RegisterClientOptions,
view: {[key: string] : string}
): Promise {
- const { peertubeHelpers } = registerClientOptions
+ const peertubeHelpers = registerClientOptions.peertubeHelpers
+
view.title = await peertubeHelpers.translate(LOC_LIVECHAT_CONFIGURATION_CHANNEL_TITLE)
view.description = await peertubeHelpers.translate(LOC_LIVECHAT_CONFIGURATION_CHANNEL_DESC)
diff --git a/client/common/configuration/templates/home.ts b/client/common/configuration/templates/home.ts
index f343e7cd..342e24ce 100644
--- a/client/common/configuration/templates/home.ts
+++ b/client/common/configuration/templates/home.ts
@@ -5,7 +5,10 @@
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
import { localizedHelpUrl } from '../../../utils/help'
import { helpButtonSVG } from '../../../videowatch/buttons'
-import { TemplateResult, html } from 'lit';
+import { TemplateResult, html } from 'lit'
+import { unsafeHTML } from 'lit/directives/unsafe-html.js'
+import { ptTr } from './TranslationDirective'
+import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
interface HomeViewData {
title: string
@@ -86,9 +89,9 @@ async function _fillViewHelpButtons ( // TODO: refactor with the similar functio
return html`${helpIcon} `
+ >${unsafeHTML(helpIcon)}`
}
return button('documentation/user/streamers/channel')
diff --git a/languages/en.yml b/languages/en.yml
index bc6e2c55..d200d801 100644
--- a/languages/en.yml
+++ b/languages/en.yml
@@ -398,6 +398,8 @@ livechat_configuration_channel_forbidden_words_reason_label: "Reason"
livechat_configuration_channel_forbidden_words_reason_desc: "Reason to display besides deleted messages"
livechat_configuration_channel_forbidden_words_regexp_label: "Consider as regular expressions"
livechat_configuration_channel_forbidden_words_regexp_desc: "By checking this option, you can use regular expressions."
+livechat_configuration_channel_forbidden_words_label_label: "Label"
+livechat_configuration_channel_forbidden_words_label_desc: "Label for this forbidden words rule"
livechat_configuration_channel_forbidden_words_applytomoderators_label: "Also moderate messages from moderators"
livechat_configuration_channel_forbidden_words_applytomoderators_desc: |
By default, moderator messages will not be deleted when containing forbidden words.
diff --git a/package-lock.json b/package-lock.json
index 6855f716..ba13ba0b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,8 @@
"version": "10.0.2",
"license": "AGPL-3.0",
"dependencies": {
+ "@lit/context": "^1.1.1",
+ "@lit/task": "^1.0.0",
"@xmpp/jid": "^0.13.1",
"async": "^3.2.2",
"decache": "^4.6.0",
@@ -18,6 +20,7 @@
"lit": "^3.1.3",
"log-rotate": "^0.2.8",
"openid-client": "^5.6.5",
+ "rxjs": "^7.8.1",
"validate-color": "^2.2.1",
"xmppjs-chat-bot": "^0.3.0"
},
@@ -2670,6 +2673,14 @@
"resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz",
"integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g=="
},
+ "node_modules/@lit/context": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@lit/context/-/context-1.1.1.tgz",
+ "integrity": "sha512-q/Rw7oWSJidUP43f/RUPwqZ6f5VlY8HzinTWxL/gW1Hvm2S5q2hZvV+qM8WFcC+oLNNknc3JKsd5TwxLk1hbdg==",
+ "dependencies": {
+ "@lit/reactive-element": "^1.6.2 || ^2.0.0"
+ }
+ },
"node_modules/@lit/reactive-element": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz",
@@ -2678,6 +2689,14 @@
"@lit-labs/ssr-dom-shim": "^1.2.0"
}
},
+ "node_modules/@lit/task": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@lit/task/-/task-1.0.0.tgz",
+ "integrity": "sha512-7jocGBh3yGlo3kKxQggZph2txK4X5GYNWp2FAsmV9u2spzUypwrzRzXe8I72icAb02B00+k2nlvxVcrQB6vyrw==",
+ "dependencies": {
+ "@lit/reactive-element": "^1.0.0 || ^2.0.0"
+ }
+ },
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz",
@@ -10516,6 +10535,19 @@
"integrity": "sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==",
"dev": true
},
+ "node_modules/rxjs": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+ "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/rxjs/node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+ },
"node_modules/safe-array-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz",
@@ -14289,6 +14321,14 @@
"resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz",
"integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g=="
},
+ "@lit/context": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@lit/context/-/context-1.1.1.tgz",
+ "integrity": "sha512-q/Rw7oWSJidUP43f/RUPwqZ6f5VlY8HzinTWxL/gW1Hvm2S5q2hZvV+qM8WFcC+oLNNknc3JKsd5TwxLk1hbdg==",
+ "requires": {
+ "@lit/reactive-element": "^1.6.2 || ^2.0.0"
+ }
+ },
"@lit/reactive-element": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz",
@@ -14297,6 +14337,14 @@
"@lit-labs/ssr-dom-shim": "^1.2.0"
}
},
+ "@lit/task": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@lit/task/-/task-1.0.0.tgz",
+ "integrity": "sha512-7jocGBh3yGlo3kKxQggZph2txK4X5GYNWp2FAsmV9u2spzUypwrzRzXe8I72icAb02B00+k2nlvxVcrQB6vyrw==",
+ "requires": {
+ "@lit/reactive-element": "^1.0.0 || ^2.0.0"
+ }
+ },
"@msgpackr-extract/msgpackr-extract-darwin-arm64": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz",
@@ -20289,6 +20337,21 @@
"integrity": "sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==",
"dev": true
},
+ "rxjs": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+ "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "requires": {
+ "tslib": "^2.1.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+ }
+ }
+ },
"safe-array-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz",
diff --git a/package.json b/package.json
index 3447335c..b738cbcb 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,8 @@
"dist/assets/styles/configuration.css"
],
"dependencies": {
+ "@lit/context": "^1.1.1",
+ "@lit/task": "^1.0.0",
"@xmpp/jid": "^0.13.1",
"async": "^3.2.2",
"decache": "^4.6.0",
@@ -42,6 +44,7 @@
"lit": "^3.1.3",
"log-rotate": "^0.2.8",
"openid-client": "^5.6.5",
+ "rxjs": "^7.8.1",
"validate-color": "^2.2.1",
"xmppjs-chat-bot": "^0.3.0"
},