Merge branch 'main' of https://github.com/JohnXLivingston/peertube-plugin-livechat
This commit is contained in:
commit
a4bf37d534
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"root": true,
|
|
||||||
"env": {},
|
|
||||||
"extends": [],
|
|
||||||
"globals": {},
|
|
||||||
"plugins": [],
|
|
||||||
"ignorePatterns": [
|
|
||||||
"node_modules/", "dist/", "webpack.config.js",
|
|
||||||
"build/",
|
|
||||||
"vendor/",
|
|
||||||
"support/documentation",
|
|
||||||
"build-*js"],
|
|
||||||
"rules": {}
|
|
||||||
}
|
|
40
.github/dependabot.yml
vendored
Normal file
40
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
|
||||||
|
groups:
|
||||||
|
minor-and-patch:
|
||||||
|
applies-to: version-updates
|
||||||
|
update-types:
|
||||||
|
- "patch"
|
||||||
|
- "minor"
|
||||||
|
|
||||||
|
versioning-strategy: increase
|
||||||
|
|
||||||
|
ignore:
|
||||||
|
- dependency-name: typescript
|
||||||
|
versions:
|
||||||
|
- ">=5.6.0" # linting libs are not ready for 5.6
|
||||||
|
- dependency-name: "@types/node"
|
||||||
|
versions:
|
||||||
|
- ">=17.0.0" # must be set to the Peertube required version.
|
||||||
|
- dependency-name: "@peertube/peertube-types"
|
||||||
|
versions:
|
||||||
|
- ">5.2.0" # must be set to the Peertube required version.
|
||||||
|
- dependency-name: eslint
|
||||||
|
versions:
|
||||||
|
- ">=9.0.0" # not ready for v9, missing dependencies.
|
||||||
|
- dependency-name: got
|
||||||
|
versions:
|
||||||
|
- ">=12.0.0" # breaking changes, must adapt code.
|
||||||
|
- dependency-name: "@typescript-eslint/parser"
|
||||||
|
versions:
|
||||||
|
- ">=8.5.0" # for now 8.5.0 is broken because of the lack of ./tsconfig.json file. Must fix conf.
|
34
.github/workflows/gh-build.yml
vendored
Normal file
34
.github/workflows/gh-build.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
name: github build and lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
types: [assigned, opened, synchronize, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: false # Fetch Hugo themes (true OR recursive)
|
||||||
|
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20.x'
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: sudo apt update && sudo apt install wget reuse -y
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: npm run lint
|
@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
'use strict';
|
'use strict'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
extends: [
|
extends: [
|
||||||
@ -14,7 +14,7 @@ module.exports = {
|
|||||||
// extending the kebab-case to accept ConverseJS class names.
|
// extending the kebab-case to accept ConverseJS class names.
|
||||||
'^([a-z][a-z0-9]*)(-[a-z0-9]+)*((__|--)[a-z]+(-[a-z0-9]+)*)?$',
|
'^([a-z][a-z0-9]*)(-[a-z0-9]+)*((__|--)[a-z]+(-[a-z0-9]+)*)?$',
|
||||||
{
|
{
|
||||||
message: 'Expected class selector to be kebab-case, or ConverseJS-style.',
|
message: 'Expected class selector to be kebab-case, or ConverseJS-style.'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
29
CHANGELOG.md
29
CHANGELOG.md
@ -1,5 +1,34 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 12.0.0
|
||||||
|
|
||||||
|
### Importante Notes
|
||||||
|
|
||||||
|
This version requires Peertube 5.2.0 or superior.
|
||||||
|
It also requires NodeJS 16 or superior (same as Peertube 5.2.0.).
|
||||||
|
|
||||||
|
If you use the "system Prosody", you should update to Prosody 0.12.4, and Lua 5.4.
|
||||||
|
|
||||||
|
### New features
|
||||||
|
|
||||||
|
* #131: Emoji only mode.
|
||||||
|
* #516: new option for the moderation bot: forbid duplicate messages.
|
||||||
|
* #517: new option for the moderation bot: forbid messages with too many special characters.
|
||||||
|
* #518: moderators can send announcements and highlighted messages.
|
||||||
|
* #610: compatibility with PeerTube v7
|
||||||
|
|
||||||
|
### Minor changes and fixes
|
||||||
|
|
||||||
|
* Updating ConverseJS (v11 WIP) with latest fixes.
|
||||||
|
* Updating Prosody AppImage to Prosody 0.12.4 + Lua 5.4.
|
||||||
|
* Various translation updates.
|
||||||
|
* Using Typescript 5.5.4, and Eslint 8.57.0 (with new ruleset).
|
||||||
|
* Fix race condition in bot/ctl.
|
||||||
|
* Various type improvements.
|
||||||
|
* Update dependencies.
|
||||||
|
* Fix emoji picker colors and size.
|
||||||
|
* Fix: moderation delay max value was not correctly handled.
|
||||||
|
|
||||||
## 11.0.1
|
## 11.0.1
|
||||||
|
|
||||||
### Minor changes and fixes
|
### Minor changes and fixes
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
/* See Peertube sub-menu-h1 mixin */
|
/* See Peertube sub-menu-h1 mixin */
|
||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
border-bottom: 2px solid var(--greyBackgroundColor);
|
border-bottom: 2px solid var(--bg-secondary-400, var(--greyBackgroundColor));
|
||||||
padding-bottom: 15px;
|
padding-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,45 +42,49 @@
|
|||||||
|
|
||||||
input[type="submit"],
|
input[type="submit"],
|
||||||
button[type="submit"] {
|
button[type="submit"] {
|
||||||
// Peertube orange-button mixin
|
|
||||||
&,
|
&,
|
||||||
&:active,
|
&:active,
|
||||||
|
&.active,
|
||||||
&:focus {
|
&:focus {
|
||||||
color: #fff;
|
color: var(--on-primary, #fff);
|
||||||
background-color: var(--mainColor);
|
background-color: var(--primary, var(--mainColor));
|
||||||
|
border: 1px solid var(--primary, var(--mainColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #fff;
|
color: var(--on-primary, #fff);
|
||||||
background-color: var(--mainHoverColor);
|
background-color: var(--primary-400, var(--mainHoverColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
&[disabled],
|
&[disabled] {
|
||||||
&.disabled {
|
pointer-events: none;
|
||||||
cursor: default;
|
opacity: 0.6;
|
||||||
color: #fff;
|
|
||||||
background-color: var(--inputBorderColor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="reset"],
|
input[type="reset"],
|
||||||
button[type="reset"] {
|
button[type="reset"] {
|
||||||
// Peertube grey-button mixin
|
color: var(--fg, var(--mainForegroundColor));
|
||||||
background-color: var(--greyBackgroundColor);
|
background-color: transparent;
|
||||||
color: var(--greyForegroundColor);
|
border: 1px solid var(--bg-secondary-500, var(--inputBorderColor)) !important;
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:active,
|
&:active,
|
||||||
|
&.active,
|
||||||
&:focus,
|
&:focus,
|
||||||
&[disabled],
|
&:focus-visible {
|
||||||
&.disabled {
|
color: var(--fg, var(--mainForegroundColor));
|
||||||
color: var(--greyForegroundColor);
|
background-color: var(--bg-secondary-500, var(--inputBorderColor));
|
||||||
background-color: var(--greySecondaryBackgroundColor);
|
border-color: var(--bg-secondary-500, var(--inputBorderColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
&[disabled],
|
&:hover {
|
||||||
&.disabled {
|
color: var(--fg, var(--mainForegroundColor));
|
||||||
cursor: default;
|
background-color: var(--bg-secondary-450, var(--inputBorderColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ $small-view: 800px;
|
|||||||
|
|
||||||
/* See Peertube sub-menu-h1 mixin */
|
/* See Peertube sub-menu-h1 mixin */
|
||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
border-bottom: 2px solid var(--greyBackgroundColor);
|
border-bottom: 2px solid var(--bg-secondary-400, var(--greyBackgroundColor));
|
||||||
padding-bottom: 15px;
|
padding-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ $small-view: 800px;
|
|||||||
&.peertube-plugin-livechat-configuration-channel {
|
&.peertube-plugin-livechat-configuration-channel {
|
||||||
.peertube-plugin-livechat-configuration-channel-info {
|
.peertube-plugin-livechat-configuration-channel-info {
|
||||||
/* stylelint-disable-next-line value-keyword-case */
|
/* stylelint-disable-next-line value-keyword-case */
|
||||||
color: var(--mainForegroundColor);
|
color: var(--fg, var(--mainForegroundColor));
|
||||||
|
|
||||||
span:first-child {
|
span:first-child {
|
||||||
/* See Peertube .video-channel-display-name */
|
/* See Peertube .video-channel-display-name */
|
||||||
@ -48,7 +48,7 @@ $small-view: 800px;
|
|||||||
h2 {
|
h2 {
|
||||||
// See Peertube settings-big-title mixin
|
// See Peertube settings-big-title mixin
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--mainColor);
|
color: var(--primary, var(--mainColor));
|
||||||
font-weight: variables.$font-bold;
|
font-weight: variables.$font-bold;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@ -82,35 +82,35 @@ $small-view: 800px;
|
|||||||
&:active,
|
&:active,
|
||||||
&:focus {
|
&:focus {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: var(--mainColor);
|
background-color: var(--primary, var(--mainColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: var(--mainHoverColor);
|
background-color: var(--fg-400, var(--mainHoverColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
&[disabled],
|
&[disabled],
|
||||||
&.disabled {
|
&.disabled {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: var(--inputBorderColor);
|
background-color: var(--input-border-color, var(--inputBorderColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="reset"],
|
input[type="reset"],
|
||||||
button[type="reset"] {
|
button[type="reset"] {
|
||||||
// Peertube grey-button mixin
|
// Peertube grey-button mixin
|
||||||
background-color: var(--greyBackgroundColor);
|
background-color: var(--bg-secondary-400, var(--greyBackgroundColor));
|
||||||
color: var(--greyForegroundColor);
|
color: var(--fg-400, var(--greyForegroundColor));
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active,
|
&:active,
|
||||||
&:focus,
|
&:focus,
|
||||||
&[disabled],
|
&[disabled],
|
||||||
&.disabled {
|
&.disabled {
|
||||||
color: var(--greyForegroundColor);
|
color: var(--fg-400, var(--greyForegroundColor));
|
||||||
background-color: var(--greySecondaryBackgroundColor);
|
background-color: var(--bg-secondary-300, var(--greySecondaryBackgroundColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
&[disabled],
|
&[disabled],
|
||||||
@ -174,6 +174,12 @@ $small-view: 800px;
|
|||||||
|
|
||||||
a {
|
a {
|
||||||
/* See Peertube .video-channel-names */
|
/* See Peertube .video-channel-names */
|
||||||
|
width: fit-content;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
/* stylelint-disable-next-line value-keyword-case */
|
||||||
|
color: var(--fg, var(--mainForegroundColor));
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:active {
|
&:active {
|
||||||
@ -184,12 +190,6 @@ $small-view: 800px;
|
|||||||
outline: none !important;
|
outline: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
width: fit-content;
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
/* stylelint-disable-next-line value-keyword-case */
|
|
||||||
color: var(--mainForegroundColor);
|
|
||||||
|
|
||||||
div:first-child {
|
div:first-child {
|
||||||
/* See Peertube .video-channel-display-name */
|
/* See Peertube .video-channel-display-name */
|
||||||
font-weight: variables.$font-semibold;
|
font-weight: variables.$font-semibold;
|
||||||
|
@ -56,7 +56,7 @@ livechat-share-chat {
|
|||||||
&.livechat-shareurl-suboptions-disabled {
|
&.livechat-shareurl-suboptions-disabled {
|
||||||
label {
|
label {
|
||||||
/* stylelint-disable-next-line custom-property-pattern */
|
/* stylelint-disable-next-line custom-property-pattern */
|
||||||
color: var(--greyForegroundColor);
|
color: var(--fg-400, var(--greyForegroundColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,9 @@ livechat-spinner,
|
|||||||
height: 48px;
|
height: 48px;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
/* stylelint-disable-next-line custom-property-pattern */
|
/* stylelint-disable-next-line custom-property-pattern */
|
||||||
border: 5px solid var(--greyBackgroundColor) !important; // !important is required for it to work in ConverseJS
|
border: 5px solid var(--bg-secondary-400, var(--greyBackgroundColor)) !important; // !important is required for it to work in ConverseJS
|
||||||
/* stylelint-disable-next-line custom-property-pattern */
|
/* stylelint-disable-next-line custom-property-pattern */
|
||||||
border-bottom-color: var(--mainColor) !important; // !important is required for it to work in ConverseJS
|
border-bottom-color: var(--primary, var(--mainColor)) !important; // !important is required for it to work in ConverseJS
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -51,14 +51,14 @@ livechat-tags-input {
|
|||||||
transition-duration: 0.3s;
|
transition-duration: 0.3s;
|
||||||
|
|
||||||
@supports (scrollbar-width: auto) {
|
@supports (scrollbar-width: auto) {
|
||||||
scrollbar-color: var(--greyForegroundColor) transparent;
|
scrollbar-color: var(--fg-400, var(--greyForegroundColor)) transparent;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.livechat-tags-container,
|
.livechat-tags-container,
|
||||||
.livechat-tags-searched {
|
.livechat-tags-searched {
|
||||||
border-bottom: 1px dashed var(--greyForegroundColor);
|
border-bottom: 1px dashed var(--fg-400, var(--greyForegroundColor));
|
||||||
|
|
||||||
&.livechat-empty {
|
&.livechat-empty {
|
||||||
height: 0;
|
height: 0;
|
||||||
@ -104,7 +104,7 @@ livechat-tags-input {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
margin-left: var(--tag-padding-horizontal);
|
margin-left: var(--tag-padding-horizontal);
|
||||||
color: var(--mainColor);
|
color: var(--primary, var(--mainColor));
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -118,19 +118,19 @@ livechat-tags-input {
|
|||||||
&:active,
|
&:active,
|
||||||
&:focus {
|
&:focus {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: var(--mainColor);
|
background-color: var(--primary, var(--mainColor));
|
||||||
|
|
||||||
.livechat-tag-close {
|
.livechat-tag-close {
|
||||||
color: var(--mainColor);
|
color: var(--primary, var(--mainColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: var(--mainHoverColor);
|
background-color: var(--fg-400, var(--mainHoverColor));
|
||||||
|
|
||||||
.livechat-tag-close {
|
.livechat-tag-close {
|
||||||
color: var(--mainHoverColor);
|
color: var(--fg-400, var(--mainHoverColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,10 +138,10 @@ livechat-tags-input {
|
|||||||
&.disabled {
|
&.disabled {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: var(--inputBorderColor);
|
background-color: var(--input-border-color, var(--inputBorderColor));
|
||||||
|
|
||||||
.livechat-tag-close {
|
.livechat-tag-close {
|
||||||
color: var(--inputBorderColor);
|
color: var(--input-border-color, var(--inputBorderColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,10 +9,10 @@
|
|||||||
|
|
||||||
livechat-token-list {
|
livechat-token-list {
|
||||||
table {
|
table {
|
||||||
@include tables.data-table;
|
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
@include tables.data-table;
|
||||||
|
|
||||||
tr th:first-child,
|
tr th:first-child,
|
||||||
tr th:last-child {
|
tr th:last-child {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
|
@ -19,7 +19,7 @@ table.peertube-plugin-livechat-prosody-list-rooms tr:nth-child(even) {
|
|||||||
|
|
||||||
table.peertube-plugin-livechat-prosody-list-rooms th {
|
table.peertube-plugin-livechat-prosody-list-rooms th {
|
||||||
/* stylelint-disable-next-line custom-property-pattern */
|
/* stylelint-disable-next-line custom-property-pattern */
|
||||||
background-color: var(--mainHoverColor);
|
background-color: var(--fg-400, var(--mainHoverColor));
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
/* stylelint-disable-next-line custom-property-pattern */
|
/* stylelint-disable-next-line custom-property-pattern */
|
||||||
color: var(--mainBackgroundColor);
|
color: var(--mainBackgroundColor);
|
||||||
|
@ -54,7 +54,7 @@ $bs-green: #39cc0b;
|
|||||||
&.disabled {
|
&.disabled {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: var(--inputBorderColor);
|
background-color: var(--input-border-color, var(--inputBorderColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ $bs-green: #39cc0b;
|
|||||||
&:active,
|
&:active,
|
||||||
&:focus {
|
&:focus {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: var(--mainColor);
|
background-color: var(--primary, var(--mainColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus,
|
&:focus,
|
||||||
@ -77,13 +77,13 @@ $bs-green: #39cc0b;
|
|||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: var(--mainHoverColor);
|
background-color: var(--fg-400, var(--mainHoverColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
&[disabled],
|
&[disabled],
|
||||||
&.disabled {
|
&.disabled {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: var(--inputBorderColor);
|
background-color: var(--input-border-color, var(--inputBorderColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
tr {
|
tr {
|
||||||
border: 1px var(--greyBackgroundColor) solid;
|
border: 1px var(--bg-secondary-400, var(--greyBackgroundColor)) solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
td,
|
td,
|
||||||
@ -34,6 +34,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
tbody tr:nth-child(odd) {
|
tbody tr:nth-child(odd) {
|
||||||
background-color: var(--greySecondaryBackgroundColor);
|
background-color: var(--bg-secondary-300, var(--greySecondaryBackgroundColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,4 +10,4 @@
|
|||||||
@use "video";
|
@use "video";
|
||||||
@use "configuration/configuration";
|
@use "configuration/configuration";
|
||||||
@use "admin/firewall/firewall";
|
@use "admin/firewall/firewall";
|
||||||
@use "list-rooms/list-rooms.scss";
|
@use "list-rooms/list-rooms";
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
min-height: max(30vh, 300px);
|
min-height: max(30vh, 300px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (orientation: portrait) and (max-width: 767px) {
|
@media screen and (orientation: portrait) and (width <= 767px) {
|
||||||
/* On small screen, and when portrait mode, we are giving the chat more vertical space.
|
/* On small screen, and when portrait mode, we are giving the chat more vertical space.
|
||||||
It should go under the video.
|
It should go under the video.
|
||||||
*/
|
*/
|
||||||
|
@ -15,7 +15,7 @@ const clientFiles = [
|
|||||||
'admin-plugin-client-plugin'
|
'admin-plugin-client-plugin'
|
||||||
]
|
]
|
||||||
|
|
||||||
function loadLocs() {
|
function loadLocs(globalFile) {
|
||||||
// Loading english strings, so we can inject them as constants.
|
// Loading english strings, so we can inject them as constants.
|
||||||
const refFile = path.resolve(__dirname, 'dist', 'languages', 'en.reference.json')
|
const refFile = path.resolve(__dirname, 'dist', 'languages', 'en.reference.json')
|
||||||
if (!fs.existsSync(refFile)) {
|
if (!fs.existsSync(refFile)) {
|
||||||
@ -25,7 +25,6 @@ function loadLocs() {
|
|||||||
|
|
||||||
// Reading client/@types/global.d.ts, to have a list of needed localized strings.
|
// Reading client/@types/global.d.ts, to have a list of needed localized strings.
|
||||||
const r = {}
|
const r = {}
|
||||||
const globalFile = path.resolve(__dirname, 'client', '@types', 'global.d.ts')
|
|
||||||
const globalFileContent = '' + fs.readFileSync(globalFile)
|
const globalFileContent = '' + fs.readFileSync(globalFile)
|
||||||
const matches = globalFileContent.matchAll(/^declare const LOC_(\w+)\b/gm)
|
const matches = globalFileContent.matchAll(/^declare const LOC_(\w+)\b/gm)
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
@ -41,7 +40,7 @@ function loadLocs() {
|
|||||||
const define = Object.assign({
|
const define = Object.assign({
|
||||||
PLUGIN_CHAT_PACKAGE_NAME: JSON.stringify(packagejson.name),
|
PLUGIN_CHAT_PACKAGE_NAME: JSON.stringify(packagejson.name),
|
||||||
PLUGIN_CHAT_SHORT_NAME: JSON.stringify(packagejson.name.replace(/^peertube-plugin-/, ''))
|
PLUGIN_CHAT_SHORT_NAME: JSON.stringify(packagejson.name.replace(/^peertube-plugin-/, ''))
|
||||||
}, loadLocs())
|
}, loadLocs(path.resolve(__dirname, 'client', '@types', 'global.d.ts')))
|
||||||
|
|
||||||
const configs = clientFiles.map(f => ({
|
const configs = clientFiles.map(f => ({
|
||||||
entryPoints: [ path.resolve(__dirname, 'client', f + '.ts') ],
|
entryPoints: [ path.resolve(__dirname, 'client', f + '.ts') ],
|
||||||
@ -59,8 +58,14 @@ const configs = clientFiles.map(f => ({
|
|||||||
outfile: path.resolve(__dirname, 'dist/client', f + '.js'),
|
outfile: path.resolve(__dirname, 'dist/client', f + '.js'),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const defineBuiltin = Object.assign(
|
||||||
|
{},
|
||||||
|
loadLocs(path.resolve(__dirname, 'conversejs', 'lib', '@types', 'global.d.ts'))
|
||||||
|
)
|
||||||
|
|
||||||
configs.push({
|
configs.push({
|
||||||
entryPoints: ["./conversejs/builtin.ts"],
|
entryPoints: ["./conversejs/builtin.ts"],
|
||||||
|
define: defineBuiltin,
|
||||||
bundle: true,
|
bundle: true,
|
||||||
minify: true,
|
minify: true,
|
||||||
sourcemap,
|
sourcemap,
|
||||||
|
@ -10,12 +10,12 @@ set -euo pipefail
|
|||||||
# This script download the Prosody AppImage from the https://github.com/JohnXLivingston/prosody-appimage project.
|
# This script download the Prosody AppImage from the https://github.com/JohnXLivingston/prosody-appimage project.
|
||||||
|
|
||||||
repo_base_url='https://github.com/JohnXLivingston/prosody-appimage/releases/download'
|
repo_base_url='https://github.com/JohnXLivingston/prosody-appimage/releases/download'
|
||||||
wanted_release='v0.12.3-1'
|
wanted_release='v0.12.4-3'
|
||||||
|
|
||||||
x86_64_filename='prosody-x86_64.AppImage'
|
x86_64_filename='prosody-x86_64.AppImage'
|
||||||
x86_64_sha256sum='f4af9bfefa2f804ad7e8b03a68f04194abb801f070ae620b3d4bcedb144e8523'
|
x86_64_sha256sum='83a583ac7036387514bed17afab257dab4161ccdd0ab7453818c78b51f830357'
|
||||||
aarch64_filename='prosody-aarch64.AppImage'
|
aarch64_filename='prosody-aarch64.AppImage'
|
||||||
aarch64_sha256sum='878c5be719e1e36a84d637fd2bd44e3059aa91ddb6906ad05f1dd0334078df09'
|
aarch64_sha256sum='7b7e6bf30d4498fc99a40022232c3065707ee4f4df24dc17947b007621634304'
|
||||||
|
|
||||||
download_dir="$(pwd)/vendor/prosody-appimage"
|
download_dir="$(pwd)/vendor/prosody-appimage"
|
||||||
dist_dir="$(pwd)/dist/server/prosody"
|
dist_dir="$(pwd)/dist/server/prosody"
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"root": true,
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"es6": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"standard-with-typescript",
|
|
||||||
"plugin:lit/recommended"
|
|
||||||
],
|
|
||||||
"globals": {},
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2018,
|
|
||||||
"project": [
|
|
||||||
"./client/tsconfig.json"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"@typescript-eslint"
|
|
||||||
],
|
|
||||||
"ignorePatterns": [],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-unused-vars": [2, {"argsIgnorePattern": "^_"}],
|
|
||||||
"@typescript-eslint/no-floating-promises": "error",
|
|
||||||
"@typescript-eslint/no-misused-promises": "error",
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"@typescript-eslint/strict-boolean-expressions": "off",
|
|
||||||
"@typescript-eslint/return-await": [2, "in-try-catch"], // FIXME: correct?
|
|
||||||
"@typescript-eslint/no-invalid-void-type": "off",
|
|
||||||
"@typescript-eslint/triple-slash-reference": "off",
|
|
||||||
"max-len": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"code": 120,
|
|
||||||
"comments": 120
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-unused-vars": "off"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
24
client/@types/global.d.ts
vendored
24
client/@types/global.d.ts
vendored
@ -57,12 +57,12 @@ declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_BOT_OPTIONS_TITLE: string
|
|||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL: string
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL: string
|
||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC: string
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC: string
|
||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC2: string
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC2: string
|
||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REASON_LABEL: string
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_RETRACTATION_REASON_LABEL: string
|
||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REASON_DESC: string
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_RETRACTATION_REASON_DESC: string
|
||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REGEXP_LABEL: string
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REGEXP_LABEL: string
|
||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REGEXP_DESC: string
|
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_APPLYTOMODERATORS_LABEL: string
|
||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_DESC: string
|
declare const LOC_LIVECHAT_CONFIGURATION_APPLYTOMODERATORS_DESC: string
|
||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_LABEL: 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_LABEL_DESC: string
|
||||||
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_COMMENTS_LABEL: string
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_COMMENTS_LABEL: string
|
||||||
@ -144,3 +144,19 @@ declare const LOC_PROSODY_FIREWALL_FILE_ENABLED: string
|
|||||||
declare const LOC_PROSODY_FIREWALL_NAME: string
|
declare const LOC_PROSODY_FIREWALL_NAME: string
|
||||||
declare const LOC_PROSODY_FIREWALL_NAME_DESC: string
|
declare const LOC_PROSODY_FIREWALL_NAME_DESC: string
|
||||||
declare const LOC_PROSODY_FIREWALL_CONTENT: string
|
declare const LOC_PROSODY_FIREWALL_CONTENT: string
|
||||||
|
|
||||||
|
declare const LOC_EMOJI_ONLY_MODE_TITLE: string
|
||||||
|
declare const LOC_EMOJI_ONLY_MODE_DESC_1: string
|
||||||
|
declare const LOC_EMOJI_ONLY_MODE_DESC_2: string
|
||||||
|
declare const LOC_EMOJI_ONLY_MODE_DESC_3: string
|
||||||
|
declare const LOC_EMOJI_ONLY_ENABLE_ALL_ROOMS: string
|
||||||
|
|
||||||
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_SPECIAL_CHARS_LABEL: string
|
||||||
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_SPECIAL_CHARS_DESC: string
|
||||||
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_SPECIAL_CHARS_TOLERANCE_LABEL: string
|
||||||
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_SPECIAL_CHARS_TOLERANCE_DESC: string
|
||||||
|
|
||||||
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_NO_DUPLICATE_LABEL: string
|
||||||
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_NO_DUPLICATE_DESC: string
|
||||||
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_NO_DUPLICATE_DELAY_LABEL: string
|
||||||
|
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_NO_DUPLICATE_DELAY_DESC: string
|
||||||
|
@ -143,7 +143,7 @@ function register (clientOptions: RegisterClientOptions): void {
|
|||||||
lastActivityEl.textContent = date.toLocaleDateString() + ' ' + date.toLocaleTimeString()
|
lastActivityEl.textContent = date.toLocaleDateString() + ' ' + date.toLocaleTimeString()
|
||||||
}
|
}
|
||||||
const promoteButton = document.createElement('a')
|
const promoteButton = document.createElement('a')
|
||||||
promoteButton.classList.add('orange-button', 'peertube-button-link')
|
promoteButton.classList.add('primary-button', 'orange-button', 'peertube-button-link')
|
||||||
promoteButton.style.margin = '5px'
|
promoteButton.style.margin = '5px'
|
||||||
promoteButton.onclick = async () => {
|
promoteButton.onclick = async () => {
|
||||||
await fetch(
|
await fetch(
|
||||||
@ -243,7 +243,10 @@ function register (clientOptions: RegisterClientOptions): void {
|
|||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
peertubeHelpers.notifier.error(error.toString(), await peertubeHelpers.translate(LOC_LOADING_ERROR))
|
peertubeHelpers.notifier.error(
|
||||||
|
(error as Error).toString(),
|
||||||
|
await peertubeHelpers.translate(LOC_LOADING_ERROR)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -46,7 +46,7 @@ async function register (clientOptions: RegisterClientOptions): Promise<void> {
|
|||||||
])
|
])
|
||||||
const webchatFieldOptions: RegisterClientFormFieldOptions = {
|
const webchatFieldOptions: RegisterClientFormFieldOptions = {
|
||||||
name: 'livechat-active',
|
name: 'livechat-active',
|
||||||
label: label,
|
label,
|
||||||
descriptionHTML: description,
|
descriptionHTML: description,
|
||||||
type: 'input-checkbox',
|
type: 'input-checkbox',
|
||||||
default: true,
|
default: true,
|
||||||
|
@ -22,7 +22,7 @@ export class AdminFirewallElement extends LivechatElement {
|
|||||||
public validationError?: ValidationError
|
public validationError?: ValidationError
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
public actionDisabled: boolean = false
|
public actionDisabled = false
|
||||||
|
|
||||||
private _asyncTaskRender: Task
|
private _asyncTaskRender: Task
|
||||||
|
|
||||||
@ -101,9 +101,9 @@ export class AdminFirewallElement extends LivechatElement {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly getInputValidationClass = (propertyName: string): { [key: string]: boolean } => {
|
public readonly getInputValidationClass = (propertyName: string): Record<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 }) : {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ export class AdminFirewallElement extends LivechatElement {
|
|||||||
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
|
||||||
|
|
||||||
// FIXME: this code is duplicated in dymamic table form
|
// FIXME: this code is duplicated in dymamic table form
|
||||||
if (validationErrorTypes && validationErrorTypes.length !== 0) {
|
if (validationErrorTypes && validationErrorTypes.length !== 0) {
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import type { AdminFirewallElement } from '../elements/admin-firewall'
|
import type { AdminFirewallElement } from '../elements/admin-firewall'
|
||||||
import type { TemplateResult } from 'lit'
|
import type { TemplateResult } from 'lit'
|
||||||
import type { DynamicFormHeader, DynamicFormSchema } from '../../../lib/elements/dynamic-table-form'
|
import type { DynamicFormHeader, DynamicFormSchema } from '../../../lib/elements/dynamic-table-form'
|
||||||
@ -64,7 +67,7 @@ export function tplAdminFirewall (el: AdminFirewallElement): TemplateResult {
|
|||||||
.maxLines=${maxFirewallFiles}
|
.maxLines=${maxFirewallFiles}
|
||||||
.validation=${el.validationError?.properties}
|
.validation=${el.validationError?.properties}
|
||||||
.validationPrefix=${'files'}
|
.validationPrefix=${'files'}
|
||||||
.rows=${el.firewallConfiguration?.files}
|
.rows=${el.firewallConfiguration?.files ?? []}
|
||||||
@update=${(e: CustomEvent) => {
|
@update=${(e: CustomEvent) => {
|
||||||
el.resetValidation(e)
|
el.resetValidation(e)
|
||||||
if (el.firewallConfiguration) {
|
if (el.firewallConfiguration) {
|
||||||
|
@ -32,7 +32,7 @@ export class ChannelConfigurationElement extends LivechatElement {
|
|||||||
public validationError?: ValidationError
|
public validationError?: ValidationError
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
public actionDisabled: boolean = false
|
public actionDisabled = false
|
||||||
|
|
||||||
private _asyncTaskRender: Task
|
private _asyncTaskRender: Task
|
||||||
|
|
||||||
@ -113,9 +113,9 @@ export class ChannelConfigurationElement extends LivechatElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly getInputValidationClass = (propertyName: string): { [key: string]: boolean } => {
|
public readonly getInputValidationClass = (propertyName: string): Record<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 }) : {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ export class ChannelConfigurationElement extends LivechatElement {
|
|||||||
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
|
||||||
|
|
||||||
// FIXME: this code is duplicated in dymamic table form
|
// FIXME: this code is duplicated in dymamic table form
|
||||||
if (validationErrorTypes && validationErrorTypes.length !== 0) {
|
if (validationErrorTypes && validationErrorTypes.length !== 0) {
|
||||||
|
@ -30,7 +30,7 @@ export class ChannelEmojisElement extends LivechatElement {
|
|||||||
public validationError?: ValidationError
|
public validationError?: ValidationError
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
public actionDisabled: boolean = false
|
public actionDisabled = false
|
||||||
|
|
||||||
private _asyncTaskRender: Task
|
private _asyncTaskRender: Task
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ export class ChannelEmojisElement extends LivechatElement {
|
|||||||
throw new Error('Invalid data')
|
throw new Error('Invalid data')
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = await this._convertImageToDataUrl(entry.url)
|
const url = await this._convertImageToDataUrl(entry.url as string)
|
||||||
const sn = entry.sn as string
|
const sn = entry.sn as string
|
||||||
|
|
||||||
const item: ChannelEmojisConfiguration['emojis']['customEmojis'][0] = {
|
const item: ChannelEmojisConfiguration['emojis']['customEmojis'][0] = {
|
||||||
@ -211,7 +211,7 @@ export class ChannelEmojisElement extends LivechatElement {
|
|||||||
await this.ptTranslate(LOC_ACTION_IMPORT_EMOJIS_INFO)
|
await this.ptTranslate(LOC_ACTION_IMPORT_EMOJIS_INFO)
|
||||||
)
|
)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.ptNotifier.error(err.toString(), await this.ptTranslate(LOC_ERROR))
|
this.ptNotifier.error((err as Error).toString(), await this.ptTranslate(LOC_ERROR))
|
||||||
} finally {
|
} finally {
|
||||||
this.actionDisabled = false
|
this.actionDisabled = false
|
||||||
}
|
}
|
||||||
@ -250,12 +250,27 @@ export class ChannelEmojisElement extends LivechatElement {
|
|||||||
a.remove()
|
a.remove()
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.logger.error(err)
|
this.logger.error(err)
|
||||||
this.ptNotifier.error(err.toString())
|
this.ptNotifier.error((err as Error).toString())
|
||||||
} finally {
|
} finally {
|
||||||
this.actionDisabled = false
|
this.actionDisabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async enableEmojisOnlyModeOnAllRooms (ev: Event): Promise<void> {
|
||||||
|
ev.preventDefault()
|
||||||
|
if (!this._channelDetailsService || !this.channelId) {
|
||||||
|
this.ptNotifier.error(await this.ptTranslate(LOC_ERROR))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this._channelDetailsService.enableEmojisOnlyModeOnAllRooms(this.channelId)
|
||||||
|
this.ptNotifier.info(await this.ptTranslate(LOC_SUCCESSFULLY_SAVED))
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
this.ptNotifier.error(await this.ptTranslate(LOC_ERROR))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes an url (or dataUrl), download the image, and converts to dataUrl.
|
* Takes an url (or dataUrl), download the image, and converts to dataUrl.
|
||||||
* @param url the url
|
* @param url the url
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { customElement, state } from 'lit/decorators.js'
|
import { customElement, state } from 'lit/decorators.js'
|
||||||
import { ptTr } from '../../lib/directives/translation'
|
import { ptTr } from '../../lib/directives/translation'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { LivechatElement } from '../../lib/elements/livechat'
|
import { LivechatElement } from '../../lib/elements/livechat'
|
||||||
import { ptTr } from '../../lib/directives/translation'
|
import { ptTr } from '../../lib/directives/translation'
|
||||||
import { html, TemplateResult } from 'lit'
|
import { html, TemplateResult } from 'lit'
|
||||||
|
@ -2,14 +2,18 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import type { ChannelConfigurationElement } from '../channel-configuration'
|
import type { ChannelConfigurationElement } from '../channel-configuration'
|
||||||
import type { DynamicFormHeader, DynamicFormSchema } from '../../../lib/elements/dynamic-table-form'
|
import type { DynamicFormHeader, DynamicFormSchema } from '../../../lib/elements/dynamic-table-form'
|
||||||
import { ptTr } from '../../../lib/directives/translation'
|
import { ptTr } from '../../../lib/directives/translation'
|
||||||
import { html, TemplateResult } from 'lit'
|
import { html, TemplateResult } from 'lit'
|
||||||
import { classMap } from 'lit/directives/class-map.js'
|
import { classMap } from 'lit/directives/class-map.js'
|
||||||
|
import { noDuplicateMaxDelay, forbidSpecialCharsMaxTolerance } from 'shared/lib/constants'
|
||||||
|
|
||||||
export function tplChannelConfiguration (el: ChannelConfigurationElement): TemplateResult {
|
export function tplChannelConfiguration (el: ChannelConfigurationElement): TemplateResult {
|
||||||
const tableHeaderList: {[key: string]: DynamicFormHeader} = {
|
const tableHeaderList: Record<string, DynamicFormHeader> = {
|
||||||
forbiddenWords: {
|
forbiddenWords: {
|
||||||
entries: {
|
entries: {
|
||||||
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL),
|
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL),
|
||||||
@ -20,16 +24,16 @@ export function tplChannelConfiguration (el: ChannelConfigurationElement): Templ
|
|||||||
description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REGEXP_DESC)
|
description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REGEXP_DESC)
|
||||||
},
|
},
|
||||||
applyToModerators: {
|
applyToModerators: {
|
||||||
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_LABEL),
|
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_APPLYTOMODERATORS_LABEL),
|
||||||
description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_DESC)
|
description: ptTr(LOC_LIVECHAT_CONFIGURATION_APPLYTOMODERATORS_DESC)
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_LABEL),
|
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_LABEL),
|
||||||
description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_DESC)
|
description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_DESC)
|
||||||
},
|
},
|
||||||
reason: {
|
reason: {
|
||||||
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REASON_LABEL),
|
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_RETRACTATION_REASON_LABEL),
|
||||||
description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REASON_DESC)
|
description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_RETRACTATION_REASON_DESC)
|
||||||
},
|
},
|
||||||
comments: {
|
comments: {
|
||||||
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_COMMENTS_LABEL),
|
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_COMMENTS_LABEL),
|
||||||
@ -57,7 +61,7 @@ export function tplChannelConfiguration (el: ChannelConfigurationElement): Templ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const tableSchema: {[key: string]: DynamicFormSchema} = {
|
const tableSchema: Record<string, DynamicFormSchema> = {
|
||||||
forbiddenWords: {
|
forbiddenWords: {
|
||||||
entries: {
|
entries: {
|
||||||
inputType: 'tags',
|
inputType: 'tags',
|
||||||
@ -337,6 +341,246 @@ export function tplChannelConfiguration (el: ChannelConfigurationElement): Templ
|
|||||||
${el.renderFeedback('peertube-livechat-bot-nickname-feedback', 'bot.nickname')}
|
${el.renderFeedback('peertube-livechat-bot-nickname-feedback', 'bot.nickname')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<livechat-configuration-section-header
|
||||||
|
.label=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_SPECIAL_CHARS_LABEL)}
|
||||||
|
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_SPECIAL_CHARS_DESC)}
|
||||||
|
.helpPage=${'documentation/user/streamers/bot/special_chars'}>
|
||||||
|
</livechat-configuration-section-header>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="forbid_special_chars"
|
||||||
|
id="peertube-livechat-forbid-special-chars"
|
||||||
|
@input=${(event: InputEvent) => {
|
||||||
|
if (event?.target && el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.forbidSpecialChars.enabled =
|
||||||
|
(event.target as HTMLInputElement).checked
|
||||||
|
}
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value="1"
|
||||||
|
?checked=${el.channelConfiguration?.configuration.bot.forbidSpecialChars.enabled}
|
||||||
|
/>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_SPECIAL_CHARS_LABEL)}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
${!el.channelConfiguration?.configuration.bot.forbidSpecialChars.enabled
|
||||||
|
? ''
|
||||||
|
: html`
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_SPECIAL_CHARS_TOLERANCE_LABEL)}
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="special_chars_tolerance"
|
||||||
|
class=${classMap(
|
||||||
|
Object.assign(
|
||||||
|
{ 'form-control': true },
|
||||||
|
el.getInputValidationClass('bot.forbidSpecialChars.tolerance')
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
min="0"
|
||||||
|
max="${forbidSpecialCharsMaxTolerance}"
|
||||||
|
id="peertube-livechat-forbid-special-chars-tolerance"
|
||||||
|
aria-describedby="peertube-livechat-forbid-special-chars-tolerance-feedback"
|
||||||
|
@input=${(event: InputEvent) => {
|
||||||
|
if (event?.target && el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.forbidSpecialChars.tolerance =
|
||||||
|
Number((event.target as HTMLInputElement).value)
|
||||||
|
}
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value="${el.channelConfiguration?.configuration.bot.forbidSpecialChars.tolerance ?? '0'}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_SPECIAL_CHARS_TOLERANCE_DESC)}
|
||||||
|
</small>
|
||||||
|
${el.renderFeedback('peertube-livechat-forbid-special-chars-tolerance-feedback',
|
||||||
|
'bot.forbidSpecialChars.tolerance')
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_RETRACTATION_REASON_LABEL)}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="special_chars_reason"
|
||||||
|
class=${classMap(
|
||||||
|
Object.assign(
|
||||||
|
{ 'form-control': true },
|
||||||
|
el.getInputValidationClass('bot.forbidSpecialChars.reason')
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
id="peertube-livechat-forbid-special-chars-reason"
|
||||||
|
aria-describedby="peertube-livechat-forbid-special-chars-reason-feedback"
|
||||||
|
@input=${(event: InputEvent) => {
|
||||||
|
if (event?.target && el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.forbidSpecialChars.reason =
|
||||||
|
(event.target as HTMLInputElement).value
|
||||||
|
}
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value="${el.channelConfiguration?.configuration.bot.forbidSpecialChars.reason ?? ''}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_RETRACTATION_REASON_DESC)}
|
||||||
|
</small>
|
||||||
|
${el.renderFeedback('peertube-livechat-forbid-special-chars-reason-feedback',
|
||||||
|
'bot.forbidSpecialChars.reason')
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="forbid_special_chars_applyToModerators"
|
||||||
|
id="peertube-livechat-forbid-special-chars-applyToModerators"
|
||||||
|
@input=${(event: InputEvent) => {
|
||||||
|
if (event?.target && el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.forbidSpecialChars.applyToModerators =
|
||||||
|
(event.target as HTMLInputElement).checked
|
||||||
|
}
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value="1"
|
||||||
|
?checked=${el.channelConfiguration?.configuration.bot.forbidSpecialChars.applyToModerators}
|
||||||
|
/>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_APPLYTOMODERATORS_LABEL)}
|
||||||
|
</label>
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_APPLYTOMODERATORS_DESC)}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
<livechat-configuration-section-header
|
||||||
|
.label=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_NO_DUPLICATE_LABEL)}
|
||||||
|
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_NO_DUPLICATE_DESC)}
|
||||||
|
.helpPage=${'documentation/user/streamers/bot/no_duplicate'}>
|
||||||
|
</livechat-configuration-section-header>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="no_duplicate"
|
||||||
|
id="peertube-livechat-no-duplicate"
|
||||||
|
@input=${(event: InputEvent) => {
|
||||||
|
if (event?.target && el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.noDuplicate.enabled =
|
||||||
|
(event.target as HTMLInputElement).checked
|
||||||
|
}
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value="1"
|
||||||
|
?checked=${el.channelConfiguration?.configuration.bot.noDuplicate.enabled}
|
||||||
|
/>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_NO_DUPLICATE_LABEL)}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
${!el.channelConfiguration?.configuration.bot.noDuplicate.enabled
|
||||||
|
? ''
|
||||||
|
: html`
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_NO_DUPLICATE_DELAY_LABEL)}
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="no_duplicate_delay"
|
||||||
|
class=${classMap(
|
||||||
|
Object.assign(
|
||||||
|
{ 'form-control': true },
|
||||||
|
el.getInputValidationClass('bot.noDuplicate.delay')
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
min="0"
|
||||||
|
max="${noDuplicateMaxDelay.toString()}"
|
||||||
|
id="peertube-livechat-no-duplicate-delay"
|
||||||
|
aria-describedby="peertube-livechat-no-duplicate-delay-feedback"
|
||||||
|
@input=${(event: InputEvent) => {
|
||||||
|
if (event?.target && el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.noDuplicate.delay =
|
||||||
|
Number((event.target as HTMLInputElement).value)
|
||||||
|
}
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value="${el.channelConfiguration?.configuration.bot.noDuplicate.delay ?? '0'}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_NO_DUPLICATE_DELAY_DESC)}
|
||||||
|
</small>
|
||||||
|
${el.renderFeedback('peertube-livechat-no-duplicate-delay-feedback',
|
||||||
|
'bot.noDuplicate.delay')
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_RETRACTATION_REASON_LABEL)}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="no_duplicate_reason"
|
||||||
|
class=${classMap(
|
||||||
|
Object.assign(
|
||||||
|
{ 'form-control': true },
|
||||||
|
el.getInputValidationClass('bot.noDuplicate.reason')
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
id="peertube-livechat-no-duplicate-reason"
|
||||||
|
aria-describedby="peertube-livechat-no-duplicate-reason-feedback"
|
||||||
|
@input=${(event: InputEvent) => {
|
||||||
|
if (event?.target && el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.noDuplicate.reason =
|
||||||
|
(event.target as HTMLInputElement).value
|
||||||
|
}
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value="${el.channelConfiguration?.configuration.bot.noDuplicate.reason ?? ''}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_RETRACTATION_REASON_DESC)}
|
||||||
|
</small>
|
||||||
|
${el.renderFeedback('peertube-livechat-no-duplicate-reason-feedback',
|
||||||
|
'bot.noDuplicate.reason')
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="no_duplicate_applyToModerators"
|
||||||
|
id="peertube-livechat-no-duplicate-applyToModerators"
|
||||||
|
@input=${(event: InputEvent) => {
|
||||||
|
if (event?.target && el.channelConfiguration) {
|
||||||
|
el.channelConfiguration.configuration.bot.noDuplicate.applyToModerators =
|
||||||
|
(event.target as HTMLInputElement).checked
|
||||||
|
}
|
||||||
|
el.requestUpdate('channelConfiguration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value="1"
|
||||||
|
?checked=${el.channelConfiguration?.configuration.bot.noDuplicate.applyToModerators}
|
||||||
|
/>
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_APPLYTOMODERATORS_LABEL)}
|
||||||
|
</label>
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_APPLYTOMODERATORS_DESC)}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
<livechat-configuration-section-header
|
<livechat-configuration-section-header
|
||||||
.label=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL)}
|
.label=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL)}
|
||||||
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC)}
|
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC)}
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import type { ChannelEmojisElement } from '../channel-emojis'
|
import type { ChannelEmojisElement } from '../channel-emojis'
|
||||||
import type { DynamicFormHeader, DynamicFormSchema } from '../../../lib/elements/dynamic-table-form'
|
import type { DynamicFormHeader, DynamicFormSchema } from '../../../lib/elements/dynamic-table-form'
|
||||||
import { maxEmojisPerChannel } from 'shared/lib/emojis'
|
import { maxEmojisPerChannel } from 'shared/lib/emojis'
|
||||||
@ -45,13 +48,14 @@ export function tplChannelEmojis (el: ChannelEmojisElement): TemplateResult {
|
|||||||
|
|
||||||
<livechat-channel-tabs .active=${'emojis'} .channelId=${el.channelId}></livechat-channel-tabs>
|
<livechat-channel-tabs .active=${'emojis'} .channelId=${el.channelId}></livechat-channel-tabs>
|
||||||
|
|
||||||
|
<h2>${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_EMOJIS_TITLE)}</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_EMOJIS_DESC)}
|
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_EMOJIS_DESC)}
|
||||||
<livechat-help-button .page=${'documentation/user/streamers/emojis'}>
|
<livechat-help-button .page=${'documentation/user/streamers/emojis'}>
|
||||||
</livechat-help-button>
|
</livechat-help-button>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<form role="form" @submit=${el.saveEmojis} @change=${el.resetValidation}>
|
<form role="form" @submit=${el.saveEmojis} @change=${el.resetValidation}>
|
||||||
<div class="peertube-plugin-livechat-configuration-actions">
|
<div class="peertube-plugin-livechat-configuration-actions">
|
||||||
${
|
${
|
||||||
@ -86,7 +90,7 @@ export function tplChannelEmojis (el: ChannelEmojisElement): TemplateResult {
|
|||||||
.maxLines=${maxEmojisPerChannel}
|
.maxLines=${maxEmojisPerChannel}
|
||||||
.validation=${el.validationError?.properties}
|
.validation=${el.validationError?.properties}
|
||||||
.validationPrefix=${'emojis'}
|
.validationPrefix=${'emojis'}
|
||||||
.rows=${el.channelEmojisConfiguration?.emojis.customEmojis}
|
.rows=${el.channelEmojisConfiguration?.emojis.customEmojis ?? []}
|
||||||
@update=${(e: CustomEvent) => {
|
@update=${(e: CustomEvent) => {
|
||||||
el.resetValidation(e)
|
el.resetValidation(e)
|
||||||
if (el.channelEmojisConfiguration) {
|
if (el.channelEmojisConfiguration) {
|
||||||
@ -106,5 +110,23 @@ export function tplChannelEmojis (el: ChannelEmojisElement): TemplateResult {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<h2>${ptTr(LOC_EMOJI_ONLY_MODE_TITLE)}</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
${ptTr(LOC_EMOJI_ONLY_MODE_DESC_1, true)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
${ptTr(LOC_EMOJI_ONLY_MODE_DESC_2, true)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
${ptTr(LOC_EMOJI_ONLY_MODE_DESC_3, true)}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="peertube-plugin-livechat-configuration-actions">
|
||||||
|
<button type="button" @click=${el.enableEmojisOnlyModeOnAllRooms}>
|
||||||
|
${ptTr(LOC_EMOJI_ONLY_ENABLE_ALL_ROOMS)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
}
|
}
|
||||||
|
@ -66,15 +66,16 @@ async function registerConfiguration (clientOptions: RegisterClientOptions): Pro
|
|||||||
for (const link of links) {
|
for (const link of links) {
|
||||||
if (typeof link !== 'object') { continue }
|
if (typeof link !== 'object') { continue }
|
||||||
if (!('key' in link)) { continue }
|
if (!('key' in link)) { continue }
|
||||||
if (link.key !== 'in-my-library') { continue }
|
if (link.key === 'in-my-library' || link.key === 'my-video-space') {
|
||||||
myLibraryLinks = link
|
myLibraryLinks = link
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!myLibraryLinks) { return links }
|
if (!myLibraryLinks) { return links }
|
||||||
if (!Array.isArray(myLibraryLinks.links)) { return links }
|
if (!Array.isArray(myLibraryLinks.links)) { return links }
|
||||||
|
|
||||||
const label = await peertubeHelpers.translate(LOC_MENU_CONFIGURATION_LABEL)
|
const label = await peertubeHelpers.translate(LOC_MENU_CONFIGURATION_LABEL)
|
||||||
myLibraryLinks.links.push({
|
myLibraryLinks.links.unshift({
|
||||||
label,
|
label,
|
||||||
shortLabel: label,
|
shortLabel: label,
|
||||||
path: '/p/livechat/configuration',
|
path: '/p/livechat/configuration',
|
||||||
|
@ -11,7 +11,7 @@ import type {
|
|||||||
import { ValidationError, ValidationErrorType } from '../../lib/models/validation'
|
import { ValidationError, ValidationErrorType } from '../../lib/models/validation'
|
||||||
import { getBaseRoute } from '../../../utils/uri'
|
import { getBaseRoute } from '../../../utils/uri'
|
||||||
import { maxEmojisPerChannel } from 'shared/lib/emojis'
|
import { maxEmojisPerChannel } from 'shared/lib/emojis'
|
||||||
import { channelTermsMaxLength } from 'shared/lib/constants'
|
import { channelTermsMaxLength, noDuplicateMaxDelay, forbidSpecialCharsMaxTolerance } from 'shared/lib/constants'
|
||||||
|
|
||||||
export class ChannelDetailsService {
|
export class ChannelDetailsService {
|
||||||
public _registerClientOptions: RegisterClientOptions
|
public _registerClientOptions: RegisterClientOptions
|
||||||
@ -67,11 +67,43 @@ export class ChannelDetailsService {
|
|||||||
// The backend will ignore those values.
|
// The backend will ignore those values.
|
||||||
if (botConf.enabled) {
|
if (botConf.enabled) {
|
||||||
propertiesError['bot.nickname'] = []
|
propertiesError['bot.nickname'] = []
|
||||||
|
propertiesError['bot.forbidSpecialChars.tolerance'] = []
|
||||||
|
propertiesError['bot.noDuplicate.delay'] = []
|
||||||
|
|
||||||
if (/[^\p{L}\p{N}\p{Z}_-]/u.test(botConf.nickname ?? '')) {
|
if (/[^\p{L}\p{N}\p{Z}_-]/u.test(botConf.nickname ?? '')) {
|
||||||
propertiesError['bot.nickname'].push(ValidationErrorType.WrongFormat)
|
propertiesError['bot.nickname'].push(ValidationErrorType.WrongFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (botConf.forbidSpecialChars.enabled) {
|
||||||
|
const forbidSpecialCharsTolerance = channelConfigurationOptions.bot.forbidSpecialChars.tolerance
|
||||||
|
if (
|
||||||
|
(typeof forbidSpecialCharsTolerance !== 'number') ||
|
||||||
|
isNaN(forbidSpecialCharsTolerance)
|
||||||
|
) {
|
||||||
|
propertiesError['bot.forbidSpecialChars.tolerance'].push(ValidationErrorType.WrongType)
|
||||||
|
} else if (
|
||||||
|
forbidSpecialCharsTolerance < 0 ||
|
||||||
|
forbidSpecialCharsTolerance > forbidSpecialCharsMaxTolerance
|
||||||
|
) {
|
||||||
|
propertiesError['bot.forbidSpecialChars.tolerance'].push(ValidationErrorType.NotInRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (botConf.noDuplicate.enabled) {
|
||||||
|
const noDuplicateDelay = channelConfigurationOptions.bot.noDuplicate.delay
|
||||||
|
if (
|
||||||
|
(typeof noDuplicateDelay !== 'number') ||
|
||||||
|
isNaN(noDuplicateDelay)
|
||||||
|
) {
|
||||||
|
propertiesError['bot.noDuplicate.delay'].push(ValidationErrorType.WrongType)
|
||||||
|
} else if (
|
||||||
|
noDuplicateDelay < 0 ||
|
||||||
|
noDuplicateDelay > noDuplicateMaxDelay
|
||||||
|
) {
|
||||||
|
propertiesError['bot.noDuplicate.delay'].push(ValidationErrorType.NotInRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const [i, fw] of botConf.forbiddenWords.entries()) {
|
for (const [i, fw] of botConf.forbiddenWords.entries()) {
|
||||||
for (const v of fw.entries) {
|
for (const v of fw.entries) {
|
||||||
propertiesError[`bot.forbiddenWords.${i}.entries`] = []
|
propertiesError[`bot.forbiddenWords.${i}.entries`] = []
|
||||||
@ -146,7 +178,8 @@ export class ChannelDetailsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const channel of channels.data) {
|
for (const channel of channels.data) {
|
||||||
channel.livechatConfigurationUri = '/p/livechat/configuration/channel?channelId=' + encodeURIComponent(channel.id)
|
channel.livechatConfigurationUri =
|
||||||
|
'/p/livechat/configuration/channel?channelId=' + encodeURIComponent(channel.id as string | number)
|
||||||
|
|
||||||
// Note: since Peertube v6.0.0, channel.avatar is dropped, and we have to use channel.avatars.
|
// Note: since Peertube v6.0.0, channel.avatar is dropped, and we have to use channel.avatars.
|
||||||
// So, if !channel.avatar, we will search a suitable one in channel.avatars, and fill channel.avatar.
|
// So, if !channel.avatar, we will search a suitable one in channel.avatars, and fill channel.avatar.
|
||||||
@ -180,10 +213,11 @@ export class ChannelDetailsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async fetchEmojisConfiguration (channelId: number): Promise<ChannelEmojisConfiguration> {
|
public async fetchEmojisConfiguration (channelId: number): Promise<ChannelEmojisConfiguration> {
|
||||||
const response = await fetch(
|
const url = getBaseRoute(this._registerClientOptions) +
|
||||||
getBaseRoute(this._registerClientOptions) +
|
|
||||||
'/api/configuration/channel/emojis/' +
|
'/api/configuration/channel/emojis/' +
|
||||||
encodeURIComponent(channelId),
|
encodeURIComponent(channelId)
|
||||||
|
const response = await fetch(
|
||||||
|
url,
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: this._headers
|
headers: this._headers
|
||||||
@ -295,10 +329,11 @@ export class ChannelDetailsService {
|
|||||||
channelId: number,
|
channelId: number,
|
||||||
channelEmojis: ChannelEmojis
|
channelEmojis: ChannelEmojis
|
||||||
): Promise<ChannelEmojisConfiguration> {
|
): Promise<ChannelEmojisConfiguration> {
|
||||||
const response = await fetch(
|
const url = getBaseRoute(this._registerClientOptions) +
|
||||||
getBaseRoute(this._registerClientOptions) +
|
|
||||||
'/api/configuration/channel/emojis/' +
|
'/api/configuration/channel/emojis/' +
|
||||||
encodeURIComponent(channelId),
|
encodeURIComponent(channelId)
|
||||||
|
const response = await fetch(
|
||||||
|
url,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: this._headers,
|
headers: this._headers,
|
||||||
@ -312,4 +347,24 @@ export class ChannelDetailsService {
|
|||||||
|
|
||||||
return response.json()
|
return response.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async enableEmojisOnlyModeOnAllRooms (channelId: number): Promise<void> {
|
||||||
|
const url = getBaseRoute(this._registerClientOptions) +
|
||||||
|
'/api/configuration/channel/emojis/' +
|
||||||
|
encodeURIComponent(channelId) +
|
||||||
|
'/enable_emoji_only'
|
||||||
|
const response = await fetch(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: this._headers
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Can\'t enable Emojis Only Mode on all rooms.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
// This content comes from the file assets/images/plus-square.svg, from the Feather icons set https://feathericons.com/
|
// This content comes from the file assets/images/plus-square.svg, from the Feather icons set https://feathericons.com/
|
||||||
export const AddSVG: string =
|
export const AddSVG =
|
||||||
`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
@ -14,7 +14,7 @@ export const AddSVG: string =
|
|||||||
</svg>`
|
</svg>`
|
||||||
|
|
||||||
// This content comes from the file assets/images/x-square.svg, from the Feather icons set https://feathericons.com/
|
// This content comes from the file assets/images/x-square.svg, from the Feather icons set https://feathericons.com/
|
||||||
export const RemoveSVG: string =
|
export const RemoveSVG =
|
||||||
`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
@ -13,8 +13,8 @@ import { getPtContext } from '../contexts/peertube'
|
|||||||
export class TranslationDirective extends AsyncDirective {
|
export class TranslationDirective extends AsyncDirective {
|
||||||
private readonly _peertubeHelpers: RegisterClientHelpers
|
private readonly _peertubeHelpers: RegisterClientHelpers
|
||||||
|
|
||||||
private _translatedValue: string = ''
|
private _translatedValue = ''
|
||||||
private _localizationId: string = ''
|
private _localizationId = ''
|
||||||
|
|
||||||
private _allowUnsafeHTML = false
|
private _allowUnsafeHTML = false
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ export class TranslationDirective extends AsyncDirective {
|
|||||||
this._asyncUpdateTranslation().then(() => {}, () => {})
|
this._asyncUpdateTranslation().then(() => {}, () => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
public override render = (locId: string, allowHTML: boolean = false): TemplateResult | string => {
|
public override render = (locId: string, allowHTML = false): TemplateResult | string => {
|
||||||
this._localizationId = locId // TODO Check current component for context (to infer the prefix)
|
this._localizationId = locId // TODO Check current component for context (to infer the prefix)
|
||||||
|
|
||||||
this._allowUnsafeHTML = allowHTML
|
this._allowUnsafeHTML = allowHTML
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { ptTr } from '../directives/translation'
|
import { ptTr } from '../directives/translation'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { customElement, property } from 'lit/decorators.js'
|
import { customElement, property } from 'lit/decorators.js'
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import type { TagsInputElement } from './tags-input'
|
import type { TagsInputElement } from './tags-input'
|
||||||
import type { DirectiveResult } from 'lit/directive'
|
import type { DirectiveResult } from 'lit/directive'
|
||||||
import { ValidationErrorType } from '../models/validation'
|
import { ValidationErrorType } from '../models/validation'
|
||||||
@ -20,26 +23,26 @@ import { AddSVG, RemoveSVG } from '../buttons'
|
|||||||
type DynamicTableAcceptedTypes = number | string | boolean | Date | Array<number | string>
|
type DynamicTableAcceptedTypes = number | string | boolean | Date | Array<number | string>
|
||||||
|
|
||||||
type DynamicTableAcceptedInputTypes = 'textarea'
|
type DynamicTableAcceptedInputTypes = 'textarea'
|
||||||
| 'select'
|
| 'select'
|
||||||
| 'checkbox'
|
| 'checkbox'
|
||||||
| 'range'
|
| 'range'
|
||||||
| 'color'
|
| 'color'
|
||||||
| 'date'
|
| 'date'
|
||||||
| 'datetime'
|
| 'datetime'
|
||||||
| 'datetime-local'
|
| 'datetime-local'
|
||||||
| 'email'
|
| 'email'
|
||||||
| 'file'
|
| 'file'
|
||||||
| 'image'
|
| 'image'
|
||||||
| 'month'
|
| 'month'
|
||||||
| 'number'
|
| 'number'
|
||||||
| 'password'
|
| 'password'
|
||||||
| 'tel'
|
| 'tel'
|
||||||
| 'text'
|
| 'text'
|
||||||
| 'time'
|
| 'time'
|
||||||
| 'url'
|
| 'url'
|
||||||
| 'week'
|
| 'week'
|
||||||
| 'tags'
|
| 'tags'
|
||||||
| 'image-file'
|
| 'image-file'
|
||||||
|
|
||||||
interface CellDataSchema {
|
interface CellDataSchema {
|
||||||
min?: number
|
min?: number
|
||||||
@ -47,7 +50,7 @@ interface CellDataSchema {
|
|||||||
minlength?: number
|
minlength?: number
|
||||||
maxlength?: number
|
maxlength?: number
|
||||||
size?: number
|
size?: number
|
||||||
options?: { [key: string]: string }
|
options?: Record<string, string>
|
||||||
datalist?: DynamicTableAcceptedTypes[]
|
datalist?: DynamicTableAcceptedTypes[]
|
||||||
separator?: string
|
separator?: string
|
||||||
inputType?: DynamicTableAcceptedInputTypes
|
inputType?: DynamicTableAcceptedInputTypes
|
||||||
@ -59,7 +62,7 @@ interface CellDataSchema {
|
|||||||
interface DynamicTableRowData {
|
interface DynamicTableRowData {
|
||||||
_id: number
|
_id: number
|
||||||
_originalIndex: number
|
_originalIndex: number
|
||||||
row: { [key: string]: DynamicTableAcceptedTypes }
|
row: Record<string, DynamicTableAcceptedTypes>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DynamicFormHeaderCellData {
|
interface DynamicFormHeaderCellData {
|
||||||
@ -68,10 +71,8 @@ interface DynamicFormHeaderCellData {
|
|||||||
headerClassList?: string[]
|
headerClassList?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DynamicFormHeader {
|
export type DynamicFormHeader = Record<string, DynamicFormHeaderCellData>
|
||||||
[key: string]: DynamicFormHeaderCellData
|
export type DynamicFormSchema = Record<string, CellDataSchema>
|
||||||
}
|
|
||||||
export interface DynamicFormSchema { [key: string]: CellDataSchema }
|
|
||||||
|
|
||||||
@customElement('livechat-dynamic-table-form')
|
@customElement('livechat-dynamic-table-form')
|
||||||
export class DynamicTableFormElement extends LivechatElement {
|
export class DynamicTableFormElement extends LivechatElement {
|
||||||
@ -85,19 +86,19 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
public maxLines?: number = undefined
|
public maxLines?: number = undefined
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
public validation?: {[key: string]: ValidationErrorType[] }
|
public validation?: Record<string, ValidationErrorType[]>
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public validationPrefix: string = ''
|
public validationPrefix = ''
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public rows: Array<{ [key: string]: DynamicTableAcceptedTypes }> = []
|
public rows: Array<Record<string, DynamicTableAcceptedTypes>> = []
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
public _rowsById: DynamicTableRowData[] = []
|
public _rowsById: DynamicTableRowData[] = []
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public formName: string = ''
|
public formName = ''
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private _lastRowId = 1
|
private _lastRowId = 1
|
||||||
@ -112,7 +113,7 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly _getDefaultRow = (): { [key: string]: DynamicTableAcceptedTypes } => {
|
private readonly _getDefaultRow = (): Record<string, DynamicTableAcceptedTypes> => {
|
||||||
this._updateLastRowId()
|
this._updateLastRowId()
|
||||||
return Object.fromEntries([...Object.entries(this.schema).map((entry) => [entry[0], entry[1].default ?? ''])])
|
return Object.fromEntries([...Object.entries(this.schema).map((entry) => [entry[0], entry[1].default ?? ''])])
|
||||||
}
|
}
|
||||||
@ -457,8 +458,7 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
inputTitle,
|
inputTitle,
|
||||||
propertyName,
|
propertyName,
|
||||||
propertySchema,
|
propertySchema,
|
||||||
(propertyValue)?.join(propertySchema.separator ?? ',') ??
|
(propertyValue)?.join(propertySchema.separator ?? ',') ?? propertyValue ?? propertySchema.default ?? '',
|
||||||
propertyValue ?? propertySchema.default ?? '',
|
|
||||||
originalIndex)}
|
originalIndex)}
|
||||||
${feedback}
|
${feedback}
|
||||||
`
|
`
|
||||||
@ -473,8 +473,7 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
inputTitle,
|
inputTitle,
|
||||||
propertyName,
|
propertyName,
|
||||||
propertySchema,
|
propertySchema,
|
||||||
(propertyValue)?.join(propertySchema.separator ?? ',') ??
|
(propertyValue)?.join(propertySchema.separator ?? ',') ?? propertyValue ?? propertySchema.default ?? '',
|
||||||
propertyValue ?? propertySchema.default ?? '',
|
|
||||||
originalIndex)}
|
originalIndex)}
|
||||||
${feedback}
|
${feedback}
|
||||||
`
|
`
|
||||||
@ -498,8 +497,10 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!formElement) {
|
if (!formElement) {
|
||||||
this.logger.warn(`value type '${(propertyValue.constructor.toString())}' is incompatible` +
|
this.logger.warn(
|
||||||
`with field type '${propertySchema.inputType as string}' for form entry '${propertyName.toString()}'.`)
|
`value type '${(propertyValue.constructor.toString())}' is incompatible` +
|
||||||
|
`with field type '${propertySchema.inputType as string}' for form entry '${propertyName.toString()}'.`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const classList = ['form-group']
|
const classList = ['form-group']
|
||||||
@ -678,7 +679,7 @@ export class DynamicTableFormElement extends LivechatElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_getInputValidationClass = (propertyName: string,
|
_getInputValidationClass = (propertyName: string,
|
||||||
originalIndex: number): { [key: string]: boolean } => {
|
originalIndex: number): Record<string, boolean> => {
|
||||||
const validationErrorTypes: ValidationErrorType[] | undefined =
|
const validationErrorTypes: ValidationErrorType[] | undefined =
|
||||||
this.validation?.[`${this.validationPrefix}.${originalIndex}.${propertyName}`]
|
this.validation?.[`${this.validationPrefix}.${originalIndex}.${propertyName}`]
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export class HelpButtonElement extends LivechatElement {
|
|||||||
public buttonTitle: string | DirectiveResult = ptTr(LOC_ONLINE_HELP)
|
public buttonTitle: string | DirectiveResult = ptTr(LOC_ONLINE_HELP)
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public page: string = ''
|
public page = ''
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
public url: URL = new URL('https://lmddgtfy.net/')
|
public url: URL = new URL('https://lmddgtfy.net/')
|
||||||
@ -38,7 +38,7 @@ export class HelpButtonElement extends LivechatElement {
|
|||||||
href="${this.url.href}"
|
href="${this.url.href}"
|
||||||
target=_blank
|
target=_blank
|
||||||
title="${this.buttonTitle}"
|
title="${this.buttonTitle}"
|
||||||
class="orange-button peertube-button-link"
|
class="primary-button orange-button peertube-button-link"
|
||||||
>${unsafeHTML(helpButtonSVG())}</a>`
|
>${unsafeHTML(helpButtonSVG())}</a>`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { LivechatElement } from './livechat'
|
import { LivechatElement } from './livechat'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import type { DirectiveResult } from 'lit/directive'
|
import type { DirectiveResult } from 'lit/directive'
|
||||||
|
@ -26,7 +26,7 @@ export class LivechatElement extends LitElement {
|
|||||||
this.logger = this.ptContext.logger.createLogger(this.tagName.toLowerCase())
|
this.logger = this.ptContext.logger.createLogger(this.tagName.toLowerCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override createRenderRoot = (): Element | ShadowRoot => {
|
protected override createRenderRoot = (): HTMLElement | DocumentFragment => {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { LivechatElement } from './livechat'
|
import { LivechatElement } from './livechat'
|
||||||
import { ptTr } from '../directives/translation'
|
import { ptTr } from '../directives/translation'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
@ -21,10 +24,11 @@ import type { DirectiveResult } from 'lit/directive'
|
|||||||
// Then replace the main color by «currentColor»
|
// Then replace the main color by «currentColor»
|
||||||
const copySVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 4.233 4.233">
|
const copySVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 4.233 4.233">
|
||||||
<g style="stroke-width:1.00021;stroke-miterlimit:4;stroke-dasharray:none">` +
|
<g style="stroke-width:1.00021;stroke-miterlimit:4;stroke-dasharray:none">` +
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len, @stylistic/indent-binary-ops
|
||||||
'<path style="opacity:.998;fill:none;fill-opacity:1;stroke:currentColor;stroke-width:1.17052;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m4.084 4.046-.616.015-.645-.004a.942.942 0 0 1-.942-.942v-4.398a.94.94 0 0 1 .942-.943H7.22a.94.94 0 0 1 .942.943l-.006.334-.08.962" transform="matrix(.45208 0 0 .45208 -.528 1.295)"/>' +
|
'<path style="opacity:.998;fill:none;fill-opacity:1;stroke:currentColor;stroke-width:1.17052;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m4.084 4.046-.616.015-.645-.004a.942.942 0 0 1-.942-.942v-4.398a.94.94 0 0 1 .942-.943H7.22a.94.94 0 0 1 .942.943l-.006.334-.08.962" transform="matrix(.45208 0 0 .45208 -.528 1.295)"/>' +
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
'<path style="opacity:.998;fill:none;fill-opacity:1;stroke:currentColor;stroke-width:1.17052;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M8.434 5.85c-.422.009-1.338.009-1.76.01-.733.004-2.199 0-2.199 0a.94.94 0 0 1-.942-.941V.52a.94.94 0 0 1 .942-.942h4.398a.94.94 0 0 1 .943.942s.004 1.466 0 2.2c-.003.418-.019 1.251-.006 1.67.024.812-.382 1.439-1.376 1.46z" transform="matrix(.45208 0 0 .45208 -.528 1.295)"/>' +
|
'<path style="opacity:.998;fill:none;fill-opacity:1;stroke:currentColor;stroke-width:1.17052;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M8.434 5.85c-.422.009-1.338.009-1.76.01-.733.004-2.199 0-2.199 0a.94.94 0 0 1-.942-.941V.52a.94.94 0 0 1 .942-.942h4.398a.94.94 0 0 1 .943.942s.004 1.466 0 2.2c-.003.418-.019 1.251-.006 1.67.024.812-.382 1.439-1.376 1.46z" transform="matrix(.45208 0 0 .45208 -.528 1.295)"/>' +
|
||||||
|
// eslint-disable-next-line @stylistic/indent-binary-ops
|
||||||
`</g>
|
`</g>
|
||||||
</svg>`
|
</svg>`
|
||||||
|
|
||||||
@ -64,10 +68,10 @@ export class TagsInputElement extends LivechatElement {
|
|||||||
private readonly _isPressingKey: string[] = []
|
private readonly _isPressingKey: string[] = []
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public separator: string = '\n'
|
public separator = '\n'
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public animDuration: number = 200
|
public animDuration = 200
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloading the standard focus method.
|
* Overloading the standard focus method.
|
||||||
@ -245,8 +249,9 @@ export class TagsInputElement extends LivechatElement {
|
|||||||
if (!this._isPressingKey.includes(e.key)) {
|
if (!this._isPressingKey.includes(e.key)) {
|
||||||
this._isPressingKey.push(e.key)
|
this._isPressingKey.push(e.key)
|
||||||
|
|
||||||
if ((target.selectionStart === target.selectionEnd) &&
|
if (
|
||||||
target.selectionStart === 0) {
|
(target.selectionStart === target.selectionEnd) && target.selectionStart === 0
|
||||||
|
) {
|
||||||
this._handleDeleteTag((this._searchedTagsIndex.length)
|
this._handleDeleteTag((this._searchedTagsIndex.length)
|
||||||
? this._searchedTagsIndex.slice(-1)[0]
|
? this._searchedTagsIndex.slice(-1)[0]
|
||||||
: (this.value.length - 1))
|
: (this.value.length - 1))
|
||||||
@ -259,8 +264,9 @@ export class TagsInputElement extends LivechatElement {
|
|||||||
if (!this._isPressingKey.includes(e.key)) {
|
if (!this._isPressingKey.includes(e.key)) {
|
||||||
this._isPressingKey.push(e.key)
|
this._isPressingKey.push(e.key)
|
||||||
|
|
||||||
if ((target.selectionStart === target.selectionEnd) &&
|
if (
|
||||||
target.selectionStart === target.value.length) {
|
(target.selectionStart === target.selectionEnd) && target.selectionStart === target.value.length
|
||||||
|
) {
|
||||||
this._handleDeleteTag((this._searchedTagsIndex.length)
|
this._handleDeleteTag((this._searchedTagsIndex.length)
|
||||||
? this._searchedTagsIndex[0]
|
? this._searchedTagsIndex[0]
|
||||||
: 0)
|
: 0)
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import type { LivechatTokenListElement } from '../token-list'
|
import type { LivechatTokenListElement } from '../token-list'
|
||||||
import { html, TemplateResult } from 'lit'
|
import { html, TemplateResult } from 'lit'
|
||||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js'
|
import { unsafeHTML } from 'lit/directives/unsafe-html.js'
|
||||||
@ -23,11 +26,11 @@ export function tplTokenList (el: LivechatTokenListElement): TemplateResult {
|
|||||||
<tbody>
|
<tbody>
|
||||||
${
|
${
|
||||||
repeat(el.tokenList ?? [], (token) => token.id, (token) => {
|
repeat(el.tokenList ?? [], (token) => token.id, (token) => {
|
||||||
let dateStr: string = ''
|
let dateStr = ''
|
||||||
try {
|
try {
|
||||||
const date = new Date(token.date)
|
const date = new Date(token.date)
|
||||||
dateStr = date.toLocaleDateString() + ' ' + date.toLocaleTimeString()
|
dateStr = date.toLocaleDateString() + ' ' + date.toLocaleTimeString()
|
||||||
} catch (err) {}
|
} catch (_err) {}
|
||||||
return html`<tr>
|
return html`<tr>
|
||||||
<td>${
|
<td>${
|
||||||
el.mode === 'select'
|
el.mode === 'select'
|
||||||
|
@ -27,7 +27,7 @@ export class LivechatTokenListElement extends LivechatElement {
|
|||||||
public currentSelectedToken?: LivechatToken
|
public currentSelectedToken?: LivechatToken
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public actionDisabled: boolean = false
|
public actionDisabled = false
|
||||||
|
|
||||||
private readonly _tokenListService: TokenListService
|
private readonly _tokenListService: TokenListService
|
||||||
private readonly _asyncTaskRender: Task
|
private readonly _asyncTaskRender: Task
|
||||||
@ -83,7 +83,7 @@ export class LivechatTokenListElement extends LivechatElement {
|
|||||||
this.dispatchEvent(new CustomEvent('update', {}))
|
this.dispatchEvent(new CustomEvent('update', {}))
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.logger.error(err)
|
this.logger.error(err)
|
||||||
this.ptNotifier.error(err.toString(), await this.ptTranslate(LOC_ERROR))
|
this.ptNotifier.error((err as Error).toString(), await this.ptTranslate(LOC_ERROR))
|
||||||
} finally {
|
} finally {
|
||||||
this.actionDisabled = false
|
this.actionDisabled = false
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ export class LivechatTokenListElement extends LivechatElement {
|
|||||||
this.dispatchEvent(new CustomEvent('update', {}))
|
this.dispatchEvent(new CustomEvent('update', {}))
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.logger.error(err)
|
this.logger.error(err)
|
||||||
this.ptNotifier.error(err.toString(), await this.ptTranslate(LOC_ERROR))
|
this.ptNotifier.error((err as Error).toString(), await this.ptTranslate(LOC_ERROR))
|
||||||
} finally {
|
} finally {
|
||||||
this.actionDisabled = false
|
this.actionDisabled = false
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ export enum ValidationErrorType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ValidationError extends Error {
|
export class ValidationError extends Error {
|
||||||
properties: {[key: string]: ValidationErrorType[] } = {}
|
properties: Record<string, ValidationErrorType[]> = {}
|
||||||
|
|
||||||
constructor (name: string, message: string | undefined, properties: ValidationError['properties']) {
|
constructor (name: string, message: string | undefined, properties: ValidationError['properties']) {
|
||||||
super(message)
|
super(message)
|
||||||
|
@ -27,7 +27,7 @@ type displayButtonOptions = displayButtonOptionsCallback | displayButtonOptionsH
|
|||||||
function displayButton (dbo: displayButtonOptions): void {
|
function displayButton (dbo: displayButtonOptions): void {
|
||||||
const button = document.createElement('a')
|
const button = document.createElement('a')
|
||||||
button.classList.add(
|
button.classList.add(
|
||||||
'orange-button', 'peertube-button-link',
|
'primary-button', 'orange-button', 'peertube-button-link',
|
||||||
'peertube-plugin-livechat-button',
|
'peertube-plugin-livechat-button',
|
||||||
'peertube-plugin-livechat-button-' + dbo.name
|
'peertube-plugin-livechat-button-' + dbo.name
|
||||||
)
|
)
|
||||||
|
@ -60,8 +60,8 @@ async function initChat (video: Video): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let showShareUrlButton: boolean = false
|
let showShareUrlButton = false
|
||||||
let showPromote: boolean = false
|
let showPromote = false
|
||||||
if (video.isLocal) { // No need for shareButton on remote chats.
|
if (video.isLocal) { // No need for shareButton on remote chats.
|
||||||
const chatShareUrl = settings['chat-share-url'] ?? ''
|
const chatShareUrl = settings['chat-share-url'] ?? ''
|
||||||
if (chatShareUrl === 'everyone') {
|
if (chatShareUrl === 'everyone') {
|
||||||
@ -187,9 +187,10 @@ async function _insertChatDom (
|
|||||||
callback: async () => {
|
callback: async () => {
|
||||||
try {
|
try {
|
||||||
// First we must get the room JID (can be video.uuid@ or channel.id@)
|
// First we must get the room JID (can be video.uuid@ or channel.id@)
|
||||||
|
const url = getBaseRoute(ptContext.ptOptions) + '/api/configuration/room/' +
|
||||||
|
encodeURIComponent(video.uuid)
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
getBaseRoute(ptContext.ptOptions) + '/api/configuration/room/' +
|
url,
|
||||||
encodeURIComponent(video.uuid),
|
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: peertubeHelpers.getAuthHeader()
|
headers: peertubeHelpers.getAuthHeader()
|
||||||
@ -303,7 +304,7 @@ async function _openChat (video: Video): Promise<void | false> {
|
|||||||
|
|
||||||
// Loading converseJS...
|
// Loading converseJS...
|
||||||
await displayConverseJS(ptContext.ptOptions, container, roomkey, 'peertube-video', false)
|
await displayConverseJS(ptContext.ptOptions, container, roomkey, 'peertube-video', false)
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
// Displaying an error page.
|
// Displaying an error page.
|
||||||
if (container) {
|
if (container) {
|
||||||
const message = document.createElement('div')
|
const message = document.createElement('div')
|
||||||
|
@ -14,7 +14,7 @@ import { getIframeUri, getXMPPAddr, UriOptions } from '../uri'
|
|||||||
import { isAnonymousUser } from '../../../utils/user'
|
import { isAnonymousUser } from '../../../utils/user'
|
||||||
|
|
||||||
// First is default tab.
|
// First is default tab.
|
||||||
const validTabNames = ['embed', 'dock', 'peertube', 'xmpp'] as const
|
const validTabNames: string[] = ['embed', 'dock', 'peertube', 'xmpp'] as const
|
||||||
|
|
||||||
type ValidTabNames = typeof validTabNames[number]
|
type ValidTabNames = typeof validTabNames[number]
|
||||||
|
|
||||||
@ -61,49 +61,49 @@ export class ShareChatElement extends LivechatElement {
|
|||||||
* Should we render the XMPP tab?
|
* Should we render the XMPP tab?
|
||||||
*/
|
*/
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public xmppUriEnabled: boolean = false
|
public xmppUriEnabled = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should we render the Dock tab?
|
* Should we render the Dock tab?
|
||||||
*/
|
*/
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public dockEnabled: boolean = false
|
public dockEnabled = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can we use autocolors?
|
* Can we use autocolors?
|
||||||
*/
|
*/
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public autocolorsAvailable: boolean = false
|
public autocolorsAvailable = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In the Embed tab, should we generated an iframe link.
|
* In the Embed tab, should we generated an iframe link.
|
||||||
*/
|
*/
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public embedIFrame: boolean = false
|
public embedIFrame = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In the Embed tab, should we generated a read-only chat link.
|
* In the Embed tab, should we generated a read-only chat link.
|
||||||
*/
|
*/
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public embedReadOnly: boolean = false
|
public embedReadOnly = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read-only, with scrollbar?
|
* Read-only, with scrollbar?
|
||||||
*/
|
*/
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public embedReadOnlyScrollbar: boolean = false
|
public embedReadOnlyScrollbar = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read-only, transparent background?
|
* Read-only, transparent background?
|
||||||
*/
|
*/
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public embedReadOnlyTransparentBackground: boolean = false
|
public embedReadOnlyTransparentBackground = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In the Embed tab, should we use current theme color?
|
* In the Embed tab, should we use current theme color?
|
||||||
*/
|
*/
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public embedAutocolors: boolean = false
|
public embedAutocolors = false
|
||||||
|
|
||||||
protected override firstUpdated (changedProperties: PropertyValues): void {
|
protected override firstUpdated (changedProperties: PropertyValues): void {
|
||||||
super.firstUpdated(changedProperties)
|
super.firstUpdated(changedProperties)
|
||||||
@ -156,7 +156,7 @@ export class ShareChatElement extends LivechatElement {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.logger.log('Restoring previous state')
|
this.logger.log('Restoring previous state')
|
||||||
if (validTabNames.includes(v.currentTab)) {
|
if (validTabNames.includes(v.currentTab as string)) {
|
||||||
this.currentTab = v.currentTab
|
this.currentTab = v.currentTab
|
||||||
}
|
}
|
||||||
this.embedIFrame = !!v.embedIFrame
|
this.embedIFrame = !!v.embedIFrame
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import type { ShareChatElement } from '../share-chat'
|
import type { ShareChatElement } from '../share-chat'
|
||||||
import { html, TemplateResult } from 'lit'
|
import { html, TemplateResult } from 'lit'
|
||||||
import { ptTr } from '../../../lib/directives/translation'
|
import { ptTr } from '../../../lib/directives/translation'
|
||||||
|
@ -71,8 +71,7 @@ async function shareChatUrl (
|
|||||||
addedNodes.forEach(node => {
|
addedNodes.forEach(node => {
|
||||||
if ((node as HTMLElement).localName === 'ngb-modal-window') {
|
if ((node as HTMLElement).localName === 'ngb-modal-window') {
|
||||||
logger.info('Detecting a new modal, checking if this is the good one...')
|
logger.info('Detecting a new modal, checking if this is the good one...')
|
||||||
if (!(node as HTMLElement).querySelector) { return }
|
const title = (node as HTMLElement).querySelector?.('.modal-title')
|
||||||
const title = (node as HTMLElement).querySelector('.modal-title')
|
|
||||||
if (!(title?.textContent === labelShare)) {
|
if (!(title?.textContent === labelShare)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
|
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
|
||||||
import type { Video } from '@peertube/peertube-types'
|
import type { Video } from '@peertube/peertube-types'
|
||||||
|
import type { LiveChatSettings } from '../lib/contexts/peertube'
|
||||||
import { AutoColors, isAutoColorsAvailable } from 'shared/lib/autocolors'
|
import { AutoColors, isAutoColorsAvailable } from 'shared/lib/autocolors'
|
||||||
import { getBaseRoute } from '../../utils/uri'
|
import { getBaseRoute } from '../../utils/uri'
|
||||||
import { logger } from '../../utils/logger'
|
import { logger } from '../../utils/logger'
|
||||||
@ -17,7 +18,7 @@ interface UriOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getIframeUri (
|
function getIframeUri (
|
||||||
registerOptions: RegisterClientOptions, settings: any, video: Video, uriOptions: UriOptions = {}
|
registerOptions: RegisterClientOptions, settings: LiveChatSettings, video: Video, uriOptions: UriOptions = {}
|
||||||
): string | null {
|
): string | null {
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
logger.error('Settings are not initialized, too soon to compute the iframeUri')
|
logger.error('Settings are not initialized, too soon to compute the iframeUri')
|
||||||
|
@ -156,12 +156,12 @@ function launchTests (): void {
|
|||||||
'content-type': 'application/json;charset=UTF-8'
|
'content-type': 'application/json;charset=UTF-8'
|
||||||
}),
|
}),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
test: test
|
test
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return {
|
return {
|
||||||
test: test,
|
test,
|
||||||
messages: [response.statusText ?? 'Unknown error'],
|
messages: [response.statusText ?? 'Unknown error'],
|
||||||
ok: false
|
ok: false
|
||||||
}
|
}
|
||||||
@ -169,7 +169,7 @@ function launchTests (): void {
|
|||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
if ((typeof data) !== 'object') {
|
if ((typeof data) !== 'object') {
|
||||||
return {
|
return {
|
||||||
test: test,
|
test,
|
||||||
messages: ['Incorrect reponse type: ' + (typeof data)],
|
messages: ['Incorrect reponse type: ' + (typeof data)],
|
||||||
ok: false
|
ok: false
|
||||||
}
|
}
|
||||||
@ -190,6 +190,7 @@ function launchTests (): void {
|
|||||||
waiting.innerHTML = '<i>Testing...</i>'
|
waiting.innerHTML = '<i>Testing...</i>'
|
||||||
ul.append(waiting)
|
ul.append(waiting)
|
||||||
if ((typeof result.next) === 'function') {
|
if ((typeof result.next) === 'function') {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
const r: Result = (result.next as Function)()
|
const r: Result = (result.next as Function)()
|
||||||
waiting.remove()
|
waiting.remove()
|
||||||
await machine(r)
|
await machine(r)
|
||||||
|
@ -24,18 +24,41 @@ function computeAutoColors (): AutoColors | null {
|
|||||||
const buttonStyles = window.getComputedStyle(button)
|
const buttonStyles = window.getComputedStyle(button)
|
||||||
|
|
||||||
const autocolors: AutoColors = {
|
const autocolors: AutoColors = {
|
||||||
mainForeground: styles.getPropertyValue('--mainForegroundColor').trim(),
|
mainForeground: styles.getPropertyValue('--fg').trim() ||
|
||||||
mainBackground: styles.getPropertyValue('--mainBackgroundColor').trim(),
|
styles.getPropertyValue('--mainForegroundColor').trim(),
|
||||||
greyForeground: styles.getPropertyValue('--greyForegroundColor').trim(),
|
|
||||||
greyBackground: styles.getPropertyValue('--greyBackgroundColor').trim(),
|
mainBackground: styles.getPropertyValue('--bg').trim() ||
|
||||||
menuForeground: styles.getPropertyValue('--menuForegroundColor').trim(),
|
styles.getPropertyValue('--mainBackgroundColor').trim(),
|
||||||
menuBackground: styles.getPropertyValue('--menuBackgroundColor').trim(),
|
|
||||||
inputForeground: styles.getPropertyValue('--inputForegroundColor').trim(),
|
greyForeground: styles.getPropertyValue('--fg-300').trim() ||
|
||||||
inputBackground: styles.getPropertyValue('--inputBackgroundColor').trim(),
|
styles.getPropertyValue('--greyForegroundColor').trim(),
|
||||||
buttonForeground: buttonStyles.color.trim(),
|
|
||||||
buttonBackground: styles.getPropertyValue('--mainColor').trim(),
|
greyBackground: styles.getPropertyValue('--bg-secondary-300').trim() ||
|
||||||
link: styles.getPropertyValue('--mainForegroundColor').trim(),
|
styles.getPropertyValue('--greyBackgroundColor').trim(),
|
||||||
linkHover: styles.getPropertyValue('--mainForegroundColor').trim()
|
|
||||||
|
menuForeground: styles.getPropertyValue('--fg').trim() ||
|
||||||
|
styles.getPropertyValue('--menuForegroundColor').trim(),
|
||||||
|
|
||||||
|
menuBackground: styles.getPropertyValue('--bg-secondary-400').trim() ||
|
||||||
|
styles.getPropertyValue('--menuBackgroundColor').trim(),
|
||||||
|
|
||||||
|
inputForeground: styles.getPropertyValue('--input-fg').trim() ||
|
||||||
|
styles.getPropertyValue('--inputForegroundColor').trim(),
|
||||||
|
|
||||||
|
inputBackground: styles.getPropertyValue('--input-bg').trim() ||
|
||||||
|
styles.getPropertyValue('--inputBackgroundColor').trim(),
|
||||||
|
|
||||||
|
buttonForeground: styles.getPropertyValue('--on-primary').trim() ||
|
||||||
|
buttonStyles.color.trim(),
|
||||||
|
|
||||||
|
buttonBackground: styles.getPropertyValue('--primary').trim() ||
|
||||||
|
styles.getPropertyValue('--mainColor').trim(),
|
||||||
|
|
||||||
|
link: styles.getPropertyValue('--fg').trim() ||
|
||||||
|
styles.getPropertyValue('--mainForegroundColor').trim(),
|
||||||
|
|
||||||
|
linkHover: styles.getPropertyValue('--fg-400').trim() ||
|
||||||
|
styles.getPropertyValue('--mainForegroundColor').trim()
|
||||||
}
|
}
|
||||||
const autoColorsTest = areAutoColorsValid(autocolors)
|
const autoColorsTest = areAutoColorsValid(autocolors)
|
||||||
if (autoColorsTest !== true) {
|
if (autoColorsTest !== true) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
||||||
|
|
||||||
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
|
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
|
||||||
import type { InitConverseJSParams, ChatPeertubeIncludeMode } from 'shared/lib/types'
|
import type { InitConverseJSParams, ChatPeertubeIncludeMode } from 'shared/lib/types'
|
||||||
@ -17,7 +18,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pollListenerInitiliazed: boolean = false
|
let pollListenerInitiliazed = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* load the ConverseJS CSS.
|
* load the ConverseJS CSS.
|
||||||
@ -152,10 +153,11 @@ async function displayConverseJS (
|
|||||||
|
|
||||||
const authHeader = peertubeHelpers.getAuthHeader()
|
const authHeader = peertubeHelpers.getAuthHeader()
|
||||||
|
|
||||||
const response = await fetch(
|
const url = getBaseRoute(clientOptions) + '/api/configuration/room/' +
|
||||||
getBaseRoute(clientOptions) + '/api/configuration/room/' +
|
|
||||||
encodeURIComponent(roomKey) +
|
encodeURIComponent(roomKey) +
|
||||||
(forceType ? '?forcetype=1' : ''),
|
(forceType ? '?forcetype=1' : '')
|
||||||
|
const response = await fetch(
|
||||||
|
url,
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: authHeader
|
headers: authHeader
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
|
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
|
||||||
|
|
||||||
function getBaseRoute ({ peertubeHelpers }: RegisterClientOptions, permanent: boolean = false): string {
|
function getBaseRoute ({ peertubeHelpers }: RegisterClientOptions, permanent = false): string {
|
||||||
if (permanent) {
|
if (permanent) {
|
||||||
return '/plugins/livechat/router'
|
return '/plugins/livechat/router'
|
||||||
}
|
}
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"root": true,
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"es6": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"standard-with-typescript"
|
|
||||||
],
|
|
||||||
"globals": {},
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2018,
|
|
||||||
"project": [
|
|
||||||
"./conversejs/tsconfig.json"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"@typescript-eslint"
|
|
||||||
],
|
|
||||||
"ignorePatterns": [],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-unused-vars": [2, {"argsIgnorePattern": "^_"}],
|
|
||||||
"@typescript-eslint/no-floating-promises": "error",
|
|
||||||
"@typescript-eslint/no-misused-promises": "error",
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"@typescript-eslint/strict-boolean-expressions": "off",
|
|
||||||
"@typescript-eslint/return-await": [2, "in-try-catch"], // FIXME: correct?
|
|
||||||
"@typescript-eslint/no-invalid-void-type": "off",
|
|
||||||
"@typescript-eslint/triple-slash-reference": "off",
|
|
||||||
"max-len": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"code": 120,
|
|
||||||
"comments": 120
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-unused-vars": "off"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
@ -4,6 +4,7 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
const fs = require('node:fs')
|
const fs = require('node:fs')
|
||||||
const path = require('node:path')
|
const path = require('node:path')
|
||||||
const YAML = require('yaml')
|
const YAML = require('yaml')
|
||||||
|
@ -18,8 +18,10 @@ set -x
|
|||||||
CONVERSE_VERSION="v11.0.0"
|
CONVERSE_VERSION="v11.0.0"
|
||||||
CONVERSE_REPO="https://github.com/conversejs/converse.js.git"
|
CONVERSE_REPO="https://github.com/conversejs/converse.js.git"
|
||||||
# You can eventually set CONVERSE_COMMIT to a specific commit ID, if you want to apply some patches.
|
# You can eventually set CONVERSE_COMMIT to a specific commit ID, if you want to apply some patches.
|
||||||
# 2024-09-02: using Converse upstream (v11 WIP).
|
# 2024-09-17: using Converse upstream (v11 WIP).
|
||||||
CONVERSE_COMMIT="9952046d580bc2930e29833f4c9987a3d4c95bc2"
|
CONVERSE_COMMIT="07dc6f4f5da5890b02a46a8a2f2d0498649786bc"
|
||||||
|
# 2024-12-03: using Converse upstream (v11 WIP).
|
||||||
|
CONVERSE_COMMIT="8f32df723e3aa392db02326dc6a3279c9497b6fb"
|
||||||
|
|
||||||
# It is possible to use another repository, if we want some customization that are not upstream (yet):
|
# It is possible to use another repository, if we want some customization that are not upstream (yet):
|
||||||
# CONVERSE_VERSION="livechat"
|
# CONVERSE_VERSION="livechat"
|
||||||
@ -29,8 +31,8 @@ CONVERSE_COMMIT="9952046d580bc2930e29833f4c9987a3d4c95bc2"
|
|||||||
|
|
||||||
# 2024-09-03: include badges short label and quick fix for sendMessage button
|
# 2024-09-03: include badges short label and quick fix for sendMessage button
|
||||||
CONVERSE_REPO="https://github.com/JohnXLivingston/converse.js"
|
CONVERSE_REPO="https://github.com/JohnXLivingston/converse.js"
|
||||||
CONVERSE_VERSION="livechat-11.0.1"
|
CONVERSE_VERSION="livechat-12.0.0"
|
||||||
CONVERSE_COMMIT=""
|
# CONVERSE_COMMIT=""
|
||||||
|
|
||||||
rootdir="$(pwd)"
|
rootdir="$(pwd)"
|
||||||
src_dir="$rootdir/conversejs"
|
src_dir="$rootdir/conversejs"
|
||||||
@ -40,6 +42,7 @@ if [ -n "$CONVERSE_COMMIT" ]; then
|
|||||||
fi
|
fi
|
||||||
converse_build_dir="$rootdir/build/conversejs"
|
converse_build_dir="$rootdir/build/conversejs"
|
||||||
converse_destination_dir="$rootdir/dist/client/conversejs"
|
converse_destination_dir="$rootdir/dist/client/conversejs"
|
||||||
|
converse_emoji_destination="$rootdir/dist/converse-emoji.json"
|
||||||
|
|
||||||
if [[ ! -d $src_dir ]]; then
|
if [[ ! -d $src_dir ]]; then
|
||||||
echo "$0 must be called from the plugin livechat root dir."
|
echo "$0 must be called from the plugin livechat root dir."
|
||||||
@ -119,6 +122,9 @@ cd $rootdir
|
|||||||
echo "Copying ConverseJS dist files..."
|
echo "Copying ConverseJS dist files..."
|
||||||
mkdir -p "$converse_destination_dir" && cp -r $converse_build_dir/dist/* "$converse_destination_dir/"
|
mkdir -p "$converse_destination_dir" && cp -r $converse_build_dir/dist/* "$converse_destination_dir/"
|
||||||
|
|
||||||
|
echo "Copying ConverseJS original emoji.json file..." # this is needed for some backend code.
|
||||||
|
cp "$converse_build_dir/src/headless/plugins/emoji/emoji.json" "$converse_emoji_destination"
|
||||||
|
|
||||||
echo "ConverseJS OK."
|
echo "ConverseJS OK."
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
||||||
|
|
||||||
import type { InitConverseJSParams, ChatIncludeMode, ExternalAuthResult } from 'shared/lib/types'
|
import type { InitConverseJSParams, ChatIncludeMode, ExternalAuthResult } from 'shared/lib/types'
|
||||||
import { inIframe } from './lib/utils'
|
import { inIframe } from './lib/utils'
|
||||||
import { initDom } from './lib/dom'
|
import { initDom } from './lib/dom'
|
||||||
@ -21,6 +23,7 @@ import { livechatViewerModePlugin } from './lib/plugins/livechat-viewer-mode'
|
|||||||
import { livechatMiniMucHeadPlugin } from './lib/plugins/livechat-mini-muc-head'
|
import { livechatMiniMucHeadPlugin } from './lib/plugins/livechat-mini-muc-head'
|
||||||
import { livechatEmojisPlugin } from './lib/plugins/livechat-emojis'
|
import { livechatEmojisPlugin } from './lib/plugins/livechat-emojis'
|
||||||
import { moderationDelayPlugin } from './lib/plugins/moderation-delay'
|
import { moderationDelayPlugin } from './lib/plugins/moderation-delay'
|
||||||
|
import { livechatAnnouncementsPlugin } from './lib/plugins/livechat-announcements'
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@ -35,11 +38,17 @@ declare global {
|
|||||||
html: Function
|
html: Function
|
||||||
sizzle: Function
|
sizzle: Function
|
||||||
dayjs: Function
|
dayjs: Function
|
||||||
|
__: Function
|
||||||
|
u: {
|
||||||
|
hasClass: Function
|
||||||
|
addClass: Function
|
||||||
|
removeClass: Function
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initConversePlugins: typeof initConversePlugins
|
initConversePlugins: typeof initConversePlugins
|
||||||
initConverse: typeof initConverse
|
initConverse: typeof initConverse
|
||||||
reconnectConverse?: (room: string) => void
|
reconnectConverse?: (params: any) => void
|
||||||
externalAuthGetResult?: (data: ExternalAuthResult) => void
|
externalAuthGetResult?: (data: ExternalAuthResult) => void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,6 +83,8 @@ function initConversePlugins (peertubeEmbedded: boolean): void {
|
|||||||
converse.plugins.add('livechatViewerModePlugin', livechatViewerModePlugin)
|
converse.plugins.add('livechatViewerModePlugin', livechatViewerModePlugin)
|
||||||
|
|
||||||
converse.plugins.add('converse-moderation-delay', moderationDelayPlugin)
|
converse.plugins.add('converse-moderation-delay', moderationDelayPlugin)
|
||||||
|
|
||||||
|
converse.plugins.add('livechatAnnouncementsPlugin', livechatAnnouncementsPlugin)
|
||||||
}
|
}
|
||||||
window.initConversePlugins = initConversePlugins
|
window.initConversePlugins = initConversePlugins
|
||||||
|
|
||||||
@ -86,7 +97,7 @@ window.initConversePlugins = initConversePlugins
|
|||||||
async function initConverse (
|
async function initConverse (
|
||||||
initConverseParams: InitConverseJSParams,
|
initConverseParams: InitConverseJSParams,
|
||||||
chatIncludeMode: ChatIncludeMode = 'chat-only',
|
chatIncludeMode: ChatIncludeMode = 'chat-only',
|
||||||
peertubeAuthHeader?: { [header: string]: string } | null
|
peertubeAuthHeader?: Record<string, string> | null
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// First, fixing relative websocket urls.
|
// First, fixing relative websocket urls.
|
||||||
if (initConverseParams.localWebsocketServiceUrl?.startsWith('/')) {
|
if (initConverseParams.localWebsocketServiceUrl?.startsWith('/')) {
|
||||||
@ -121,9 +132,9 @@ async function initConverse (
|
|||||||
params.view_mode = chatIncludeMode === 'chat-only' ? 'fullscreen' : 'embedded'
|
params.view_mode = chatIncludeMode === 'chat-only' ? 'fullscreen' : 'embedded'
|
||||||
params.allow_url_history_change = chatIncludeMode === 'chat-only'
|
params.allow_url_history_change = chatIncludeMode === 'chat-only'
|
||||||
|
|
||||||
let isAuthenticated: boolean = false
|
let isAuthenticated = false
|
||||||
let isAuthenticatedWithExternalAccount: boolean = false
|
let isAuthenticatedWithExternalAccount = false
|
||||||
let isRemoteWithNicknameSet: boolean = false
|
let isRemoteWithNicknameSet = false
|
||||||
|
|
||||||
// OIDC (OpenID Connect):
|
// OIDC (OpenID Connect):
|
||||||
const tryOIDC = (initConverseParams.externalAuthOIDC?.length ?? 0) > 0
|
const tryOIDC = (initConverseParams.externalAuthOIDC?.length ?? 0) > 0
|
||||||
|
@ -66,6 +66,7 @@ CORE_PLUGINS.push('livechat-converse-mam-search')
|
|||||||
// We must also add our custom ROOM_FEATURES, so that they correctly resets
|
// We must also add our custom ROOM_FEATURES, so that they correctly resets
|
||||||
// (see headless/plugins/muc, getDiscoInfoFeatures, which loops on this const)
|
// (see headless/plugins/muc, getDiscoInfoFeatures, which loops on this const)
|
||||||
ROOM_FEATURES.push('x_peertubelivechat_mute_anonymous')
|
ROOM_FEATURES.push('x_peertubelivechat_mute_anonymous')
|
||||||
|
ROOM_FEATURES.push('x_peertubelivechat_emoji_only_mode')
|
||||||
|
|
||||||
_converse.exports.CustomElement = CustomElement
|
_converse.exports.CustomElement = CustomElement
|
||||||
|
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { converseLocalizedHelpUrl } from '../../../shared/lib/help'
|
import { converseLocalizedHelpUrl } from '../../../shared/lib/help'
|
||||||
import { tplMUCApp } from '../../../shared/components/muc-app/templates/muc-app.js'
|
import { tplMUCApp } from '../../../shared/components/muc-app/templates/muc-app.js'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { api } from '@converse/headless'
|
import { api } from '@converse/headless'
|
||||||
import { getAuthorStyle } from '../../../../src/utils/color.js'
|
import { getAuthorStyle } from '../../../../src/utils/color.js'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { converseLocalizedHelpUrl } from '../../../shared/lib/help'
|
import { converseLocalizedHelpUrl } from '../../../shared/lib/help'
|
||||||
import { tplMUCApp } from '../../../shared/components/muc-app/templates/muc-app.js'
|
import { tplMUCApp } from '../../../shared/components/muc-app/templates/muc-app.js'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { api } from '@converse/headless'
|
import { api } from '@converse/headless'
|
||||||
import { getAuthorStyle } from '../../../../src/utils/color.js'
|
import { getAuthorStyle } from '../../../../src/utils/color.js'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { api } from '@converse/headless'
|
import { api } from '@converse/headless'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { repeat } from 'lit/directives/repeat.js'
|
import { repeat } from 'lit/directives/repeat.js'
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
import BaseModal from 'plugins/modal/modal.js'
|
import BaseModal from 'plugins/modal/modal.js'
|
||||||
import { api } from '@converse/headless'
|
import { api } from '@converse/headless'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { converseLocalizedHelpUrl } from '../../../shared/lib/help'
|
import { converseLocalizedHelpUrl } from '../../../shared/lib/help'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { repeat } from 'lit/directives/repeat.js'
|
import { repeat } from 'lit/directives/repeat.js'
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { repeat } from 'lit/directives/repeat.js'
|
import { repeat } from 'lit/directives/repeat.js'
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { converseLocalizedHelpUrl } from '../../../shared/lib/help'
|
import { converseLocalizedHelpUrl } from '../../../shared/lib/help'
|
||||||
import { tplMUCApp } from '../../../shared/components/muc-app/templates/muc-app.js'
|
import { tplMUCApp } from '../../../shared/components/muc-app/templates/muc-app.js'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { repeat } from 'lit/directives/repeat.js'
|
import { repeat } from 'lit/directives/repeat.js'
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { repeat } from 'lit/directives/repeat.js'
|
import { repeat } from 'lit/directives/repeat.js'
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
|
|
||||||
@ -20,7 +23,8 @@ export function tplMucTask (el, task) {
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
.checked=${done === true}
|
.checked=${done === true}
|
||||||
@click=${(_ev) => {
|
@click=${(ev) => {
|
||||||
|
ev?.preventDefault()
|
||||||
task.set('done', !done)
|
task.set('done', !done)
|
||||||
task.saveItem()
|
task.saveItem()
|
||||||
}}
|
}}
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { CustomElement } from 'shared/components/element.js'
|
import { CustomElement } from 'shared/components/element.js'
|
||||||
import { api } from '@converse/headless'
|
import { api } from '@converse/headless'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
|
59
conversejs/custom/shared/styles/_announcements.scss
Normal file
59
conversejs/custom/shared/styles/_announcements.scss
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
// FIXME: this should be with the livechat-announcement plugin.
|
||||||
|
// But for now, there is no way to build scss from there.
|
||||||
|
|
||||||
|
#conversejs {
|
||||||
|
.message.chat-msg {
|
||||||
|
&.livechat-announcement {
|
||||||
|
--livechat-announcement-color: #000;
|
||||||
|
--livechat-announcement-background-color: #dbf2d8;
|
||||||
|
--livechat-announcement-border-color: #2ab218;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.livechat-highlight {
|
||||||
|
--livechat-announcement-color: #000;
|
||||||
|
--livechat-announcement-background-color: #dce8fa;
|
||||||
|
--livechat-announcement-border-color: #3075e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.livechat-warning {
|
||||||
|
--livechat-announcement-color: #000;
|
||||||
|
--livechat-announcement-background-color: #fadede;
|
||||||
|
--livechat-announcement-border-color: #e03e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.livechat-announcement,
|
||||||
|
&.livechat-highlight,
|
||||||
|
&.livechat-warning {
|
||||||
|
converse-chat-message-body {
|
||||||
|
border: 2px solid var(--livechat-announcement-border-color);
|
||||||
|
color: var(--livechat-announcement-color);
|
||||||
|
background-color: var(--livechat-announcement-background-color);
|
||||||
|
min-width: 50%;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.livechat-announcements-form {
|
||||||
|
label {
|
||||||
|
// only for screen readers
|
||||||
|
border: 0 !important;
|
||||||
|
clip: rect(1px, 1px, 1px, 1px) !important;
|
||||||
|
/* stylelint-disable-next-line property-no-vendor-prefix */
|
||||||
|
-webkit-clip-path: inset(50%) !important;
|
||||||
|
clip-path: inset(50%) !important;
|
||||||
|
height: 1px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
position: absolute !important;
|
||||||
|
width: 1px !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -35,6 +35,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emoji only info box
|
||||||
|
.livechat-emoji-only-info-box {
|
||||||
|
border: 1px dashed var(--peertube-menu-background);
|
||||||
|
color: var(--peertube-main-foreground);
|
||||||
|
background-color: var(--peertube-main-background);
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
converse-chat-toolbar {
|
converse-chat-toolbar {
|
||||||
border-top: none !important; // removing border, to avoid confusing the toolbar with an input field.
|
border-top: none !important; // removing border, to avoid confusing the toolbar with an input field.
|
||||||
color: var(--peertube-main-foreground);
|
color: var(--peertube-main-foreground);
|
||||||
@ -43,6 +51,10 @@
|
|||||||
|
|
||||||
// Fixing emoji colors for some emoji like «motorcycle»
|
// Fixing emoji colors for some emoji like «motorcycle»
|
||||||
converse-emoji-picker {
|
converse-emoji-picker {
|
||||||
|
// Must set display block. Without this, Converse defined max-width will not apply.
|
||||||
|
// Don't really know why it is working in pure ConverseJs and not in livechat.
|
||||||
|
display: block;
|
||||||
|
|
||||||
.emoji-picker {
|
.emoji-picker {
|
||||||
.insert-emoji {
|
.insert-emoji {
|
||||||
a {
|
a {
|
||||||
@ -52,13 +64,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.emoji-picker__header {
|
.emoji-picker__header {
|
||||||
color: var(--peertube-main-background);
|
background-color: var(--peertube-main-background);
|
||||||
background-color: var(--peertube-main-foreground);
|
color: var(--peertube-main-foreground);
|
||||||
|
|
||||||
|
.emoji-search {
|
||||||
|
color: currentcolor;
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
.emoji-category {
|
.emoji-category {
|
||||||
color: var(--peertube-main-background);
|
background-color: var(--peertube-main-background);
|
||||||
background-color: var(--peertube-main-foreground);
|
color: var(--peertube-main-foreground);
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: currentcolor;
|
color: currentcolor;
|
||||||
@ -190,7 +206,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bigger occupants sidebar when width is not big enough.
|
// Bigger occupants sidebar when width is not big enough.
|
||||||
@media screen and (max-width: 576px) {
|
@media screen and (width <= 576px) {
|
||||||
.chatroom .box-flyout .chatroom-body .occupants {
|
.chatroom .box-flyout .chatroom-body .occupants {
|
||||||
min-width: 50%;
|
min-width: 50%;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
@import "./variables";
|
@import "./variables";
|
||||||
@import "shared/styles/index";
|
@import "shared/styles/index";
|
||||||
@import "./peertubetheme";
|
@import "./peertubetheme";
|
||||||
|
@import "./announcements";
|
||||||
|
|
||||||
body.livechat-iframe {
|
body.livechat-iframe {
|
||||||
#conversejs .chat-head {
|
#conversejs .chat-head {
|
||||||
@ -21,7 +22,7 @@ body.livechat-iframe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#conversejs .livechat-mini-muc-bar-buttons {
|
#conversejs .livechat-mini-muc-bar-buttons {
|
||||||
a.orange-button {
|
a.primary-button {
|
||||||
// force these colors...
|
// force these colors...
|
||||||
color: var(--peertube-button-foreground);
|
color: var(--peertube-button-foreground);
|
||||||
background-color: var(--peertube-button-background);
|
background-color: var(--peertube-button-background);
|
||||||
@ -202,6 +203,7 @@ body.converse-embedded {
|
|||||||
// This margin-left trick is to align the button on the right.
|
// This margin-left trick is to align the button on the right.
|
||||||
margin-left: auto !important;
|
margin-left: auto !important;
|
||||||
order: 99;
|
order: 99;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,3 +231,27 @@ body.converse-embedded {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When livechat has not many height, must reduce the emoji picker height.
|
||||||
|
/* stylelint-disable-next-line no-duplicate-selectors */
|
||||||
|
#conversejs {
|
||||||
|
&[livechat-converse-root-height="small"] {
|
||||||
|
converse-emoji-picker {
|
||||||
|
converse-emoji-picker-content {
|
||||||
|
.emoji-picker__lists {
|
||||||
|
height: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[livechat-converse-root-height="medium"] {
|
||||||
|
converse-emoji-picker {
|
||||||
|
converse-emoji-picker-content {
|
||||||
|
.emoji-picker__lists {
|
||||||
|
height: 4em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { _converse, api } from '@converse/headless'
|
import { _converse, api } from '@converse/headless'
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
@ -28,8 +31,9 @@ function externalLoginClickHandler (ev, el, externalAuthOIDCUrl) {
|
|||||||
|
|
||||||
console.log('Received an external authentication result...', data)
|
console.log('Received an external authentication result...', data)
|
||||||
if (!data.ok) {
|
if (!data.ok) {
|
||||||
|
el.external_auth_oidc_alert_message =
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
el.external_auth_oidc_alert_message = __(LOC_login_external_auth_alert_message) +
|
__(LOC_login_external_auth_alert_message) +
|
||||||
(data.message ? ` (${data.message})` : '')
|
(data.message ? ` (${data.message})` : '')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { __ } from 'i18n'
|
import { __ } from 'i18n'
|
||||||
import { _converse, api } from '@converse/headless'
|
import { _converse, api } from '@converse/headless'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
@ -83,6 +86,20 @@ const tplSlowMode = (o) => {
|
|||||||
return html`<livechat-slow-mode jid=${o.model.get('jid')}>`
|
return html`<livechat-slow-mode jid=${o.model.get('jid')}>`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tplEmojiOnly = (o) => {
|
||||||
|
if (!o.can_post) { return html`` }
|
||||||
|
if (!o.model.features?.get?.('x_peertubelivechat_emoji_only_mode')) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return html`<div class="livechat-emoji-only-info-box">
|
||||||
|
<converse-icon class="fa fa-info-circle" size="1.2em"></converse-icon>
|
||||||
|
${
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
__(LOC_emoji_only_info)
|
||||||
|
}
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
|
|
||||||
const tplViewerMode = (o) => {
|
const tplViewerMode = (o) => {
|
||||||
if (!api.settings.get('livechat_enable_viewer_mode')) {
|
if (!api.settings.get('livechat_enable_viewer_mode')) {
|
||||||
return html``
|
return html``
|
||||||
@ -145,6 +162,7 @@ export default (o) => {
|
|||||||
return html`
|
return html`
|
||||||
${tplViewerMode(o)}
|
${tplViewerMode(o)}
|
||||||
${tplSlowMode(o)}
|
${tplSlowMode(o)}
|
||||||
|
${tplEmojiOnly(o)}
|
||||||
${
|
${
|
||||||
mutedAnonymousMessage
|
mutedAnonymousMessage
|
||||||
? html`<span class="muc-bottom-panel muc-bottom-panel--muted">${mutedAnonymousMessage}</span>`
|
? html`<span class="muc-bottom-panel muc-bottom-panel--muted">${mutedAnonymousMessage}</span>`
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { api } from '@converse/headless'
|
import { api } from '@converse/headless'
|
||||||
import tplMUCChatarea from '../../src/plugins/muc-views/templates/muc-chatarea.js'
|
import tplMUCChatarea from '../../src/plugins/muc-views/templates/muc-chatarea.js'
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
import { html } from 'lit'
|
import { html } from 'lit'
|
||||||
import { api } from '@converse/headless'
|
import { api } from '@converse/headless'
|
||||||
import { until } from 'lit/directives/until.js'
|
import { until } from 'lit/directives/until.js'
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
// Must import the original muc.js, because it imports some custom elements files.
|
// Must import the original muc.js, because it imports some custom elements files.
|
||||||
import '../../src/plugins/muc-views/templates/muc.js'
|
import '../../src/plugins/muc-views/templates/muc.js'
|
||||||
import { getChatRoomBodyTemplate } from '../../src/plugins/muc-views/utils.js'
|
import { getChatRoomBodyTemplate } from '../../src/plugins/muc-views/utils.js'
|
||||||
|
11
conversejs/lib/@types/global.d.ts
vendored
Normal file
11
conversejs/lib/@types/global.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// Important note: loc segments that are declared here must also be in loc.keys.js (for now).
|
||||||
|
|
||||||
|
declare const LOC_ANNOUNCEMENTS_MESSAGE_TYPE: string
|
||||||
|
declare const LOC_ANNOUNCEMENTS_MESSAGE_TYPE_STANDARD: string
|
||||||
|
declare const LOC_ANNOUNCEMENTS_MESSAGE_TYPE_ANNOUNCEMENT: string
|
||||||
|
declare const LOC_ANNOUNCEMENTS_MESSAGE_TYPE_HIGHLIGHT: string
|
||||||
|
declare const LOC_ANNOUNCEMENTS_MESSAGE_TYPE_WARNING: string
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import type { ProsodyAuthentInfos } from 'shared/lib/types'
|
import type { ProsodyAuthentInfos } from 'shared/lib/types'
|
||||||
|
|
||||||
interface AuthHeader { [key: string]: string }
|
type AuthHeader = Record<string, string>
|
||||||
|
|
||||||
async function getLocalAuthentInfos (
|
async function getLocalAuthentInfos (
|
||||||
authenticationUrl: string,
|
authenticationUrl: string,
|
||||||
@ -66,7 +66,7 @@ async function getLocalAuthentInfos (
|
|||||||
{
|
{
|
||||||
'content-type': 'application/json;charset=UTF-8'
|
'content-type': 'application/json;charset=UTF-8'
|
||||||
}
|
}
|
||||||
)
|
) as HeadersInit
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ async function getLocalAuthentInfos (
|
|||||||
function getLivechatTokenAuthInfos (): ProsodyAuthentInfos | undefined {
|
function getLivechatTokenAuthInfos (): ProsodyAuthentInfos | undefined {
|
||||||
try {
|
try {
|
||||||
const hash = window.location.hash
|
const hash = window.location.hash
|
||||||
if (!hash || !hash.startsWith('#?')) { return undefined }
|
if (!hash?.startsWith('#?')) { return undefined }
|
||||||
// We try to read the hash as a queryString.
|
// We try to read the hash as a queryString.
|
||||||
const u = new URL('http://localhost' + hash.substring(1))
|
const u = new URL('http://localhost' + hash.substring(1))
|
||||||
const jid = u.searchParams.get('j')
|
const jid = u.searchParams.get('j')
|
||||||
|
@ -86,7 +86,8 @@ function defaultConverseParams (
|
|||||||
'livechatDisconnectOnUnloadPlugin',
|
'livechatDisconnectOnUnloadPlugin',
|
||||||
'converse-slow-mode',
|
'converse-slow-mode',
|
||||||
'livechatEmojis',
|
'livechatEmojis',
|
||||||
'converse-moderation-delay'
|
'converse-moderation-delay',
|
||||||
|
'livechatAnnouncementsPlugin'
|
||||||
],
|
],
|
||||||
show_retraction_warning: false, // No need to use this warning (except if we open to external clients?)
|
show_retraction_warning: false, // No need to use this warning (except if we open to external clients?)
|
||||||
muc_show_info_messages: mucShowInfoMessages,
|
muc_show_info_messages: mucShowInfoMessages,
|
||||||
|
249
conversejs/lib/plugins/livechat-announcements.ts
Normal file
249
conversejs/lib/plugins/livechat-announcements.ts
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
interface Current {
|
||||||
|
announcementType: string | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* livechat announcements ConverseJS plugin:
|
||||||
|
* with this plugin, moderators can send highlighted/announcements messages.
|
||||||
|
*
|
||||||
|
* Moderators will have a special select field in the chat toolbar, so that they can choose a messaging style.
|
||||||
|
* These special messages will have a first line with a generated title (for XMPP compatibility).
|
||||||
|
* They will also have a special attribute on the body tag.
|
||||||
|
* This attribute will be used to apply some CSS with this plugin.
|
||||||
|
*/
|
||||||
|
export const livechatAnnouncementsPlugin = {
|
||||||
|
dependencies: ['converse-muc', 'converse-muc-views'],
|
||||||
|
initialize: function (this: any) {
|
||||||
|
const _converse = this._converse
|
||||||
|
|
||||||
|
// This is a closure variable, to get the current form status when sending a message.
|
||||||
|
const current: Current = {
|
||||||
|
announcementType: undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
overrideMUCMessageForm(_converse, current)
|
||||||
|
|
||||||
|
_converse.api.listen.on('getToolbarButtons', getToolbarButtons.bind(this))
|
||||||
|
|
||||||
|
_converse.api.listen.on('chatRoomInitialized', (muc: any) => onAffiliationChange(_converse, muc))
|
||||||
|
|
||||||
|
_converse.api.listen.on('getOutgoingMessageAttributes', (chatbox: any, attrs: any) => {
|
||||||
|
return onGetOutgoingMessageAttributes(current, _converse, chatbox, attrs)
|
||||||
|
})
|
||||||
|
|
||||||
|
_converse.api.listen.on('createMessageStanza', createMessageStanza)
|
||||||
|
|
||||||
|
_converse.api.listen.on('parseMUCMessage', parseMUCMessage)
|
||||||
|
|
||||||
|
overrideMessage(_converse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloads the MUCMessageForm to handle the announcement type field (if present) when sending a message.
|
||||||
|
*
|
||||||
|
* Also hides the announcement type field if we are correcting a previous message.
|
||||||
|
*/
|
||||||
|
function overrideMUCMessageForm (_converse: any, current: Current): void {
|
||||||
|
const MUCMessageForm = _converse.api.elements.registry['converse-muc-message-form']
|
||||||
|
if (MUCMessageForm) {
|
||||||
|
class MUCMessageFormloaded extends MUCMessageForm {
|
||||||
|
async onFormSubmitted (ev?: Event): Promise<void> {
|
||||||
|
const announcementSelect = this.querySelector('[name=livechat-announcements]')
|
||||||
|
current.announcementType = announcementSelect?.selectedOptions?.[0]?.value || undefined
|
||||||
|
try {
|
||||||
|
await super.onFormSubmitted(ev)
|
||||||
|
if (announcementSelect) { announcementSelect.selectedIndex = 0 } // set back to default
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
current.announcementType = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
insertIntoTextArea (...args: any[]): void {
|
||||||
|
super.insertIntoTextArea(...args)
|
||||||
|
try {
|
||||||
|
// FIXME: doing this here is not very clean.
|
||||||
|
// But that's how ConverseJS adds or removes the 'correction' class to the textarea.
|
||||||
|
const textarea = this.querySelector('.chat-textarea')
|
||||||
|
if (!textarea) { return }
|
||||||
|
const correcting = window.converse.env.u.hasClass('correcting', textarea)
|
||||||
|
const announcementForm = this.querySelector('.livechat-announcements-form')
|
||||||
|
const announcementSelect = this.querySelector('[name=livechat-announcements]')
|
||||||
|
if (correcting) {
|
||||||
|
if (announcementSelect) { announcementSelect.selectedIndex = 0 }
|
||||||
|
if (announcementForm) { announcementForm.style.display = 'none' }
|
||||||
|
} else {
|
||||||
|
if (announcementForm) { announcementForm.style.display = 'block' }
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_converse.api.elements.define('converse-muc-message-form', MUCMessageFormloaded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the announcement selector in the toolbar for owner/admin.
|
||||||
|
* @param this the plugin
|
||||||
|
* @param toolbarEl the toolbar element
|
||||||
|
* @param buttons the button list
|
||||||
|
* @returns the updated "button" list
|
||||||
|
*/
|
||||||
|
function getToolbarButtons (this: any, toolbarEl: any, buttons: any[]): Parameters<typeof getToolbarButtons>[1] {
|
||||||
|
const _converse = this._converse
|
||||||
|
const mucModel = toolbarEl.model
|
||||||
|
if (!toolbarEl.is_groupchat) {
|
||||||
|
return buttons
|
||||||
|
}
|
||||||
|
const myself = mucModel.getOwnOccupant()
|
||||||
|
if (!myself || !['admin', 'owner'].includes(myself.get('affiliation') as string)) {
|
||||||
|
return buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
const { __ } = _converse
|
||||||
|
const { html } = window.converse.env
|
||||||
|
|
||||||
|
const i18n = __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE)
|
||||||
|
const i18nStandard = __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_STANDARD)
|
||||||
|
const i18nAnnouncement = __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_ANNOUNCEMENT)
|
||||||
|
const i18nHighlight = __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_HIGHLIGHT)
|
||||||
|
const i18nWarning = __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_WARNING)
|
||||||
|
|
||||||
|
const select = html`<span class="livechat-announcements-form form-inline">
|
||||||
|
<label for="livechat-announcements-select">${i18n}</label>
|
||||||
|
<select
|
||||||
|
name="livechat-announcements"
|
||||||
|
id="livechat-announcements-select"
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
title=${i18n}
|
||||||
|
>
|
||||||
|
<option value="">${i18nStandard}</option>
|
||||||
|
<option value="highlight">${i18nHighlight}</option>
|
||||||
|
<option value="announcement">${i18nAnnouncement}</option>
|
||||||
|
<option value="warning">${i18nWarning}</option>
|
||||||
|
</select>
|
||||||
|
</span>`
|
||||||
|
|
||||||
|
if (_converse.api.settings.get('visible_toolbar_buttons').emoji) {
|
||||||
|
// Emojis should be the first entry, so adding select in second place.
|
||||||
|
buttons = [
|
||||||
|
buttons.shift(),
|
||||||
|
select,
|
||||||
|
...buttons
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
// Adding the select in first place.
|
||||||
|
buttons.unshift(select)
|
||||||
|
}
|
||||||
|
return buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshed the toolbar when current user affiliation changes.
|
||||||
|
* @param _converse _converse object
|
||||||
|
* @param muc the current muc
|
||||||
|
*/
|
||||||
|
function onAffiliationChange (_converse: any, muc: any): void {
|
||||||
|
muc.occupants.on('change:affiliation', (occupant: any) => {
|
||||||
|
if (occupant.get('jid') !== _converse.bare_jid) { // only for myself
|
||||||
|
return
|
||||||
|
}
|
||||||
|
document.querySelectorAll('converse-chat-toolbar').forEach(e => (e as any).requestUpdate?.())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For outgoing message, adding the announcement type if there is a current one.
|
||||||
|
* @param current current object
|
||||||
|
* @param _converse _converse object
|
||||||
|
* @param chatbox the chatbox
|
||||||
|
* @param attrs message attributes
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function onGetOutgoingMessageAttributes (
|
||||||
|
current: Current,
|
||||||
|
_converse: any,
|
||||||
|
chatbox: any,
|
||||||
|
attrs: any
|
||||||
|
): Parameters<typeof onGetOutgoingMessageAttributes>[3] {
|
||||||
|
if (!current.announcementType) { return attrs }
|
||||||
|
|
||||||
|
const { __ } = _converse
|
||||||
|
attrs.livechat_announcement_type = current.announcementType
|
||||||
|
if (current.announcementType === 'announcement') {
|
||||||
|
attrs.body = '* ' + __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_ANNOUNCEMENT) + ' * \n' + attrs.body
|
||||||
|
} else if (current.announcementType === 'warning') {
|
||||||
|
attrs.body = '* ' + __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_WARNING) + ' *\n' + attrs.body
|
||||||
|
}
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outgoing messages: adding an attribute on body for announcements.
|
||||||
|
* @param chat
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
async function createMessageStanza (
|
||||||
|
chat: any,
|
||||||
|
data: any
|
||||||
|
): Promise<Parameters<typeof createMessageStanza>[1]> {
|
||||||
|
const { message, stanza } = data
|
||||||
|
const announcementType = message.get('livechat_announcement_type')
|
||||||
|
if (!announcementType) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
stanza.tree().querySelector('message body')?.setAttribute('x-livechat-announcement-type', announcementType)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incoming messages: checking if there is an announcement attribute, and adding it in computed attributes.
|
||||||
|
* @param stanza
|
||||||
|
* @param attrs
|
||||||
|
*/
|
||||||
|
function parseMUCMessage (stanza: any, attrs: any): Parameters<typeof parseMUCMessage>[1] {
|
||||||
|
const { sizzle } = window.converse.env
|
||||||
|
const body = sizzle('message body', stanza)?.[0]
|
||||||
|
if (!body) { return attrs }
|
||||||
|
|
||||||
|
const announcementType = body.getAttribute('x-livechat-announcement-type')
|
||||||
|
if (!announcementType) { return attrs }
|
||||||
|
|
||||||
|
// Note: we don't check the value here. Will be done in getExtraMessageClasses.
|
||||||
|
// Moreover, the backend server will ensure that only admins/owners can send this attribute.
|
||||||
|
attrs.livechat_announcement_type = announcementType
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloading the Message class to add CSS for announcements.
|
||||||
|
* @param _converse
|
||||||
|
*/
|
||||||
|
function overrideMessage (_converse: any): void {
|
||||||
|
const Message = _converse.api.elements.registry['converse-chat-message']
|
||||||
|
if (Message) {
|
||||||
|
class MessageOverloaded extends Message {
|
||||||
|
getExtraMessageClasses (this: any): string {
|
||||||
|
// Adding CSS class if the message is an announcement.
|
||||||
|
let extraClasses = super.getExtraMessageClasses() ?? ''
|
||||||
|
const announcementType: string | undefined = this.model.get('livechat_announcement_type')
|
||||||
|
if (!announcementType) {
|
||||||
|
return extraClasses
|
||||||
|
}
|
||||||
|
if (['announcement', 'highlight', 'warning'].includes(announcementType)) {
|
||||||
|
extraClasses += ' livechat-' + announcementType
|
||||||
|
}
|
||||||
|
return extraClasses
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_converse.api.elements.define('converse-chat-message', MessageOverloaded)
|
||||||
|
}
|
||||||
|
}
|
@ -17,19 +17,19 @@ export const livechatEmojisPlugin = {
|
|||||||
livechat_custom_emojis_url: false
|
livechat_custom_emojis_url: false
|
||||||
})
|
})
|
||||||
|
|
||||||
_converse.api.listen.on('loadEmojis', async (_context: Object, json: any) => {
|
_converse.api.listen.on('loadEmojis', async (_context: object, json: Record<string, Record<string, unknown>>) => {
|
||||||
const url = _converse.api.settings.get('livechat_custom_emojis_url')
|
const url = _converse.api.settings.get('livechat_custom_emojis_url') as string | undefined
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
let customs
|
let customs: CustomEmojiDefinition[] | undefined
|
||||||
try {
|
try {
|
||||||
customs = await loadCustomEmojis(url)
|
customs = await loadCustomEmojis(url)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
if (customs === undefined || !customs?.length) {
|
if (!customs?.length) {
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ export const livechatEmojisPlugin = {
|
|||||||
|
|
||||||
// We must also remove any existing emojis in category other than custom
|
// We must also remove any existing emojis in category other than custom
|
||||||
for (const type of Object.keys(json)) {
|
for (const type of Object.keys(json)) {
|
||||||
const v: {[key: string]: any} = json[type]
|
const v: Record<string, any> = json[type]
|
||||||
if (type !== 'custom' && type !== 'modifiers' && (def.sn in v)) {
|
if (type !== 'custom' && type !== 'modifiers' && (def.sn in v)) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete v[def.sn]
|
delete v[def.sn]
|
||||||
|
@ -9,6 +9,7 @@ import { chatRoomOverrides } from './livechat-specific/chatroom'
|
|||||||
import { chatRoomMessageOverrides } from './livechat-specific/chatroom-message'
|
import { chatRoomMessageOverrides } from './livechat-specific/chatroom-message'
|
||||||
import { customizeMessageAction } from './livechat-specific/message-action'
|
import { customizeMessageAction } from './livechat-specific/message-action'
|
||||||
import { customizeProfileModal } from './livechat-specific/profile'
|
import { customizeProfileModal } from './livechat-specific/profile'
|
||||||
|
import { customizeMUCBottomPanel } from './livechat-specific/muc-bottom-panel'
|
||||||
|
|
||||||
export const livechatSpecificsPlugin = {
|
export const livechatSpecificsPlugin = {
|
||||||
dependencies: ['converse-muc', 'converse-muc-views'],
|
dependencies: ['converse-muc', 'converse-muc-views'],
|
||||||
@ -26,6 +27,7 @@ export const livechatSpecificsPlugin = {
|
|||||||
customizeToolbar(this)
|
customizeToolbar(this)
|
||||||
customizeMessageAction(this)
|
customizeMessageAction(this)
|
||||||
customizeProfileModal(this)
|
customizeProfileModal(this)
|
||||||
|
customizeMUCBottomPanel(this)
|
||||||
|
|
||||||
_converse.api.listen.on('chatRoomViewInitialized', function (this: any, _model: any): void {
|
_converse.api.listen.on('chatRoomViewInitialized', function (this: any, _model: any): void {
|
||||||
// Remove the spinner if present...
|
// Remove the spinner if present...
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
export function chatRoomMessageOverrides (): {[key: string]: Function} {
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
|
export function chatRoomMessageOverrides (): Record<string, Function> {
|
||||||
return {
|
return {
|
||||||
/* By default, ConverseJS groups messages from the same users for a 10 minutes period.
|
/* By default, ConverseJS groups messages from the same users for a 10 minutes period.
|
||||||
* This make no sense in a livechat room. So we override isFollowup to ignore. */
|
* This make no sense in a livechat room. So we override isFollowup to ignore. */
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
export function chatRoomOverrides (): {[key: string]: Function} {
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
|
export function chatRoomOverrides (): Record<string, Function> {
|
||||||
return {
|
return {
|
||||||
getActionInfoMessage: function getActionInfoMessage (this: any, code: string, nick: string, actor: any): any {
|
getActionInfoMessage: function getActionInfoMessage (this: any, code: string, nick: string, actor: any): any {
|
||||||
if (code === '303') {
|
if (code === '303') {
|
||||||
@ -27,8 +28,9 @@ export function chatRoomOverrides (): {[key: string]: Function} {
|
|||||||
initOccupants: function initOccupants (this: any) {
|
initOccupants: function initOccupants (this: any) {
|
||||||
const r = this.__super__.initOccupants()
|
const r = this.__super__.initOccupants()
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
const originalComparatorFunction: Function = this.occupants.comparator
|
const originalComparatorFunction: Function = this.occupants.comparator
|
||||||
this.occupants.comparator = function (this: any, occupant1: any, occupant2: any): Number {
|
this.occupants.comparator = function (this: any, occupant1: any, occupant2: any): number {
|
||||||
// Overriding Occupants comparators, to display anonymous users at the end of the list.
|
// Overriding Occupants comparators, to display anonymous users at the end of the list.
|
||||||
const nick1: string = occupant1.getDisplayName()
|
const nick1: string = occupant1.getDisplayName()
|
||||||
const nick2: string = occupant2.getDisplayName()
|
const nick2: string = occupant2.getDisplayName()
|
||||||
|
@ -19,7 +19,7 @@ export function customizeMessageAction (plugin: any): void {
|
|||||||
try {
|
try {
|
||||||
txt += this.model.getDisplayName() as string
|
txt += this.model.getDisplayName() as string
|
||||||
txt += ' - '
|
txt += ' - '
|
||||||
const date = new Date(this.model.get('edited') || this.model.get('time'))
|
const date = new Date((this.model.get('edited') || this.model.get('time')) as string)
|
||||||
txt += date.toLocaleDateString() + ' ' + date.toLocaleTimeString()
|
txt += date.toLocaleDateString() + ' ' + date.toLocaleTimeString()
|
||||||
txt += '\n'
|
txt += '\n'
|
||||||
} catch {}
|
} catch {}
|
||||||
|
23
conversejs/lib/plugins/livechat-specific/muc-bottom-panel.ts
Normal file
23
conversejs/lib/plugins/livechat-specific/muc-bottom-panel.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the MUCBottomPanel custom element
|
||||||
|
*/
|
||||||
|
export function customizeMUCBottomPanel (plugin: any): void {
|
||||||
|
const _converse = plugin._converse
|
||||||
|
const MUCBottomPanel = _converse.api.elements.registry['converse-muc-bottom-panel']
|
||||||
|
if (MUCBottomPanel) {
|
||||||
|
class MUCBottomPanelOverloaded extends MUCBottomPanel {
|
||||||
|
async initialize (): Promise<any> {
|
||||||
|
await super.initialize()
|
||||||
|
// We must refresh the bottom panel when these features changes (to display the infobox)
|
||||||
|
// FIXME: the custom muc-bottom-panel template should be used here, in an overloaded render method, instead
|
||||||
|
// of using webpack to overload the original file.
|
||||||
|
this.listenTo(this.model.features, 'change:x_peertubelivechat_emoji_only_mode', () => this.requestUpdate())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_converse.api.elements.define('converse-muc-bottom-panel', MUCBottomPanelOverloaded)
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// FIXME: @stylistic/indent is buggy with strings literrals.
|
||||||
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do some customization on the toolbar:
|
* Do some customization on the toolbar:
|
||||||
* * change the appearance of the toggle occupants button
|
* * change the appearance of the toggle occupants button
|
||||||
|
@ -25,7 +25,7 @@ export function getOpenPromise (): any {
|
|||||||
promise.isResolved = false
|
promise.isResolved = false
|
||||||
promise.isPending = false
|
promise.isPending = false
|
||||||
promise.isRejected = true
|
promise.isRejected = true
|
||||||
throw (e)
|
throw (e as Error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return promise
|
return promise
|
||||||
|
@ -22,11 +22,11 @@ export const livechatViewerModePlugin = {
|
|||||||
console.error('[livechatViewerModePlugin] getDefaultMUCNickname is not initialized.')
|
console.error('[livechatViewerModePlugin] getDefaultMUCNickname is not initialized.')
|
||||||
} else {
|
} else {
|
||||||
Object.assign(_converse.exports, {
|
Object.assign(_converse.exports, {
|
||||||
getDefaultMUCNickname: function (this: any): any {
|
getDefaultMUCNickname: function (this: any, ...args: any[]): any {
|
||||||
if (!_converse.api.settings.get('livechat_enable_viewer_mode')) {
|
if (!_converse.api.settings.get('livechat_enable_viewer_mode')) {
|
||||||
return originalGetDefaultMUCNickname.apply(this, arguments)
|
return originalGetDefaultMUCNickname.apply(this, args)
|
||||||
}
|
}
|
||||||
return originalGetDefaultMUCNickname.apply(this, arguments) ??
|
return originalGetDefaultMUCNickname.apply(this, args) ??
|
||||||
getPreviousAnonymousNick() ??
|
getPreviousAnonymousNick() ??
|
||||||
randomNick('Anonymous')
|
randomNick('Anonymous')
|
||||||
}
|
}
|
||||||
@ -72,8 +72,8 @@ export const livechatViewerModePlugin = {
|
|||||||
// Note: when previousNickname is set, model.get('nick') has not the nick yet...
|
// Note: when previousNickname is set, model.get('nick') has not the nick yet...
|
||||||
// It will only come after receiving a presence stanza.
|
// It will only come after receiving a presence stanza.
|
||||||
// So we use previousNickname before trying to read the model.
|
// So we use previousNickname before trying to read the model.
|
||||||
const nick = getPreviousAnonymousNick() ?? (model?.get ? model.get('nick') : '')
|
const nick = getPreviousAnonymousNick() ?? (model?.get ? model.get('nick') as string : '')
|
||||||
refreshViewerMode(nick && !/^Anonymous /.test(nick))
|
refreshViewerMode(!!nick && !/^Anonymous /.test(nick))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,13 @@ export const slowModePlugin = {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const slowModeDuration = parseInt(chatbox?.config?.get('slow_mode_duration'))
|
const slowModeDurationRaw = chatbox?.config?.get('slow_mode_duration') ?? NaN
|
||||||
|
const slowModeDuration =
|
||||||
|
typeof slowModeDurationRaw === 'string'
|
||||||
|
? parseInt(slowModeDurationRaw)
|
||||||
|
: typeof slowModeDurationRaw === 'number'
|
||||||
|
? Math.trunc(slowModeDurationRaw)
|
||||||
|
: NaN
|
||||||
if (!(slowModeDuration > 0)) { // undefined, NaN, ... are not considered > 0.
|
if (!(slowModeDuration > 0)) { // undefined, NaN, ... are not considered > 0.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
function inIframe (): boolean {
|
function inIframe (): boolean {
|
||||||
try {
|
try {
|
||||||
return window.self !== window.top
|
return window.self !== window.top
|
||||||
} catch (e) {
|
} catch (_err) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,13 @@ const locKeys = [
|
|||||||
'moderator_note_original_nick',
|
'moderator_note_original_nick',
|
||||||
'search_occupant_message',
|
'search_occupant_message',
|
||||||
'message_search',
|
'message_search',
|
||||||
'message_search_original_nick'
|
'message_search_original_nick',
|
||||||
|
'emoji_only_info',
|
||||||
|
'announcements_message_type',
|
||||||
|
'announcements_message_type_standard',
|
||||||
|
'announcements_message_type_announcement',
|
||||||
|
'announcements_message_type_highlight',
|
||||||
|
'announcements_message_type_warning'
|
||||||
]
|
]
|
||||||
|
|
||||||
module.exports = locKeys
|
module.exports = locKeys
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user