Using ChannelConfigurationElement + Adding TranslationDirective
and a lots of fixes
This commit is contained in:
parent
0fe9fb4dca
commit
9ea26dfd26
2
client/@types/global.d.ts
vendored
2
client/@types/global.d.ts
vendored
@ -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
|
||||
|
@ -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`<channel-configuration .registerClientOptions=${clientOptions}></channel-configuration>`, rootEl)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -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`
|
||||
<ul>
|
||||
${repeat(this.list, (el: string, index) => html`<li>${el}<button @click=${this._removeFromList(index)}>remove</button></li>`
|
||||
)}
|
||||
<li><input .value=${this.newEl}/><button @click=${this._addToList(this.newEl)}>add</button></li>
|
||||
</ul>
|
||||
`
|
||||
}
|
||||
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`
|
||||
<div class="container">
|
||||
<channel-configuration></channel-configuration>
|
||||
<plugin-configuration-row
|
||||
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL)}
|
||||
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC)}
|
||||
.helpLink=${helpLink}
|
||||
>
|
||||
<dynamic-table-form
|
||||
.header=${tableHeader}
|
||||
.schema=${tableSchema}
|
||||
.rows=${tableRows}
|
||||
.formName=${'forbidden-words'}
|
||||
>
|
||||
</dynamic-table-form>
|
||||
</plugin-configuration-row>
|
||||
</div>`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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`
|
||||
<div class="row mt-5">
|
||||
<div class="col-12 col-lg-4 col-xl-3">
|
||||
<h2>Bot command #1</h2>
|
||||
<p>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.</p>
|
||||
<a href="https://livingston.frama.io/peertube-plugin-livechat/documentation/user/streamers/bot/commands/" target="_blank" title="Online help" class="orange-button peertube-button-link">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 4.233 4.233">
|
||||
<path style="display:inline;opacity:.998;fill:none;fill-opacity:1;stroke:currentColor;stroke-width:.529167;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M1.48 1.583V.86c0-.171.085-.31.19-.31h.893c.106 0 .19.139.19.31v.838c0 .171-.107.219-.19.284l-.404.314c-.136.106-.219.234-.221.489l-.003.247"></path>
|
||||
<path style="display:inline;fill:currentColor;stroke-width:.235169" d="M1.67 3.429h.529v.597H1.67z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-12 col-lg-8 col-xl-9">
|
||||
<table class="table table-striped table-hover table-sm" id=${inputId}>
|
||||
${this._renderHeader()}
|
||||
<tbody>
|
||||
${repeat(this.rows, this._renderDataRow)}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr><td><button @click=${this._addRow}>Add Row</button></td></tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
${JSON.stringify(this.rows)}
|
||||
<table class="table table-striped table-hover table-sm" id=${inputId}>
|
||||
${this._renderHeader()}
|
||||
<tbody>
|
||||
${repeat(this.rows,(row) => row._id, this._renderDataRow)}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr><td><button @click=${this._addRow}>Add Row</button></td></tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
`
|
||||
|
||||
}
|
||||
|
||||
private _renderHeader = () => {
|
||||
return html`<thead><tr><th scope="col">#</th>${Object.values(this.header).map(this._renderHeaderCell)}<th scope="col">Remove Row</th></tr></thead>`
|
||||
return html`<thead>
|
||||
<tr>
|
||||
<!-- <th scope="col">#</th> -->
|
||||
${Object.values(this.header).map(this._renderHeaderCell)}
|
||||
<th scope="col">Remove Row</th>
|
||||
</tr>
|
||||
</thead>`
|
||||
|
||||
}
|
||||
|
||||
private _renderHeaderCell = (headerCellData: TemplateResult<1> | any) => {
|
||||
return html`<th scope="col">${headerCellData}</th>`
|
||||
|
||||
private _renderHeaderCell = (headerCellData: { colName: TemplateResult, description: TemplateResult }) => {
|
||||
return html`<th scope="col">
|
||||
<div data-toggle="tooltip" data-placement="bottom" data-html="true" title="${headerCellData.description}">${headerCellData.colName}</div>
|
||||
</th>`
|
||||
}
|
||||
|
||||
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`<tr id=${inputId}><td class="form-group">${rowData._id}</td>${repeat(Object.entries(rowData).filter(([k,v]) => k != '_id'), (data) => this.renderDataCell(data, rowData._id))}<td class="form-group"><button @click=${() => this._removeRow(rowData._id)}>Remove</button></td></tr>`
|
||||
return html`<tr id=${inputId}>
|
||||
<!-- <td class="form-group">${rowData._id}</td> -->
|
||||
${Object.entries(rowData).filter(([k,v]) => k != '_id').map((data) => this.renderDataCell(data, rowData._id))}
|
||||
<td class="form-group"><button @click=${() => this._removeRow(rowData._id)}>Remove</button></td>
|
||||
</tr>`
|
||||
|
||||
}
|
||||
|
||||
@ -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}
|
||||
></textarea>`
|
||||
break
|
||||
|
||||
case 'select':
|
||||
formElement = html`<select class="form-select" aria-label="Default select example">
|
||||
formElement = html`<select
|
||||
class="form-select"
|
||||
aria-label="Default select example"
|
||||
@change=${(event: InputEvent) => this._updatePropertyFromValue(event, propertyName, rowId)}
|
||||
>
|
||||
<option ?selected=${!propertyValue}>${propertySchema?.label ?? 'Choose your option'}</option>
|
||||
${Object.entries(propertySchema?.options ?? {})?.map(([value,name]) => html`<option ?selected=${propertyValue === value} value=${value}>${name}</option>`)}
|
||||
${Object.entries(propertySchema?.options ?? {})
|
||||
?.map(([value,name]) =>
|
||||
html`<option ?selected=${propertyValue === value} value=${value}>${name}</option>`
|
||||
)}
|
||||
</select>`
|
||||
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`<td class="form-group">${formElement}</td>`
|
||||
|
||||
}
|
||||
|
||||
|
||||
_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
|
||||
}
|
||||
|
@ -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`
|
||||
<div class="row mt-5">
|
||||
<div class="col-12 col-lg-4 col-xl-3">
|
||||
<h2>${this.title}</h2>
|
||||
<p>${this.description}</p>
|
||||
<a
|
||||
href="${this.helpLink.url.href}"
|
||||
target=_blank
|
||||
title="${this.helpLink.title}"
|
||||
class="orange-button peertube-button-link"
|
||||
>${unsafeSVG(helpButtonSVG())}</a>
|
||||
</div>
|
||||
<div class="col-12 col-lg-8 col-xl-9">
|
||||
<slot><p>Nothing in this row.</p></slot>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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<TemplateResult> {
|
||||
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}<div data-toggle="tooltip" data-placement="bottom" data-html="true" title=${view.forbiddenWordsDesc}></div>`,
|
||||
regex: html`${view.forbiddenWordsRegexp}<div data-toggle="tooltip" data-placement="bottom" data-html="true" title=${view.forbiddenWordsRegexpDesc}></div>`,
|
||||
applyToModerators: html`${view.forbiddenWordsApplyToModerators}<div data-toggle="tooltip" data-placement="bottom" data-html="true" title=${view.forbiddenWordsApplyToModeratorsDesc}></div>`,
|
||||
label: html`${view.forbiddenWordsLabel}<div data-toggle="tooltip" data-placement="bottom" data-html="true" title=${view.forbiddenWordsLabelDesc}></div>`,
|
||||
reason: html`${view.forbiddenWordsReason}<div data-toggle="tooltip" data-placement="bottom" data-html="true" title=${view.forbiddenWordsReasonDesc}></div>`,
|
||||
comments: html`${view.forbiddenWordsComments}<div data-toggle="tooltip" data-placement="bottom" data-html="true" title=${view.forbiddenWordsCommentsDesc}></div>`,
|
||||
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`
|
||||
<div class="container">
|
||||
<channel-configuration></channel-configuration>
|
||||
<dynamic-table-form
|
||||
.header=${tableHeader}
|
||||
.schema=${tableSchema}
|
||||
.rows=${tableRows}
|
||||
.formName=${'dynamic-table-form'}
|
||||
<plugin-configuration-row
|
||||
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL)}
|
||||
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC)}
|
||||
.helpLink=${helpLink}
|
||||
>
|
||||
</dynamic-table-form>
|
||||
</div>${JSON.stringify(tableRows)}`
|
||||
<dynamic-table-form
|
||||
.header=${tableHeader}
|
||||
.schema=${tableSchema}
|
||||
.rows=${tableRows}
|
||||
.formName=${'forbidden-words'}
|
||||
>
|
||||
</dynamic-table-form>
|
||||
</plugin-configuration-row>
|
||||
</div>`
|
||||
} 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<void> {
|
||||
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)
|
||||
|
||||
|
@ -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`<a
|
||||
href="${helpUrl}"
|
||||
target=_blank
|
||||
title="${title}"
|
||||
title="${ptTr(LOC_ONLINE_HELP)}"
|
||||
class="orange-button peertube-button-link"
|
||||
>${helpIcon}</a>`
|
||||
>${unsafeHTML(helpIcon)}</a>`
|
||||
}
|
||||
|
||||
return button('documentation/user/streamers/channel')
|
||||
|
@ -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.
|
||||
|
63
package-lock.json
generated
63
package-lock.json
generated
@ -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",
|
||||
|
@ -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"
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user