Compare commits

...

96 Commits

Author SHA1 Message Date
36934ed56f Merge branch 'main' of gitea.nicecrew.digital:matty/peertube-plugin-livechat 2024-10-01 19:20:01 -04:00
b5e18faaaa Merge branch 'main' of https://github.com/JohnXLivingston/peertube-plugin-livechat 2024-10-01 19:19:13 -04:00
John Livingston
4a29b68d57
Merge pull request #558 from framabot/weblate-peertube-livechat-peertube-plugin-livechat
Translations update from Framasoft Weblate
2024-09-25 10:20:43 +02:00
Michał Stankiewicz
c94f23fbe5
Translated using Weblate (Polish)
Currently translated at 30.3% (96 of 316 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/pl/
2024-09-25 10:20:00 +02:00
John Livingston
abfdb0f08d
Fix dependabot config: use versioning-strategy: increase 2024-09-25 10:19:45 +02:00
John Livingston
84b21dde6d
Fix: moderation delay max value was not correctly handled. 2024-09-17 11:38:18 +02:00
John Livingston
1b53a6ec2d
Fix #300: prosody tweaking. 2024-09-17 10:34:11 +02:00
John Livingston
583f581192
Update ConverseJS + CSS fix. 2024-09-17 10:03:57 +02:00
John Livingston
3da491ea49
Merge pull request #549 from framabot/weblate-peertube-livechat-peertube-plugin-livechat-documentation
Translations update from Framasoft Weblate
2024-09-17 09:10:17 +02:00
Milo Ivir
04768347f2
Translated using Weblate (Croatian)
Currently translated at 11.4% (103 of 899 strings)

Translation: PeerTube LiveChat/Peertube Plugin Livechat Documentation
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat-documentation/hr/
2024-09-17 09:10:00 +02:00
John Livingston
261d7e0506
Merge pull request #548 from framabot/weblate-peertube-livechat-peertube-plugin-livechat
Translations update from Framasoft Weblate
2024-09-17 09:09:55 +02:00
Milo Ivir
f98013f2be
Translated using Weblate (Croatian)
Currently translated at 92.7% (293 of 316 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/hr/
2024-09-17 09:09:13 +02:00
John Livingston
c426f0c8c3
Merge pull request #556 from JohnXLivingston/dependabot/npm_and_yarn/typescript-eslint-8.6.0
Bump typescript-eslint from 8.5.0 to 8.6.0
2024-09-17 09:09:08 +02:00
John Livingston
a63240ca25
Merge pull request #557 from JohnXLivingston/dependabot/npm_and_yarn/eslint-8.57.1
Bump eslint from 8.57.0 to 8.57.1
2024-09-17 09:05:10 +02:00
dependabot[bot]
05b1f0f645
Bump eslint from 8.57.0 to 8.57.1
Bumps [eslint](https://github.com/eslint/eslint) from 8.57.0 to 8.57.1.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.57.0...v8.57.1)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-17 03:51:28 +00:00
dependabot[bot]
baf08ae321
Bump typescript-eslint from 8.5.0 to 8.6.0
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.5.0 to 8.6.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.6.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-17 03:51:13 +00:00
John Livingston
149bab2fc5
TODO 2024-09-16 15:04:19 +02:00
John Livingston
80274ecee3
TODO 2024-09-16 15:03:14 +02:00
John Livingston
e08e59d625
Merge pull request #543 from JohnXLivingston/dependabot/npm_and_yarn/multi-cf87d80143
Bump send and express
2024-09-16 11:44:44 +02:00
dependabot[bot]
98eb12104c
Bump send and express
Bumps [send](https://github.com/pillarjs/send) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `send` from 0.18.0 to 0.19.0
- [Release notes](https://github.com/pillarjs/send/releases)
- [Changelog](https://github.com/pillarjs/send/blob/master/HISTORY.md)
- [Commits](https://github.com/pillarjs/send/compare/0.18.0...0.19.0)

Updates `express` from 4.20.0 to 4.21.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.0/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.20.0...4.21.0)

---
updated-dependencies:
- dependency-name: send
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 09:36:28 +00:00
John Livingston
3c93aad053
Merge pull request #542 from JohnXLivingston/dependabot/npm_and_yarn/eslint-config-love-66.0.0
Bump eslint-config-love from 64.0.0 to 66.0.0
2024-09-16 11:35:19 +02:00
John Livingston
0cc87e95e9
Disabling new @typescript-eslint/no-magic-numbers rule. 2024-09-16 11:34:44 +02:00
John Livingston
75dcd3ab1c
Merge pull request #539 from framabot/weblate-peertube-livechat-peertube-plugin-livechat-documentation
Translations update from Framasoft Weblate
2024-09-16 10:44:46 +02:00
Victor Hampel
aaea13a2fc
Translated using Weblate (German)
Currently translated at 100.0% (899 of 899 strings)

Translation: PeerTube LiveChat/Peertube Plugin Livechat Documentation
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat-documentation/de/
2024-09-16 10:42:55 +02:00
ButterflyOfFire
e940fdc2d3
Translated using Weblate (Arabic)
Currently translated at 13.5% (122 of 899 strings)

Translation: PeerTube LiveChat/Peertube Plugin Livechat Documentation
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat-documentation/ar/
2024-09-16 10:42:55 +02:00
Victor Hampel
d7e98642f5
Translated using Weblate (German)
Currently translated at 100.0% (887 of 887 strings)

Translation: PeerTube LiveChat/Peertube Plugin Livechat Documentation
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat-documentation/de/
2024-09-16 10:42:54 +02:00
John Livingston
ee37e8893b
Merge pull request #538 from framabot/weblate-peertube-livechat-peertube-plugin-livechat
Translations update from Framasoft Weblate
2024-09-16 10:42:50 +02:00
T.S
8fa17b050b
Translated using Weblate (Japanese)
Currently translated at 100.0% (316 of 316 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/ja/
2024-09-16 10:11:24 +02:00
Victor Hampel
b336dbbc78
Translated using Weblate (German)
Currently translated at 100.0% (316 of 316 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/de/
2024-09-16 10:11:24 +02:00
Besnik Bleta
a68a2d0e30
Translated using Weblate (Albanian)
Currently translated at 62.6% (198 of 316 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/sq/
2024-09-16 10:11:24 +02:00
Besnik Bleta
99371bcdec
Translated using Weblate (Albanian)
Currently translated at 62.3% (197 of 316 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/sq/
2024-09-16 10:11:24 +02:00
Victor Hampel
1bdd7df712
Translated using Weblate (German)
Currently translated at 100.0% (311 of 311 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/de/
2024-09-16 10:11:24 +02:00
John Livingston
3ec4482043
Fix dependabot config. 2024-09-16 10:11:10 +02:00
dependabot[bot]
9a30958979
Bump eslint-config-love from 64.0.0 to 66.0.0
Bumps [eslint-config-love](https://github.com/mightyiam/eslint-config-love) from 64.0.0 to 66.0.0.
- [Release notes](https://github.com/mightyiam/eslint-config-love/releases)
- [Changelog](https://github.com/mightyiam/eslint-config-love/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mightyiam/eslint-config-love/compare/v64.0.0...v66.0.0)

---
updated-dependencies:
- dependency-name: eslint-config-love
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 03:47:41 +00:00
John Livingston
538ec28da9
Fix typo. 2024-09-12 17:36:48 +02:00
John Livingston
9824435b6e
New features: announcements WIP (#518):
* Documentation.
2024-09-12 12:54:16 +02:00
John Livingston
4e436c00f0
New features: announcements WIP (#518):
* handling message correction.
2024-09-12 12:07:55 +02:00
John Livingston
f0088671ea
New features: announcements WIP (#518):
* prevent annoucement spoofing on backend.
2024-09-12 11:44:35 +02:00
John Livingston
d92bf9073e
New features: announcements WIP (#518):
* Front-end implementation finished.
* Refactoring.
2024-09-12 11:17:44 +02:00
John Livingston
8944bb95d8
New features: announcements WIP (#518). 2024-09-11 19:27:02 +02:00
John Livingston
b357619f7a
Merge pull request #536 from framabot/weblate-peertube-livechat-peertube-plugin-livechat
Translations update from Framasoft Weblate
2024-09-11 14:38:05 +02:00
T.S
5e754b0103
Translated using Weblate (Japanese)
Currently translated at 99.6% (310 of 311 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/ja/
2024-09-11 13:36:12 +02:00
John Livingston
7e2f6ede65
Translated using Weblate (French)
Currently translated at 100.0% (311 of 311 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/fr/
2024-09-11 13:36:12 +02:00
John Livingston
91d6782a3c
Translated using Weblate (German)
Currently translated at 98.3% (306 of 311 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/de/
2024-09-11 13:36:12 +02:00
John Livingston
35c486035e
Translated using Weblate (English)
Currently translated at 100.0% (311 of 311 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/en/
2024-09-11 13:36:11 +02:00
John Livingston
b66b0aa315
Fix emoji picker colors and size. 2024-09-11 12:40:23 +02:00
John Livingston
0b196805b2
Updating ConverseJS (v11 WIP) with latest fixes. 2024-09-11 11:54:50 +02:00
John Livingston
33be9b3fc5
Update dependencies:
* update @tsconfig/nodes12 to @tsconfig/node16
* Peertube >= 5.2.0 required
2024-09-11 11:29:28 +02:00
John Livingston
1d0a7a97d2
Missing SPDX 2024-09-11 10:39:57 +02:00
John Livingston
89f1f42e7a
npm run doc:translate. 2024-09-11 10:37:02 +02:00
John Livingston
c2430856b4
Documentation:
* no_duplicate documentation
* livechat_version_notice documentation
* fix typo in english string
2024-09-11 10:34:47 +02:00
John Livingston
4c84146cff
Replace v11.1.0 by v12.0.0:
There will probably be some breaking changes, so renaming v11.1.0 to
v12.0.0.
Also fixing a typo in a filename.
2024-09-11 10:34:47 +02:00
John Livingston
6f479d26c5
Moving max and default values in constants. 2024-09-11 10:34:47 +02:00
John Livingston
5225257bb5
New option for the moderation bot: forbid duplicate messages (#516). 2024-09-11 10:34:44 +02:00
John Livingston
651641f63c
Fix typo 2024-09-11 10:33:51 +02:00
John Livingston
8dcd3ef488
Merge pull request #533 from framabot/weblate-peertube-livechat-peertube-plugin-livechat
Translations update from Framasoft Weblate
2024-09-11 10:32:28 +02:00
T.S
2d5fec25c7
Translated using Weblate (Japanese)
Currently translated at 100.0% (307 of 307 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/ja/
2024-09-11 03:55:41 +02:00
John Livingston
5566f4b6cf
Prosody modules: fix lua 5.3/5.4 compatibility:
Lua 5.3 comes with new integers type, and can fail if floats are used
where integers are wanted.
2024-09-10 17:58:12 +02:00
John Livingston
5018d04b78
Documentation: livechat_version_notice strings migration. 2024-09-10 12:35:07 +02:00
John Livingston
c65995e5fa
Documentation: new shortcode for livechat version notices. 2024-09-10 12:02:40 +02:00
John Livingston
071ee9f6b4
Dependabot: daily update. 2024-09-10 11:11:28 +02:00
John Livingston
3e23d2751f
Update @tsconfig/node12 2024-09-10 11:10:00 +02:00
John Livingston
9731835c31
Fixing again dependabot config file. 2024-09-10 11:08:18 +02:00
John Livingston
e6381c7bba
Merge pull request #527 from framabot/weblate-peertube-livechat-peertube-plugin-livechat-documentation
Translations update from Framasoft Weblate
2024-09-10 11:06:10 +02:00
Victor Hampel
ace8ad72f5
Translated using Weblate (German)
Currently translated at 100.0% (891 of 891 strings)

Translation: PeerTube LiveChat/Peertube Plugin Livechat Documentation
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat-documentation/de/
2024-09-10 11:05:26 +02:00
John Livingston
50309c6ba9
Merge pull request #525 from framabot/weblate-peertube-livechat-peertube-plugin-livechat
Translations update from Framasoft Weblate
2024-09-10 11:05:22 +02:00
Victor Hampel
0c220b2fc4
Translated using Weblate (German)
Currently translated at 100.0% (306 of 306 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/de/
2024-09-10 11:03:59 +02:00
T.S
1ce68eec7e
Translated using Weblate (Japanese)
Currently translated at 100.0% (306 of 306 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/ja/
2024-09-10 11:03:59 +02:00
John Livingston
5f4cc7c46e
Fix dependabot conf file. 2024-09-10 11:03:51 +02:00
John Livingston
7330729ac1
Enabling dependabot. 2024-09-10 10:59:33 +02:00
John Livingston
d5a25af6c8
npm audit fix 2024-09-10 10:41:56 +02:00
John Livingston
634e894522
Updating stylelint dependencies. 2024-09-10 10:38:45 +02:00
John Livingston
4f2fbfc228
Updating other dependencies. 2024-09-10 10:23:16 +02:00
John Livingston
4b5f83c45f
Updating dependencies. 2024-09-10 10:15:35 +02:00
John Livingston
c561851bb6
Fix a regression in OIDC handling. 2024-09-09 21:21:44 +02:00
John Livingston
edca1be70a
Final eslint 8.57 adjustements.
There are still some FIXME, but should be enought for now.
2024-09-09 20:37:45 +02:00
John Livingston
fd27105c2c
eslint 8.57 WIP:
* tweaking some more rules.
* fixing linting.
2024-09-09 20:01:25 +02:00
John Livingston
c010758164
eslint 8.57 WIP:
* tweaking rules
* fixing issues
2024-09-09 18:47:21 +02:00
John Livingston
7b3d93b290
Typescript v5 + eslint 8.57 WIP
This commit also improves some type handling in the project.
2024-09-09 15:57:04 +02:00
John Livingston
64a9c7be21
Fix typo. 2024-09-07 12:56:35 +02:00
John Livingston
b5990f0c1d
Documentation: fix livechat_label regression (#514). 2024-09-07 12:52:25 +02:00
John Livingston
f15d3ed542
New option for the moderation bot:
* forbid messages with too many special characters (#517).
* update moderation bot to v0.4.0.
* refactoring localization segments to reuse existing one in multiple
  context.
* npm run doc:translate
2024-09-07 12:40:38 +02:00
John Livingston
b6028ef740
Changelog 2024-09-07 12:21:32 +02:00
John Livingston
e61db352c7
Merge pull request #521 from framabot/weblate-peertube-livechat-peertube-plugin-livechat-documentation
Translations update from Framasoft Weblate
2024-09-07 12:14:04 +02:00
Victor Hampel
b3d74a3816
Translated using Weblate (German)
Currently translated at 100.0% (888 of 888 strings)

Translation: PeerTube LiveChat/Peertube Plugin Livechat Documentation
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat-documentation/de/
2024-09-07 12:13:45 +02:00
John Livingston
122ab9725b
Merge pull request #513 from framabot/weblate-peertube-livechat-peertube-plugin-livechat
Translations update from Framasoft Weblate
2024-09-07 12:13:40 +02:00
Victor Hampel
ac64c27c3e
Translated using Weblate (German)
Currently translated at 100.0% (302 of 302 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/de/
2024-09-07 08:09:03 +02:00
Besnik Bleta
8ad762eaa2
Translated using Weblate (Albanian)
Currently translated at 66.2% (196 of 296 strings)

Translation: PeerTube LiveChat/Peertube Plugin LiveChat
Translate-URL: https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat/sq/
2024-09-06 16:02:50 +02:00
John Livingston
acce1c5a5c
Reverting typescript version to 4.3.5, because it breaks linting. 2024-09-06 16:02:22 +02:00
John Livingston
e57d5b0f30
npm run doc:translate 2024-09-06 12:35:38 +02:00
John Livingston
b45ab79c0e
Emoji only mode documentation. 2024-09-06 12:30:43 +02:00
John Livingston
91cddfa8d8
Emoji only mode WIP:
* Button to enable it on all rooms.
2024-09-06 11:53:07 +02:00
John Livingston
08017ac2bb
Emoji only mode WIP:
* refactoring + optimization
* migration
2024-09-06 11:01:48 +02:00
John Livingston
b115c28ee7
Fix typo. 2024-09-05 19:22:58 +02:00
John Livingston
5db4f46421
Emoji only mode WIP:
Fix emojis regexp. The RCPE2 library can't handle long regexp, so we
switch to Oniguruma.
2024-09-05 19:12:07 +02:00
John Livingston
1a75b30c50
Emoji only mode WIP 2024-09-05 18:28:54 +02:00
216 changed files with 16766 additions and 10977 deletions

View File

@ -1,14 +0,0 @@
{
"root": true,
"env": {},
"extends": [],
"globals": {},
"plugins": [],
"ignorePatterns": [
"node_modules/", "dist/", "webpack.config.js",
"build/",
"vendor/",
"support/documentation",
"build-*js"],
"rules": {}
}

31
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,31 @@
# 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
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.

View File

@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
'use strict';
'use strict'
module.exports = {
extends: [
@ -14,7 +14,7 @@ module.exports = {
// extending the kebab-case to accept ConverseJS class names.
'^([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.'
}
]
}

View File

@ -1,5 +1,37 @@
# Changelog
## 12.0.0 (Not Released Yet)
TODO Before releasing:
* update ConverseJS with latest merges (there are currently some known bugs).
* as the Prosody version changes, check these stress test https://github.com/JohnXLivingston/livechat-perf-test/tree/main/tests/33-prosody-gc and apply the correct gc parameter. Also see https://github.com/JohnXLivingston/peertube-plugin-livechat/issues/300
### 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.
### 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
### Minor changes and fixes

View File

@ -174,6 +174,12 @@ $small-view: 800px;
a {
/* See Peertube .video-channel-names */
width: fit-content;
display: flex;
align-items: baseline;
/* stylelint-disable-next-line value-keyword-case */
color: var(--mainForegroundColor);
&:hover,
&:focus,
&:active {
@ -184,12 +190,6 @@ $small-view: 800px;
outline: none !important;
}
width: fit-content;
display: flex;
align-items: baseline;
/* stylelint-disable-next-line value-keyword-case */
color: var(--mainForegroundColor);
div:first-child {
/* See Peertube .video-channel-display-name */
font-weight: variables.$font-semibold;

View File

@ -9,10 +9,10 @@
livechat-token-list {
table {
@include tables.data-table;
width: 100%;
@include tables.data-table;
tr th:first-child,
tr th:last-child {
width: 50px;

View File

@ -10,4 +10,4 @@
@use "video";
@use "configuration/configuration";
@use "admin/firewall/firewall";
@use "list-rooms/list-rooms.scss";
@use "list-rooms/list-rooms";

View File

@ -34,7 +34,7 @@
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.
It should go under the video.
*/

View File

@ -15,7 +15,7 @@ const clientFiles = [
'admin-plugin-client-plugin'
]
function loadLocs() {
function loadLocs(globalFile) {
// Loading english strings, so we can inject them as constants.
const refFile = path.resolve(__dirname, 'dist', 'languages', 'en.reference.json')
if (!fs.existsSync(refFile)) {
@ -25,7 +25,6 @@ function loadLocs() {
// Reading client/@types/global.d.ts, to have a list of needed localized strings.
const r = {}
const globalFile = path.resolve(__dirname, 'client', '@types', 'global.d.ts')
const globalFileContent = '' + fs.readFileSync(globalFile)
const matches = globalFileContent.matchAll(/^declare const LOC_(\w+)\b/gm)
for (const match of matches) {
@ -41,7 +40,7 @@ function loadLocs() {
const define = Object.assign({
PLUGIN_CHAT_PACKAGE_NAME: JSON.stringify(packagejson.name),
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 => ({
entryPoints: [ path.resolve(__dirname, 'client', f + '.ts') ],
@ -59,8 +58,14 @@ const configs = clientFiles.map(f => ({
outfile: path.resolve(__dirname, 'dist/client', f + '.js'),
}))
const defineBuiltin = Object.assign(
{},
loadLocs(path.resolve(__dirname, 'conversejs', 'lib', '@types', 'global.d.ts'))
)
configs.push({
entryPoints: ["./conversejs/builtin.ts"],
define: defineBuiltin,
bundle: true,
minify: true,
sourcemap,

View File

@ -10,12 +10,12 @@ set -euo pipefail
# 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'
wanted_release='v0.12.3-1'
wanted_release='v0.12.4-3'
x86_64_filename='prosody-x86_64.AppImage'
x86_64_sha256sum='f4af9bfefa2f804ad7e8b03a68f04194abb801f070ae620b3d4bcedb144e8523'
x86_64_sha256sum='83a583ac7036387514bed17afab257dab4161ccdd0ab7453818c78b51f830357'
aarch64_filename='prosody-aarch64.AppImage'
aarch64_sha256sum='878c5be719e1e36a84d637fd2bd44e3059aa91ddb6906ad05f1dd0334078df09'
aarch64_sha256sum='7b7e6bf30d4498fc99a40022232c3065707ee4f4df24dc17947b007621634304'
download_dir="$(pwd)/vendor/prosody-appimage"
dist_dir="$(pwd)/dist/server/prosody"

View File

@ -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"
}
}

View File

@ -1,3 +0,0 @@
SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
SPDX-License-Identifier: AGPL-3.0-only

View File

@ -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_DESC: 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_FORBIDDEN_WORDS_REASON_DESC: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_RETRACTATION_REASON_LABEL: 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_DESC: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_LABEL: string
declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_DESC: string
declare const LOC_LIVECHAT_CONFIGURATION_APPLYTOMODERATORS_LABEL: 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_DESC: 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_DESC: 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

View File

@ -243,7 +243,10 @@ function register (clientOptions: RegisterClientOptions): void {
}
} catch (error: any) {
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)
)
}
}
})

View File

@ -46,7 +46,7 @@ async function register (clientOptions: RegisterClientOptions): Promise<void> {
])
const webchatFieldOptions: RegisterClientFormFieldOptions = {
name: 'livechat-active',
label: label,
label,
descriptionHTML: description,
type: 'input-checkbox',
default: true,

View File

@ -22,7 +22,7 @@ export class AdminFirewallElement extends LivechatElement {
public validationError?: ValidationError
@state()
public actionDisabled: boolean = false
public actionDisabled = false
private _asyncTaskRender: Task
@ -101,7 +101,7 @@ 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 =
this.validationError?.properties[`${propertyName}`]
return validationErrorTypes ? (validationErrorTypes.length ? { 'is-invalid': true } : { 'is-valid': true }) : {}

View File

@ -2,6 +2,9 @@
//
// 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 { TemplateResult } from 'lit'
import type { DynamicFormHeader, DynamicFormSchema } from '../../../lib/elements/dynamic-table-form'
@ -64,7 +67,7 @@ export function tplAdminFirewall (el: AdminFirewallElement): TemplateResult {
.maxLines=${maxFirewallFiles}
.validation=${el.validationError?.properties}
.validationPrefix=${'files'}
.rows=${el.firewallConfiguration?.files}
.rows=${el.firewallConfiguration?.files ?? []}
@update=${(e: CustomEvent) => {
el.resetValidation(e)
if (el.firewallConfiguration) {

View File

@ -32,7 +32,7 @@ export class ChannelConfigurationElement extends LivechatElement {
public validationError?: ValidationError
@state()
public actionDisabled: boolean = false
public actionDisabled = false
private _asyncTaskRender: Task
@ -113,7 +113,7 @@ 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 =
this.validationError?.properties[`${propertyName}`]
return validationErrorTypes ? (validationErrorTypes.length ? { 'is-invalid': true } : { 'is-valid': true }) : {}

View File

@ -30,7 +30,7 @@ export class ChannelEmojisElement extends LivechatElement {
public validationError?: ValidationError
@state()
public actionDisabled: boolean = false
public actionDisabled = false
private _asyncTaskRender: Task
@ -192,7 +192,7 @@ export class ChannelEmojisElement extends LivechatElement {
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 item: ChannelEmojisConfiguration['emojis']['customEmojis'][0] = {
@ -211,7 +211,7 @@ export class ChannelEmojisElement extends LivechatElement {
await this.ptTranslate(LOC_ACTION_IMPORT_EMOJIS_INFO)
)
} 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 {
this.actionDisabled = false
}
@ -250,12 +250,27 @@ export class ChannelEmojisElement extends LivechatElement {
a.remove()
} catch (err: any) {
this.logger.error(err)
this.ptNotifier.error(err.toString())
this.ptNotifier.error((err as Error).toString())
} finally {
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.
* @param url the url

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { html } from 'lit'
import { customElement, state } from 'lit/decorators.js'
import { ptTr } from '../../lib/directives/translation'

View File

@ -2,6 +2,9 @@
//
// 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 { ptTr } from '../../lib/directives/translation'
import { html, TemplateResult } from 'lit'

View File

@ -2,14 +2,18 @@
//
// 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 { DynamicFormHeader, DynamicFormSchema } from '../../../lib/elements/dynamic-table-form'
import { ptTr } from '../../../lib/directives/translation'
import { html, TemplateResult } from 'lit'
import { classMap } from 'lit/directives/class-map.js'
import { noDuplicateMaxDelay, forbidSpecialCharsMaxTolerance } from 'shared/lib/constants'
export function tplChannelConfiguration (el: ChannelConfigurationElement): TemplateResult {
const tableHeaderList: {[key: string]: DynamicFormHeader} = {
const tableHeaderList: Record<string, DynamicFormHeader> = {
forbiddenWords: {
entries: {
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)
},
applyToModerators: {
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_LABEL),
description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_APPLYTOMODERATORS_DESC)
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_APPLYTOMODERATORS_LABEL),
description: ptTr(LOC_LIVECHAT_CONFIGURATION_APPLYTOMODERATORS_DESC)
},
label: {
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_LABEL),
description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL_DESC)
},
reason: {
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REASON_LABEL),
description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_REASON_DESC)
colName: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_RETRACTATION_REASON_LABEL),
description: ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_RETRACTATION_REASON_DESC)
},
comments: {
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: {
entries: {
inputType: 'tags',
@ -337,6 +341,246 @@ export function tplChannelConfiguration (el: ChannelConfigurationElement): Templ
${el.renderFeedback('peertube-livechat-bot-nickname-feedback', 'bot.nickname')}
</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
.label=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_LABEL)}
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_FORBIDDEN_WORDS_DESC)}

View File

@ -2,6 +2,9 @@
//
// 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 { DynamicFormHeader, DynamicFormSchema } from '../../../lib/elements/dynamic-table-form'
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>
<h2>${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_EMOJIS_TITLE)}</h2>
<p>
${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_EMOJIS_DESC)}
<livechat-help-button .page=${'documentation/user/streamers/emojis'}>
</livechat-help-button>
</p>
<form role="form" @submit=${el.saveEmojis} @change=${el.resetValidation}>
<div class="peertube-plugin-livechat-configuration-actions">
${
@ -86,7 +90,7 @@ export function tplChannelEmojis (el: ChannelEmojisElement): TemplateResult {
.maxLines=${maxEmojisPerChannel}
.validation=${el.validationError?.properties}
.validationPrefix=${'emojis'}
.rows=${el.channelEmojisConfiguration?.emojis.customEmojis}
.rows=${el.channelEmojisConfiguration?.emojis.customEmojis ?? []}
@update=${(e: CustomEvent) => {
el.resetValidation(e)
if (el.channelEmojisConfiguration) {
@ -106,5 +110,23 @@ export function tplChannelEmojis (el: ChannelEmojisElement): TemplateResult {
</button>
</div>
</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>`
}

View File

@ -11,7 +11,7 @@ import type {
import { ValidationError, ValidationErrorType } from '../../lib/models/validation'
import { getBaseRoute } from '../../../utils/uri'
import { maxEmojisPerChannel } from 'shared/lib/emojis'
import { channelTermsMaxLength } from 'shared/lib/constants'
import { channelTermsMaxLength, noDuplicateMaxDelay, forbidSpecialCharsMaxTolerance } from 'shared/lib/constants'
export class ChannelDetailsService {
public _registerClientOptions: RegisterClientOptions
@ -67,11 +67,43 @@ export class ChannelDetailsService {
// The backend will ignore those values.
if (botConf.enabled) {
propertiesError['bot.nickname'] = []
propertiesError['bot.forbidSpecialChars.tolerance'] = []
propertiesError['bot.noDuplicate.delay'] = []
if (/[^\p{L}\p{N}\p{Z}_-]/u.test(botConf.nickname ?? '')) {
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 v of fw.entries) {
propertiesError[`bot.forbiddenWords.${i}.entries`] = []
@ -146,7 +178,8 @@ export class ChannelDetailsService {
}
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.
// 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> {
const url = getBaseRoute(this._registerClientOptions) +
'/api/configuration/channel/emojis/' +
encodeURIComponent(channelId)
const response = await fetch(
getBaseRoute(this._registerClientOptions) +
'/api/configuration/channel/emojis/' +
encodeURIComponent(channelId),
url,
{
method: 'GET',
headers: this._headers
@ -295,10 +329,11 @@ export class ChannelDetailsService {
channelId: number,
channelEmojis: ChannelEmojis
): Promise<ChannelEmojisConfiguration> {
const url = getBaseRoute(this._registerClientOptions) +
'/api/configuration/channel/emojis/' +
encodeURIComponent(channelId)
const response = await fetch(
getBaseRoute(this._registerClientOptions) +
'/api/configuration/channel/emojis/' +
encodeURIComponent(channelId),
url,
{
method: 'POST',
headers: this._headers,
@ -312,4 +347,24 @@ export class ChannelDetailsService {
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()
}
}

View File

@ -4,7 +4,7 @@
// 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/
export const AddSVG: string =
export const AddSVG =
`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
aria-hidden="true"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
@ -14,7 +14,7 @@ export const AddSVG: string =
</svg>`
// 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"
aria-hidden="true"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"

View File

@ -13,8 +13,8 @@ import { getPtContext } from '../contexts/peertube'
export class TranslationDirective extends AsyncDirective {
private readonly _peertubeHelpers: RegisterClientHelpers
private _translatedValue: string = ''
private _localizationId: string = ''
private _translatedValue = ''
private _localizationId = ''
private _allowUnsafeHTML = false
@ -25,7 +25,7 @@ export class TranslationDirective extends AsyncDirective {
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._allowUnsafeHTML = allowHTML

View File

@ -3,6 +3,9 @@
//
// 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 { html } from 'lit'
import { customElement, property } from 'lit/decorators.js'

View File

@ -3,6 +3,9 @@
//
// 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 { DirectiveResult } from 'lit/directive'
import { ValidationErrorType } from '../models/validation'
@ -20,26 +23,26 @@ import { AddSVG, RemoveSVG } from '../buttons'
type DynamicTableAcceptedTypes = number | string | boolean | Date | Array<number | string>
type DynamicTableAcceptedInputTypes = 'textarea'
| 'select'
| 'checkbox'
| 'range'
| 'color'
| 'date'
| 'datetime'
| 'datetime-local'
| 'email'
| 'file'
| 'image'
| 'month'
| 'number'
| 'password'
| 'tel'
| 'text'
| 'time'
| 'url'
| 'week'
| 'tags'
| 'image-file'
| 'select'
| 'checkbox'
| 'range'
| 'color'
| 'date'
| 'datetime'
| 'datetime-local'
| 'email'
| 'file'
| 'image'
| 'month'
| 'number'
| 'password'
| 'tel'
| 'text'
| 'time'
| 'url'
| 'week'
| 'tags'
| 'image-file'
interface CellDataSchema {
min?: number
@ -47,7 +50,7 @@ interface CellDataSchema {
minlength?: number
maxlength?: number
size?: number
options?: { [key: string]: string }
options?: Record<string, string>
datalist?: DynamicTableAcceptedTypes[]
separator?: string
inputType?: DynamicTableAcceptedInputTypes
@ -59,7 +62,7 @@ interface CellDataSchema {
interface DynamicTableRowData {
_id: number
_originalIndex: number
row: { [key: string]: DynamicTableAcceptedTypes }
row: Record<string, DynamicTableAcceptedTypes>
}
interface DynamicFormHeaderCellData {
@ -68,10 +71,8 @@ interface DynamicFormHeaderCellData {
headerClassList?: string[]
}
export interface DynamicFormHeader {
[key: string]: DynamicFormHeaderCellData
}
export interface DynamicFormSchema { [key: string]: CellDataSchema }
export type DynamicFormHeader = Record<string, DynamicFormHeaderCellData>
export type DynamicFormSchema = Record<string, CellDataSchema>
@customElement('livechat-dynamic-table-form')
export class DynamicTableFormElement extends LivechatElement {
@ -85,19 +86,19 @@ export class DynamicTableFormElement extends LivechatElement {
public maxLines?: number = undefined
@property()
public validation?: {[key: string]: ValidationErrorType[] }
public validation?: Record<string, ValidationErrorType[]>
@property({ attribute: false })
public validationPrefix: string = ''
public validationPrefix = ''
@property({ attribute: false })
public rows: Array<{ [key: string]: DynamicTableAcceptedTypes }> = []
public rows: Array<Record<string, DynamicTableAcceptedTypes>> = []
@state()
public _rowsById: DynamicTableRowData[] = []
@property({ attribute: false })
public formName: string = ''
public formName = ''
@state()
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()
return Object.fromEntries([...Object.entries(this.schema).map((entry) => [entry[0], entry[1].default ?? ''])])
}
@ -245,11 +246,11 @@ export class DynamicTableFormElement extends LivechatElement {
return html`<tr id=${inputId}>
${Object.keys(this.header)
.sort((k1, k2) => this.columnOrder.indexOf(k1) - this.columnOrder.indexOf(k2))
.map(key => this.renderDataCell(key,
rowData.row[key] ?? this.schema[key].default,
rowData._id,
rowData._originalIndex))}
.sort((k1, k2) => this.columnOrder.indexOf(k1) - this.columnOrder.indexOf(k2))
.map(key => this.renderDataCell(key,
rowData.row[key] ?? this.schema[key].default,
rowData._id,
rowData._originalIndex))}
<td class="form-group">
<button type="button"
class="dynamic-table-remove-row"
@ -457,8 +458,7 @@ export class DynamicTableFormElement extends LivechatElement {
inputTitle,
propertyName,
propertySchema,
(propertyValue)?.join(propertySchema.separator ?? ',') ??
propertyValue ?? propertySchema.default ?? '',
(propertyValue)?.join(propertySchema.separator ?? ',') ?? propertyValue ?? propertySchema.default ?? '',
originalIndex)}
${feedback}
`
@ -473,8 +473,7 @@ export class DynamicTableFormElement extends LivechatElement {
inputTitle,
propertyName,
propertySchema,
(propertyValue)?.join(propertySchema.separator ?? ',') ??
propertyValue ?? propertySchema.default ?? '',
(propertyValue)?.join(propertySchema.separator ?? ',') ?? propertyValue ?? propertySchema.default ?? '',
originalIndex)}
${feedback}
`
@ -498,8 +497,10 @@ export class DynamicTableFormElement extends LivechatElement {
}
if (!formElement) {
this.logger.warn(`value type '${(propertyValue.constructor.toString())}' is incompatible` +
`with field type '${propertySchema.inputType as string}' for form entry '${propertyName.toString()}'.`)
this.logger.warn(
`value type '${(propertyValue.constructor.toString())}' is incompatible` +
`with field type '${propertySchema.inputType as string}' for form entry '${propertyName.toString()}'.`
)
}
const classList = ['form-group']
@ -678,7 +679,7 @@ export class DynamicTableFormElement extends LivechatElement {
}
_getInputValidationClass = (propertyName: string,
originalIndex: number): { [key: string]: boolean } => {
originalIndex: number): Record<string, boolean> => {
const validationErrorTypes: ValidationErrorType[] | undefined =
this.validation?.[`${this.validationPrefix}.${originalIndex}.${propertyName}`]

View File

@ -18,7 +18,7 @@ export class HelpButtonElement extends LivechatElement {
public buttonTitle: string | DirectiveResult = ptTr(LOC_ONLINE_HELP)
@property({ attribute: false })
public page: string = ''
public page = ''
@state()
public url: URL = new URL('https://lmddgtfy.net/')

View File

@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { LivechatElement } from './livechat'
import { html } from 'lit'
import type { DirectiveResult } from 'lit/directive'

View File

@ -26,7 +26,7 @@ export class LivechatElement extends LitElement {
this.logger = this.ptContext.logger.createLogger(this.tagName.toLowerCase())
}
protected override createRenderRoot = (): Element | ShadowRoot => {
protected override createRenderRoot = (): HTMLElement | DocumentFragment => {
return this
}
}

View File

@ -3,6 +3,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { LivechatElement } from './livechat'
import { ptTr } from '../directives/translation'
import { html } from 'lit'
@ -21,10 +24,11 @@ import type { DirectiveResult } from 'lit/directive'
// 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">
<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)"/>' +
// 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)"/>' +
// eslint-disable-next-line @stylistic/indent-binary-ops
`</g>
</svg>`
@ -64,10 +68,10 @@ export class TagsInputElement extends LivechatElement {
private readonly _isPressingKey: string[] = []
@property({ attribute: false })
public separator: string = '\n'
public separator = '\n'
@property({ attribute: false })
public animDuration: number = 200
public animDuration = 200
/**
* Overloading the standard focus method.
@ -245,8 +249,9 @@ export class TagsInputElement extends LivechatElement {
if (!this._isPressingKey.includes(e.key)) {
this._isPressingKey.push(e.key)
if ((target.selectionStart === target.selectionEnd) &&
target.selectionStart === 0) {
if (
(target.selectionStart === target.selectionEnd) && target.selectionStart === 0
) {
this._handleDeleteTag((this._searchedTagsIndex.length)
? this._searchedTagsIndex.slice(-1)[0]
: (this.value.length - 1))
@ -259,8 +264,9 @@ export class TagsInputElement extends LivechatElement {
if (!this._isPressingKey.includes(e.key)) {
this._isPressingKey.push(e.key)
if ((target.selectionStart === target.selectionEnd) &&
target.selectionStart === target.value.length) {
if (
(target.selectionStart === target.selectionEnd) && target.selectionStart === target.value.length
) {
this._handleDeleteTag((this._searchedTagsIndex.length)
? this._searchedTagsIndex[0]
: 0)

View File

@ -2,6 +2,9 @@
//
// 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 { html, TemplateResult } from 'lit'
import { unsafeHTML } from 'lit/directives/unsafe-html.js'
@ -23,11 +26,11 @@ export function tplTokenList (el: LivechatTokenListElement): TemplateResult {
<tbody>
${
repeat(el.tokenList ?? [], (token) => token.id, (token) => {
let dateStr: string = ''
let dateStr = ''
try {
const date = new Date(token.date)
dateStr = date.toLocaleDateString() + ' ' + date.toLocaleTimeString()
} catch (err) {}
} catch (_err) {}
return html`<tr>
<td>${
el.mode === 'select'

View File

@ -27,7 +27,7 @@ export class LivechatTokenListElement extends LivechatElement {
public currentSelectedToken?: LivechatToken
@property({ attribute: false })
public actionDisabled: boolean = false
public actionDisabled = false
private readonly _tokenListService: TokenListService
private readonly _asyncTaskRender: Task
@ -83,7 +83,7 @@ export class LivechatTokenListElement extends LivechatElement {
this.dispatchEvent(new CustomEvent('update', {}))
} catch (err: any) {
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 {
this.actionDisabled = false
}
@ -102,7 +102,7 @@ export class LivechatTokenListElement extends LivechatElement {
this.dispatchEvent(new CustomEvent('update', {}))
} catch (err: any) {
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 {
this.actionDisabled = false
}

View File

@ -12,7 +12,7 @@ export enum ValidationErrorType {
}
export class ValidationError extends Error {
properties: {[key: string]: ValidationErrorType[] } = {}
properties: Record<string, ValidationErrorType[]> = {}
constructor (name: string, message: string | undefined, properties: ValidationError['properties']) {
super(message)

View File

@ -60,8 +60,8 @@ async function initChat (video: Video): Promise<void> {
return
}
let showShareUrlButton: boolean = false
let showPromote: boolean = false
let showShareUrlButton = false
let showPromote = false
if (video.isLocal) { // No need for shareButton on remote chats.
const chatShareUrl = settings['chat-share-url'] ?? ''
if (chatShareUrl === 'everyone') {
@ -187,9 +187,10 @@ async function _insertChatDom (
callback: async () => {
try {
// 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(
getBaseRoute(ptContext.ptOptions) + '/api/configuration/room/' +
encodeURIComponent(video.uuid),
url,
{
method: 'GET',
headers: peertubeHelpers.getAuthHeader()
@ -303,7 +304,7 @@ async function _openChat (video: Video): Promise<void | false> {
// Loading converseJS...
await displayConverseJS(ptContext.ptOptions, container, roomkey, 'peertube-video', false)
} catch (err) {
} catch (_err) {
// Displaying an error page.
if (container) {
const message = document.createElement('div')

View File

@ -14,7 +14,7 @@ import { getIframeUri, getXMPPAddr, UriOptions } from '../uri'
import { isAnonymousUser } from '../../../utils/user'
// 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]
@ -61,49 +61,49 @@ export class ShareChatElement extends LivechatElement {
* Should we render the XMPP tab?
*/
@property({ attribute: false })
public xmppUriEnabled: boolean = false
public xmppUriEnabled = false
/**
* Should we render the Dock tab?
*/
@property({ attribute: false })
public dockEnabled: boolean = false
public dockEnabled = false
/**
* Can we use autocolors?
*/
@property({ attribute: false })
public autocolorsAvailable: boolean = false
public autocolorsAvailable = false
/**
* In the Embed tab, should we generated an iframe link.
*/
@property({ attribute: false })
public embedIFrame: boolean = false
public embedIFrame = false
/**
* In the Embed tab, should we generated a read-only chat link.
*/
@property({ attribute: false })
public embedReadOnly: boolean = false
public embedReadOnly = false
/**
* Read-only, with scrollbar?
*/
@property({ attribute: false })
public embedReadOnlyScrollbar: boolean = false
public embedReadOnlyScrollbar = false
/**
* Read-only, transparent background?
*/
@property({ attribute: false })
public embedReadOnlyTransparentBackground: boolean = false
public embedReadOnlyTransparentBackground = false
/**
* In the Embed tab, should we use current theme color?
*/
@property({ attribute: false })
public embedAutocolors: boolean = false
public embedAutocolors = false
protected override firstUpdated (changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties)
@ -156,7 +156,7 @@ export class ShareChatElement extends LivechatElement {
return
}
this.logger.log('Restoring previous state')
if (validTabNames.includes(v.currentTab)) {
if (validTabNames.includes(v.currentTab as string)) {
this.currentTab = v.currentTab
}
this.embedIFrame = !!v.embedIFrame

View File

@ -2,6 +2,9 @@
//
// 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 { html, TemplateResult } from 'lit'
import { ptTr } from '../../../lib/directives/translation'

View File

@ -71,8 +71,7 @@ async function shareChatUrl (
addedNodes.forEach(node => {
if ((node as HTMLElement).localName === 'ngb-modal-window') {
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)) {
return
}

View File

@ -4,6 +4,7 @@
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
import type { Video } from '@peertube/peertube-types'
import type { LiveChatSettings } from '../lib/contexts/peertube'
import { AutoColors, isAutoColorsAvailable } from 'shared/lib/autocolors'
import { getBaseRoute } from '../../utils/uri'
import { logger } from '../../utils/logger'
@ -17,7 +18,7 @@ interface UriOptions {
}
function getIframeUri (
registerOptions: RegisterClientOptions, settings: any, video: Video, uriOptions: UriOptions = {}
registerOptions: RegisterClientOptions, settings: LiveChatSettings, video: Video, uriOptions: UriOptions = {}
): string | null {
if (!settings) {
logger.error('Settings are not initialized, too soon to compute the iframeUri')

View File

@ -156,12 +156,12 @@ function launchTests (): void {
'content-type': 'application/json;charset=UTF-8'
}),
body: JSON.stringify({
test: test
test
})
})
if (!response.ok) {
return {
test: test,
test,
messages: [response.statusText ?? 'Unknown error'],
ok: false
}
@ -169,7 +169,7 @@ function launchTests (): void {
const data = await response.json()
if ((typeof data) !== 'object') {
return {
test: test,
test,
messages: ['Incorrect reponse type: ' + (typeof data)],
ok: false
}
@ -190,6 +190,7 @@ function launchTests (): void {
waiting.innerHTML = '<i>Testing...</i>'
ul.append(waiting)
if ((typeof result.next) === 'function') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
const r: Result = (result.next as Function)()
waiting.remove()
await machine(r)

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
//
// 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 { InitConverseJSParams, ChatPeertubeIncludeMode } from 'shared/lib/types'
@ -17,7 +18,7 @@ declare global {
}
}
let pollListenerInitiliazed: boolean = false
let pollListenerInitiliazed = false
/**
* load the ConverseJS CSS.
@ -152,10 +153,11 @@ async function displayConverseJS (
const authHeader = peertubeHelpers.getAuthHeader()
const url = getBaseRoute(clientOptions) + '/api/configuration/room/' +
encodeURIComponent(roomKey) +
(forceType ? '?forcetype=1' : '')
const response = await fetch(
getBaseRoute(clientOptions) + '/api/configuration/room/' +
encodeURIComponent(roomKey) +
(forceType ? '?forcetype=1' : ''),
url,
{
method: 'GET',
headers: authHeader

View File

@ -4,7 +4,7 @@
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) {
return '/plugins/livechat/router'
}

View File

@ -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"
}
}

View File

@ -1,3 +0,0 @@
SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
SPDX-License-Identifier: AGPL-3.0-only

View File

@ -4,6 +4,7 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable @typescript-eslint/no-require-imports */
const fs = require('node:fs')
const path = require('node:path')
const YAML = require('yaml')

View File

@ -18,8 +18,8 @@ set -x
CONVERSE_VERSION="v11.0.0"
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.
# 2024-09-02: using Converse upstream (v11 WIP).
CONVERSE_COMMIT="9952046d580bc2930e29833f4c9987a3d4c95bc2"
# 2024-09-17: using Converse upstream (v11 WIP).
CONVERSE_COMMIT="07dc6f4f5da5890b02a46a8a2f2d0498649786bc"
# It is possible to use another repository, if we want some customization that are not upstream (yet):
# CONVERSE_VERSION="livechat"
@ -29,8 +29,8 @@ CONVERSE_COMMIT="9952046d580bc2930e29833f4c9987a3d4c95bc2"
# 2024-09-03: include badges short label and quick fix for sendMessage button
CONVERSE_REPO="https://github.com/JohnXLivingston/converse.js"
CONVERSE_VERSION="livechat-11.0.1"
CONVERSE_COMMIT=""
CONVERSE_VERSION="livechat-12.0.0"
# CONVERSE_COMMIT=""
rootdir="$(pwd)"
src_dir="$rootdir/conversejs"
@ -40,6 +40,7 @@ if [ -n "$CONVERSE_COMMIT" ]; then
fi
converse_build_dir="$rootdir/build/conversejs"
converse_destination_dir="$rootdir/dist/client/conversejs"
converse_emoji_destination="$rootdir/dist/converse-emoji.json"
if [[ ! -d $src_dir ]]; then
echo "$0 must be called from the plugin livechat root dir."
@ -119,6 +120,9 @@ cd $rootdir
echo "Copying ConverseJS dist files..."
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."
exit 0

View File

@ -2,6 +2,8 @@
//
// 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 { inIframe } from './lib/utils'
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 { livechatEmojisPlugin } from './lib/plugins/livechat-emojis'
import { moderationDelayPlugin } from './lib/plugins/moderation-delay'
import { livechatAnnouncementsPlugin } from './lib/plugins/livechat-announcements'
declare global {
interface Window {
@ -35,11 +38,17 @@ declare global {
html: Function
sizzle: Function
dayjs: Function
__: Function
u: {
hasClass: Function
addClass: Function
removeClass: Function
}
}
}
initConversePlugins: typeof initConversePlugins
initConverse: typeof initConverse
reconnectConverse?: (room: string) => void
reconnectConverse?: (params: any) => void
externalAuthGetResult?: (data: ExternalAuthResult) => void
}
}
@ -74,6 +83,8 @@ function initConversePlugins (peertubeEmbedded: boolean): void {
converse.plugins.add('livechatViewerModePlugin', livechatViewerModePlugin)
converse.plugins.add('converse-moderation-delay', moderationDelayPlugin)
converse.plugins.add('livechatAnnouncementsPlugin', livechatAnnouncementsPlugin)
}
window.initConversePlugins = initConversePlugins
@ -86,7 +97,7 @@ window.initConversePlugins = initConversePlugins
async function initConverse (
initConverseParams: InitConverseJSParams,
chatIncludeMode: ChatIncludeMode = 'chat-only',
peertubeAuthHeader?: { [header: string]: string } | null
peertubeAuthHeader?: Record<string, string> | null
): Promise<void> {
// First, fixing relative websocket urls.
if (initConverseParams.localWebsocketServiceUrl?.startsWith('/')) {
@ -121,9 +132,9 @@ async function initConverse (
params.view_mode = chatIncludeMode === 'chat-only' ? 'fullscreen' : 'embedded'
params.allow_url_history_change = chatIncludeMode === 'chat-only'
let isAuthenticated: boolean = false
let isAuthenticatedWithExternalAccount: boolean = false
let isRemoteWithNicknameSet: boolean = false
let isAuthenticated = false
let isAuthenticatedWithExternalAccount = false
let isRemoteWithNicknameSet = false
// OIDC (OpenID Connect):
const tryOIDC = (initConverseParams.externalAuthOIDC?.length ?? 0) > 0

View File

@ -66,6 +66,7 @@ CORE_PLUGINS.push('livechat-converse-mam-search')
// We must also add our custom ROOM_FEATURES, so that they correctly resets
// (see headless/plugins/muc, getDiscoInfoFeatures, which loops on this const)
ROOM_FEATURES.push('x_peertubelivechat_mute_anonymous')
ROOM_FEATURES.push('x_peertubelivechat_emoji_only_mode')
_converse.exports.CustomElement = CustomElement

View File

@ -2,6 +2,9 @@
//
// 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 { tplMUCApp } from '../../../shared/components/muc-app/templates/muc-app.js'
import { html } from 'lit'

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { html } from 'lit'
/**

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { html } from 'lit'
import { api } from '@converse/headless'
import { getAuthorStyle } from '../../../../src/utils/color.js'

View File

@ -2,6 +2,9 @@
//
// 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 { tplMUCApp } from '../../../shared/components/muc-app/templates/muc-app.js'
import { html } from 'lit'

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { html } from 'lit'
import { api } from '@converse/headless'
import { getAuthorStyle } from '../../../../src/utils/color.js'

View File

@ -2,6 +2,9 @@
//
// 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 { html } from 'lit'
import { __ } from 'i18n'

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { html } from 'lit'
import { repeat } from 'lit/directives/repeat.js'
import { __ } from 'i18n'

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { __ } from 'i18n'
import BaseModal from 'plugins/modal/modal.js'
import { api } from '@converse/headless'

View File

@ -2,6 +2,9 @@
//
// 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 { html } from 'lit'
import { __ } from 'i18n'

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { html } from 'lit'
import { repeat } from 'lit/directives/repeat.js'
import { __ } from 'i18n'

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { html } from 'lit'
import { repeat } from 'lit/directives/repeat.js'
import { __ } from 'i18n'

View File

@ -2,6 +2,9 @@
//
// 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 { tplMUCApp } from '../../../shared/components/muc-app/templates/muc-app.js'
import { html } from 'lit'

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { html } from 'lit'
import { repeat } from 'lit/directives/repeat.js'
import { __ } from 'i18n'

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { html } from 'lit'
import { repeat } from 'lit/directives/repeat.js'
import { __ } from 'i18n'

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { html } from 'lit'
import { __ } from 'i18n'
@ -20,7 +23,8 @@ export function tplMucTask (el, task) {
type="checkbox"
class="form-check-input"
.checked=${done === true}
@click=${(_ev) => {
@click=${(ev) => {
ev?.preventDefault()
task.set('done', !done)
task.saveItem()
}}

View File

@ -2,6 +2,9 @@
//
// 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 { api } from '@converse/headless'
import { html } from 'lit'

View 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;
}
}
}

View File

@ -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 {
border-top: none !important; // removing border, to avoid confusing the toolbar with an input field.
color: var(--peertube-main-foreground);
@ -43,6 +51,10 @@
// Fixing emoji colors for some emoji like «motorcycle»
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 {
.insert-emoji {
a {
@ -52,13 +64,17 @@
}
.emoji-picker__header {
color: var(--peertube-main-background);
background-color: var(--peertube-main-foreground);
background-color: var(--peertube-main-background);
color: var(--peertube-main-foreground);
.emoji-search {
color: currentcolor;
}
ul {
.emoji-category {
color: var(--peertube-main-background);
background-color: var(--peertube-main-foreground);
background-color: var(--peertube-main-background);
color: var(--peertube-main-foreground);
a {
color: currentcolor;
@ -190,7 +206,7 @@
}
// 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 {
min-width: 50%;
}

View File

@ -7,6 +7,7 @@
@import "./variables";
@import "shared/styles/index";
@import "./peertubetheme";
@import "./announcements";
body.livechat-iframe {
#conversejs .chat-head {
@ -202,6 +203,7 @@ body.converse-embedded {
// This margin-left trick is to align the button on the right.
margin-left: auto !important;
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;
}
}
}
}
}

View File

@ -2,6 +2,9 @@
//
// 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 { __ } from 'i18n'
import { html } from 'lit'
@ -28,8 +31,9 @@ function externalLoginClickHandler (ev, el, externalAuthOIDCUrl) {
console.log('Received an external authentication result...', data)
if (!data.ok) {
// eslint-disable-next-line no-undef
el.external_auth_oidc_alert_message = __(LOC_login_external_auth_alert_message) +
el.external_auth_oidc_alert_message =
// eslint-disable-next-line no-undef
__(LOC_login_external_auth_alert_message) +
(data.message ? ` (${data.message})` : '')
return
}

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { __ } from 'i18n'
import { _converse, api } from '@converse/headless'
import { html } from 'lit'
@ -83,6 +86,20 @@ const tplSlowMode = (o) => {
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) => {
if (!api.settings.get('livechat_enable_viewer_mode')) {
return html``
@ -145,6 +162,7 @@ export default (o) => {
return html`
${tplViewerMode(o)}
${tplSlowMode(o)}
${tplEmojiOnly(o)}
${
mutedAnonymousMessage
? html`<span class="muc-bottom-panel muc-bottom-panel--muted">${mutedAnonymousMessage}</span>`

View File

@ -2,6 +2,9 @@
//
// 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 tplMUCChatarea from '../../src/plugins/muc-views/templates/muc-chatarea.js'
import { html } from 'lit'

View File

@ -2,6 +2,9 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
// FIXME: @stylistic/indent is buggy with strings literrals.
/* eslint-disable @stylistic/indent */
import { html } from 'lit'
import { api } from '@converse/headless'
import { until } from 'lit/directives/until.js'

View File

@ -4,6 +4,9 @@
// SPDX-License-Identifier: MPL-2.0
// 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.
import '../../src/plugins/muc-views/templates/muc.js'
import { getChatRoomBodyTemplate } from '../../src/plugins/muc-views/utils.js'

11
conversejs/lib/@types/global.d.ts vendored Normal file
View 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

View File

@ -4,7 +4,7 @@
import type { ProsodyAuthentInfos } from 'shared/lib/types'
interface AuthHeader { [key: string]: string }
type AuthHeader = Record<string, string>
async function getLocalAuthentInfos (
authenticationUrl: string,
@ -66,7 +66,7 @@ async function getLocalAuthentInfos (
{
'content-type': 'application/json;charset=UTF-8'
}
)
) as HeadersInit
)
})
@ -104,7 +104,7 @@ async function getLocalAuthentInfos (
function getLivechatTokenAuthInfos (): ProsodyAuthentInfos | undefined {
try {
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.
const u = new URL('http://localhost' + hash.substring(1))
const jid = u.searchParams.get('j')

View File

@ -86,7 +86,8 @@ function defaultConverseParams (
'livechatDisconnectOnUnloadPlugin',
'converse-slow-mode',
'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?)
muc_show_info_messages: mucShowInfoMessages,

View 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)
}
}

View File

@ -17,19 +17,19 @@ export const livechatEmojisPlugin = {
livechat_custom_emojis_url: false
})
_converse.api.listen.on('loadEmojis', async (_context: Object, json: any) => {
const url = _converse.api.settings.get('livechat_custom_emojis_url')
_converse.api.listen.on('loadEmojis', async (_context: object, json: Record<string, Record<string, unknown>>) => {
const url = _converse.api.settings.get('livechat_custom_emojis_url') as string | undefined
if (!url) {
return json
}
let customs
let customs: CustomEmojiDefinition[] | undefined
try {
customs = await loadCustomEmojis(url)
} catch (err) {
console.error(err)
}
if (customs === undefined || !customs?.length) {
if (!customs?.length) {
return json
}
@ -51,7 +51,7 @@ export const livechatEmojisPlugin = {
// We must also remove any existing emojis in category other than custom
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)) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete v[def.sn]

View File

@ -9,6 +9,7 @@ import { chatRoomOverrides } from './livechat-specific/chatroom'
import { chatRoomMessageOverrides } from './livechat-specific/chatroom-message'
import { customizeMessageAction } from './livechat-specific/message-action'
import { customizeProfileModal } from './livechat-specific/profile'
import { customizeMUCBottomPanel } from './livechat-specific/muc-bottom-panel'
export const livechatSpecificsPlugin = {
dependencies: ['converse-muc', 'converse-muc-views'],
@ -26,6 +27,7 @@ export const livechatSpecificsPlugin = {
customizeToolbar(this)
customizeMessageAction(this)
customizeProfileModal(this)
customizeMUCBottomPanel(this)
_converse.api.listen.on('chatRoomViewInitialized', function (this: any, _model: any): void {
// Remove the spinner if present...

View File

@ -2,7 +2,8 @@
//
// 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 {
/* 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. */

View File

@ -2,7 +2,8 @@
//
// 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 {
getActionInfoMessage: function getActionInfoMessage (this: any, code: string, nick: string, actor: any): any {
if (code === '303') {
@ -27,8 +28,9 @@ export function chatRoomOverrides (): {[key: string]: Function} {
initOccupants: function initOccupants (this: any) {
const r = this.__super__.initOccupants()
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
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.
const nick1: string = occupant1.getDisplayName()
const nick2: string = occupant2.getDisplayName()

View File

@ -19,7 +19,7 @@ export function customizeMessageAction (plugin: any): void {
try {
txt += this.model.getDisplayName() as string
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 += '\n'
} catch {}

View 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)
}
}

View File

@ -2,6 +2,9 @@
//
// 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:
* * change the appearance of the toggle occupants button

View File

@ -25,7 +25,7 @@ export function getOpenPromise (): any {
promise.isResolved = false
promise.isPending = false
promise.isRejected = true
throw (e)
throw (e as Error)
}
)
return promise

View File

@ -22,11 +22,11 @@ export const livechatViewerModePlugin = {
console.error('[livechatViewerModePlugin] getDefaultMUCNickname is not initialized.')
} else {
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')) {
return originalGetDefaultMUCNickname.apply(this, arguments)
return originalGetDefaultMUCNickname.apply(this, args)
}
return originalGetDefaultMUCNickname.apply(this, arguments) ??
return originalGetDefaultMUCNickname.apply(this, args) ??
getPreviousAnonymousNick() ??
randomNick('Anonymous')
}
@ -72,8 +72,8 @@ export const livechatViewerModePlugin = {
// Note: when previousNickname is set, model.get('nick') has not the nick yet...
// It will only come after receiving a presence stanza.
// So we use previousNickname before trying to read the model.
const nick = getPreviousAnonymousNick() ?? (model?.get ? model.get('nick') : '')
refreshViewerMode(nick && !/^Anonymous /.test(nick))
const nick = getPreviousAnonymousNick() ?? (model?.get ? model.get('nick') as string : '')
refreshViewerMode(!!nick && !/^Anonymous /.test(nick))
})
}
}

View File

@ -21,7 +21,13 @@ export const slowModePlugin = {
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.
return
}

View File

@ -5,7 +5,7 @@
function inIframe (): boolean {
try {
return window.self !== window.top
} catch (e) {
} catch (_err) {
return true
}
}

View File

@ -62,7 +62,13 @@ const locKeys = [
'moderator_note_original_nick',
'search_occupant_message',
'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

199
eslint.config.mjs Normal file
View File

@ -0,0 +1,199 @@
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
//
// SPDX-License-Identifier: AGPL-3.0-only
import love from 'eslint-config-love'
import eslint from '@eslint/js'
import tseslint from 'typescript-eslint'
import typescriptParser from '@typescript-eslint/parser'
import stylistic from '@stylistic/eslint-plugin'
import eslintLit from 'eslint-plugin-lit'
import globals from 'globals'
export default tseslint.config(
{
ignores: [
'node_modules/', 'dist/', 'webpack.config.js',
'build/',
'vendor/',
'support/documentation', 'support',
'build-*js'
]
},
eslint.configs.recommended,
...tseslint.configs.recommended,
{
...love,
files: ['**/*.ts']
},
stylistic.configs.customize({
quotes: 'single',
semi: false,
commaDangle: 'never',
blockSpacing: true,
braceStyle: '1tbs',
indent: 2,
quoteProps: 'as-needed'
}),
{
rules: {
'@stylistic/space-before-function-paren': ['error', { anonymous: 'always', asyncArrow: 'always', named: 'always' }],
'@stylistic/arrow-parens': 'off',
'@stylistic/operator-linebreak': ['error', 'after', { overrides: { '?': 'before', ':': 'before' } }],
'@stylistic/max-statements-per-line': ['error', { max: 2 }]
}
},
{
files: ['**/*.ts'],
rules: {
'@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions'] }],
'no-unused-vars': ['error', { argsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }],
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }],
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': ['error', { checksVoidReturn: false }],
'@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',
'@typescript-eslint/no-explicit-any': 'off', // FIXME: should be "error", and we should use 'unknown' in the code.
'init-declarations': 'off',
'@typescript-eslint/init-declarations': 'off',
'@typescript-eslint/consistent-type-imports': 'off',
'@typescript-eslint/consistent-type-exports': 'off',
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/prefer-promise-reject-errors': 'off',
'max-params': 'off',
'@typescript-eslint/max-params': ['error', { max: 8 }], // FIXME: this rules should use the default max value.
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/no-confusing-void-expression': 'off',
'@typescript-eslint/class-methods-use-this': 'off',
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
'@typescript-eslint/no-magic-numbers': 'off',
'max-len': [
'error',
{
code: 120,
comments: 120
}
]
}
},
{
files: ['**/*.js'],
rules: {
// Disabling typescript-eslint rules for JS files (did not find a simplier way...)
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/no-misused-promises': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/strict-boolean-expressions': 'off',
'@typescript-eslint/return-await': 'off',
'@typescript-eslint/no-invalid-void-type': 'off',
'@typescript-eslint/triple-slash-reference': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/init-declarations': 'off',
'@typescript-eslint/consistent-type-imports': 'off',
'@typescript-eslint/consistent-type-exports': 'off',
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/prefer-promise-reject-errors': 'off',
'@typescript-eslint/max-params': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-confusing-void-expression': 'off',
'@typescript-eslint/class-methods-use-this': 'off',
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
'@typescript-eslint/no-magic-numbers': 'off',
'max-params': 'off',
'init-declarations': 'off',
'no-unused-vars': ['error', { argsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }],
'max-len': [
'error',
{
code: 120,
comments: 120
}
]
}
},
{
files: ['.stylelintrc.js'],
languageOptions: {
globals: globals.node
}
},
{
files: ['server/**/*.js', 'server/**/*.ts'],
languageOptions: {
ecmaVersion: 6,
globals: {
...globals.node
},
parser: typescriptParser,
parserOptions: {
ecmaVersion: 2018,
project: './server/tsconfig.json',
projectService: true
}
}
},
{
files: ['shared/**/*.js', 'shared/**/*.ts'],
languageOptions: {
ecmaVersion: 6,
globals: {
...globals.es2016,
...globals.browser
},
parser: typescriptParser,
parserOptions: {
ecmaVersion: 2018,
project: [
'./server/tsconfig.json'
// './client/tsconfig.json' // FIXME: dont really know what is necessary here.
],
// FIXME: how to make projectService work?
projectService: false
// projectService: {
// allowDefaultProject: ['shared/lib/*.js', 'shared/lib/*.ts'],
// defaultProject: './server/tsconfig.json'
// }
}
}
},
{
files: ['client/**/*.js', 'client/**/*.ts', 'conversejs/**/*.js', 'conversejs/**/*.ts'],
languageOptions: {
ecmaVersion: 6,
globals: {
...globals.browser
},
parser: typescriptParser,
parserOptions: {
ecmaVersion: 2018,
project: [
'./client/tsconfig.json',
'./conversejs/tsconfig.json'
]
}
},
// FIXME: not sure elintLit works.
plugins: {
...eslintLit.configs['flat/recommended'].plugins
},
rules: {
...eslintLit.configs['flat/recommended'].rules
}
},
{
files: ['conversejs/build-*.js', 'conversejs/loc.keys.js', 'conversejs/custom/webpack.livechat.js'],
languageOptions: {
globals: {
...globals.node
}
}
}
)

View File

@ -46,7 +46,7 @@ livechat_configuration_channel_forbidden_words_label_label: الملصقة
avatar_set_option_none: لا شيء
successfully_saved: تم الحفظ بنجاح
livechat_configuration_channel_slow_mode_label: الوضع البطيء
livechat_configuration_channel_forbidden_words_reason_label: السبب
livechat_configuration_channel_retractation_reason_label: السبب
livechat_configuration_channel_bot_options_title: خيارات روبوت الإشراف
livechat_configuration_channel_forbidden_words_comments_label: التعليقات
livechat_configuration_channel_command_message_label: رسالة

View File

@ -302,8 +302,8 @@ livechat_configuration_channel_desc: Sie können hier einige Optionen für diese
einstellen (Moderationsrichtlinien, ...).
livechat_configuration_channel_bot_options_title: Optionen für den Moderationsbot
livechat_configuration_channel_forbidden_words_label: Verbotene Wörter oder Ausdrücke
livechat_configuration_channel_forbidden_words_reason_label: Grund
livechat_configuration_channel_forbidden_words_reason_desc: Anzuzeigender Grund anstelle
livechat_configuration_channel_retractation_reason_label: Grund
livechat_configuration_channel_retractation_reason_desc: Anzuzeigender Grund anstelle
der gelöschen Nachricht
livechat_configuration_channel_forbidden_words_regexp_desc: Wenn Sie diese Option
aktivieren, können Sie reguläre Ausdrücke (regex) verwenden.
@ -353,11 +353,7 @@ livechat_configuration_channel_quote_delay_desc: "Der Chatbot wird die Nachricht
livechat_configuration_channel_command_desc: "Sie können den Chatbot so konfigurieren,
dass er auf Befehle reagiert.\nEin Befehl ist eine Nachricht, die mit einem \"!\"\
\ beginnt, wie zum Beispiel \"!help\" den Befehl \"help\" aufruft.\n"
livechat_configuration_channel_forbidden_words_applytomoderators_label: Auch Nachrichten
von Moderatoren moderieren
livechat_configuration_channel_forbidden_words_applytomoderators_desc: "Standardmäßig
werden Nachrichten von Moderatoren nicht gelöscht, wenn sie verbotene Wörter enthalten.\n
Wenn Sie diese Option aktivieren, werden auch Nachrichten von Moderatoren gelöscht.\n"
livechat_configuration_applytomoderators_label: Auch Nachrichten von Moderatoren moderieren
invalid_value: Ungültiger Wert.
livechat_configuration_channel_forbidden_words_comments_label: Kommentare
livechat_configuration_channel_forbidden_words_comments_desc: "Sie können hier einige
@ -601,3 +597,45 @@ prosody_firewall_name_desc: "Darf nur folgende Zeichen enthalten: alphanumerisch
geladen.\n"
prosody_firewall_content: Dateiinhalt
chat: Chat
emoji_only_info: Wenn der Modus "Nur Emoji" aktiviert ist, können Sie nur Emoji in
Ihren Nachrichten verwenden.
emoji_only_mode_title: Nur Emojis-Modus
emoji_only_mode_desc_1: "Sie können in Ihren Chaträumen einen \"Nur-Emoji-Modus\"
aktivieren.\nWenn dieser Modus aktiviert ist, können die Teilnehmer nur Emojis (Standard-
oder kanalangepasste Emojis) senden.\nModeratoren sind von dieser Einschränkung
nicht betroffen.\n"
emoji_only_mode_desc_3: "Um diesen Modus zu aktivieren oder zu deaktivieren, können
Sie das Raumkonfigurationsformular verwenden.\nWenn Sie ihn für alle Ihre Chaträume
auf einmal aktivieren möchten, können Sie die Schaltfläche unten verwenden.\n"
emoji_only_enable_all_rooms: Aktiviere den Modus "Nur Emoji" in allen Chaträumen des
Kanals
emoji_only_mode_desc_2: "Dieser Modus kann zum Beispiel nützlich sein:\n<ul>\n <li>um
Spam oder beleidigende Nachrichten zu vermeiden, wenn Sie nicht hier sind, um zu
moderieren.</li>\n <li>wenn es zu viele sprechende Teilnehmer gibt, und Sie nicht
mehr richtig moderieren können.</li>\n</ul>\n"
livechat_configuration_applytomoderators_desc: "Standardmäßig sind Nachrichten von
Moderatoren von dieser Funktion nicht betroffen.\nWenn Sie diese Option aktivieren,
werden auch Nachrichten von Moderatoren gelöscht.\n"
livechat_configuration_channel_special_chars_label: Sonderzeichen verbieten
livechat_configuration_channel_special_chars_tolerance_label: Toleranz
livechat_configuration_channel_special_chars_tolerance_desc: Anzahl der Sonderzeichen,
die in einer Nachricht akzeptiert werden, ohne sie zu löschen.
livechat_configuration_channel_special_chars_desc: "Wenn Sie diese Option aktivieren,
wird der Moderationsbot automatisch Nachrichten löschen, die mehr als X Sonderzeichen
enthalten.\nSonderzeichen sind Zeichen, die nicht in eine der folgenden Kategorien
passen: Buchstaben, Zahlen, Satzzeichen, Währungssymbole, Emojis.\n"
feature_comes_with: Diese Funktion wird mit dem Livechatplugin Version X.X.X verfügbar
sein.
livechat_configuration_channel_no_duplicate_label: Keine doppelten Nachrichten
livechat_configuration_channel_no_duplicate_desc: "Wenn Sie diese Option aktivieren,
wird der Chatbot automatisch doppelte Nachrichten moderieren.\nDas heißt, wenn ein
Benutzer die gleiche Nachricht zweimal innerhalb von X Sekunden sendet, wird die
zweite Nachricht gelöscht.\n"
livechat_configuration_channel_no_duplicate_delay_label: Zeitintervall
livechat_configuration_channel_no_duplicate_delay_desc: "Das Intervall in Sekunden,
in dem ein Benutzer die gleiche Nachricht nicht erneut senden kann.\n"
announcements_message_type: Art der Nachricht
announcements_message_type_standard: Standard
announcements_message_type_announcement: Ankündigung
announcements_message_type_highlight: Highlight
announcements_message_type_warning: Warnung

View File

@ -410,8 +410,8 @@ livechat_configuration_channel_forbidden_words_desc: |
Several examples are provided on the documentation page.
livechat_configuration_channel_forbidden_words_desc2: |
One word or expression per line. If you put multiple words on one line, it will only match messages containing the whole sequence.
livechat_configuration_channel_forbidden_words_reason_label: "Reason"
livechat_configuration_channel_forbidden_words_reason_desc: "Reason to display besides
livechat_configuration_channel_retractation_reason_label: "Reason"
livechat_configuration_channel_retractation_reason_desc: "Reason to display besides
deleted messages"
livechat_configuration_channel_forbidden_words_regexp_label: "Consider as regular
expressions"
@ -420,10 +420,9 @@ livechat_configuration_channel_forbidden_words_regexp_desc: "By checking this op
livechat_configuration_channel_forbidden_words_label_label: "Label"
livechat_configuration_channel_forbidden_words_label_desc: "Label for this forbidden
words rule"
livechat_configuration_channel_forbidden_words_applytomoderators_label: "Also moderate
messages from moderators"
livechat_configuration_channel_forbidden_words_applytomoderators_desc: |
By default, moderator messages will not be deleted when containing forbidden words.
livechat_configuration_applytomoderators_label: "Also moderate messages from moderators"
livechat_configuration_applytomoderators_desc: |
By default, moderator messages will not be affected by this feature.
By checking this option, messages from moderators will also be deleted.
livechat_configuration_channel_forbidden_words_comments_label: "Comments"
livechat_configuration_channel_forbidden_words_comments_desc: |
@ -637,3 +636,44 @@ prosody_firewall_name_desc: |
Can only contain: alphanumerical characters, underscores and hyphens.
Scripts will be loaded in alphabetical order.
prosody_firewall_content: File content
emoji_only_info: Emoji only mode is enabled, you can only use emoji in your messages.
emoji_only_mode_title: Emojis only mode
emoji_only_mode_desc_1: |
You can enable an "Emojy only mode" in your chatrooms.
When this mode is enabled, participants can only send emojis (standard, or channel custom emojis).
Moderators are not affected by this limitation.
emoji_only_mode_desc_2: |
This mode can be usefull for example:
<ul>
<li>To avoid spam or offensive message when you are not here to moderate.</li>
<li>When there are too many speaking participants, and you can't no more moderate correctly.</li>
</ul>
emoji_only_mode_desc_3: |
To enable or disable this mode, you can use the room configuration form.
If you want to enable it for all your chatrooms at once, you can use the button bellow.
emoji_only_enable_all_rooms: Enable the emoji only mode on all channel's chatrooms
livechat_configuration_channel_special_chars_label: "Forbid special characters"
livechat_configuration_channel_special_chars_desc: |
By enabling this option, the moderation bot will automatically delete messages containing more than X special characters.
Special characters are those that don't fit into one of these categories: letters, numbers, punctuation symbols, currency symbols, emojis.
livechat_configuration_channel_special_chars_tolerance_label: Tolerance
livechat_configuration_channel_special_chars_tolerance_desc: Number of special characters
to accept in a message without deleting it.
feature_comes_with: This feature comes with the livechat plugin version X.X.X.
livechat_configuration_channel_no_duplicate_label: "No duplicate message"
livechat_configuration_channel_no_duplicate_desc: |
By enabling this options, the moderation bot will automatically moderate duplicate messages.
That means if a user send the same message twice within X seconds, the second message will be deleted.
livechat_configuration_channel_no_duplicate_delay_label: Time interval
livechat_configuration_channel_no_duplicate_delay_desc: |
The interval, in seconds, during which a user can't send again the same message.
announcements_message_type: Message type
announcements_message_type_standard: Standard
announcements_message_type_announcement: Announcement
announcements_message_type_highlight: Highlight
announcements_message_type_warning: Warning

View File

@ -261,18 +261,15 @@ livechat_configuration_channel_forbidden_words_label: Palabras o expresiones pro
livechat_configuration_channel_forbidden_words_desc2: "Una palabra o expresión por
línea. Si pones varias palabras en una línea, solo coincidirá con los mensajes que
contengan la secuencia completa.\n"
livechat_configuration_channel_forbidden_words_reason_label: Motivo
livechat_configuration_channel_forbidden_words_reason_desc: Motivo para mostrar además
livechat_configuration_channel_retractation_reason_label: Motivo
livechat_configuration_channel_retractation_reason_desc: Motivo para mostrar además
de eliminar los mensajes
livechat_configuration_channel_forbidden_words_regexp_label: Considéralo como expresiones
regulares
livechat_configuration_channel_forbidden_words_regexp_desc: Marcando esta opción,
puedes usar expresiones regulares.
livechat_configuration_channel_forbidden_words_applytomoderators_label: Moderar también
livechat_configuration_applytomoderators_label: Moderar también
los mensajes de los moderadores
livechat_configuration_channel_forbidden_words_applytomoderators_desc: "Por defecto,
los mensajes de los moderadores no se borrarán cuando contengan palabras prohibidas.\n
Al marcar esta opción, los mensajes de los moderadores también se eliminarán.\n"
livechat_configuration_channel_forbidden_words_comments_label: Comentarios
livechat_configuration_channel_quote_label: Temporizador
livechat_configuration_channel_quote_desc: "Puedes configurar varios temporizadores

View File

@ -315,15 +315,15 @@ livechat_configuration_channel_forbidden_words_label: Mots ou expressions interd
livechat_configuration_channel_forbidden_words_desc2: "Un mot ou une expression par
ligne. Si vous mettez plusieurs mots sur une même ligne, seuls les messages contenant
la séquence entière seront supprimés.\n"
livechat_configuration_channel_forbidden_words_reason_label: Raison
livechat_configuration_channel_forbidden_words_reason_desc: Raison à affiche à côté
des messages supprimés
livechat_configuration_channel_retractation_reason_label: Raison
livechat_configuration_channel_retractation_reason_desc: Raison à affiche à côté des
messages supprimés
livechat_configuration_channel_forbidden_words_regexp_label: Considérer comme une
expression régulière
livechat_configuration_channel_forbidden_words_regexp_desc: En cochant cette option,
vous pouvez utiliser des expressions régulières.
livechat_configuration_channel_forbidden_words_applytomoderators_label: Également
modérer les messages des modérateur⋅rices
livechat_configuration_applytomoderators_label: Également modérer les messages des
modérateur⋅rices
livechat_configuration_channel_quote_label: Timer
livechat_configuration_channel_quote_desc: "Vous pouvez configurer quelques timers
qui enverrons des messages à intervalle régulier.\nCes messages seront envoyés par
@ -351,9 +351,9 @@ livechat_configuration_channel_forbidden_words_desc: "Vous pouvez configurer que
mots seront instantanément supprimés).\nVous pouvez aussi ajouter une raison optionnelle,
qui sera affichée à la place des messages supprimés.\nQuelques exemples de configuration
sont fournis sur la page de documentation.\n"
livechat_configuration_channel_forbidden_words_applytomoderators_desc: "Par défaut,
les messages des modérateur⋅rices ne seront pas supprimés quand ils contiennent
des mots interdits.\nEn cochant cette option, leur messages seront également supprimé.\n"
livechat_configuration_applytomoderators_desc: "Par défaut, les messages des modérateur⋅rices
ne seront pas affectés par cette fonctionnalité.\nEn cochant cette option, leur
messages seront également supprimés.\n"
livechat_configuration_channel_command_desc: "Vous pouvez configurer le bot pour répondre
à des commandes.\nUne commande est un message qui commence par un \"!\", comme par
exemple \"!help\" qui appellera la commande \"help\".\n"
@ -620,3 +620,36 @@ prosody_firewall_name_desc: "Ne peut contenir que des caractères alphanumériqu
ordre alphabétique.\n"
prosody_firewall_content: Contenu du fichier
chat: Tchat
feature_comes_with: Cette fonctionnalité arrive avec le plugin livechat version X.X.X.
emoji_only_mode_title: Mode Émojis Uniquement
emoji_only_mode_desc_3: "Pour activer ou désactiver ce mode, vous pouvez utiliser
le formulaire de configuration du salon.\nSi vous souhaitez l'activer pour tous
vos salons de discussion en même temps, vous pouvez utiliser le bouton ci-dessous.\n"
livechat_configuration_channel_special_chars_label: Interdire les caractères spéciaux
livechat_configuration_channel_special_chars_desc: "En activant cette option, le robot
de modération supprimera automatiquement les messages contenant plus de X caractères
spéciaux.\nLes caractères spéciaux sont ceux qui n'entrent pas dans l'une de ces
catégories: lettres, chiffres, symboles de ponctuation, symboles monétaires, émojis.\n"
livechat_configuration_channel_special_chars_tolerance_desc: Nombre de caractères
spéciaux à accepter dans un message avant de le supprimer.
livechat_configuration_channel_special_chars_tolerance_label: Tolérance
livechat_configuration_channel_no_duplicate_delay_label: Intervalle de temps
livechat_configuration_channel_no_duplicate_delay_desc: "Intervalle, en secondes,
pendant lequel un⋅e utilisateur⋅rice ne peut pas renvoyer le même message.\n"
emoji_only_info: Le mode Émoji Uniquement est activé, vous ne pouvez utiliser que
des émoji dans vos messages.
emoji_only_mode_desc_1: "Vous pouvez activer un mode \"Émoji Uniquement\" dans vos
salons de discussion.\nLorsque ce mode est activé, les participant⋅es ne peuvent
envoyer que des émojis (standards ou émojis personnalisés de votre chaîne).\nLes
modérateur⋅rices ne sont pas concerné⋅es par cette limitation.\n"
emoji_only_mode_desc_2: "Ce mode peut être utile par exemple:\n<ul>\n <li>Pour éviter
le spam ou les messages offensants lorsque vous n'êtes pas là pour modérer.</li>\n\
\ <li>Lorsqu'il y a trop de participant⋅es qui parlent, et que vous ne pouvez plus
modérer correctement.</li>\n</ul>\n"
emoji_only_enable_all_rooms: Activer le mode Émoji Uniquement sur tous les salons
de discussion de la chaîne
livechat_configuration_channel_no_duplicate_label: Pas de message en double
livechat_configuration_channel_no_duplicate_desc: "En activant cette option, le robot
de modération modérera automatiquement les messages en double.\nCela signifie que
si un⋅e utilisateur⋅rice envoie deux fois le même message en l'espace de X secondes,
le deuxième message sera supprimé.\n"

View File

@ -54,17 +54,13 @@ livechat_configuration_channel_forbidden_words_regexp_label: Tretiraj kao regula
izraze
livechat_configuration_channel_forbidden_words_regexp_desc: Označavanjem ove opcije
možeš koristiti regularne izraze.
livechat_configuration_channel_forbidden_words_applytomoderators_label: Također moderiraj
porukama moderatora
livechat_configuration_applytomoderators_label: Također moderiraj porukama moderatora
livechat_configuration_channel_quote_label: Timer
livechat_configuration_channel_forbidden_words_desc: "Možeš konfigurirati neke riječi
koje će bot automatski moderirati (poruke koje sadrže takve riječi će se odmah izbrisati).\n
Možeš dodati i opcionalni razlog koji će se prikazati na mjestu izbrisanih poruka.\n
Stranica dokumentacije sadrži nekoliko primjera.\n"
livechat_configuration_channel_forbidden_words_reason_label: Razlog
livechat_configuration_channel_forbidden_words_applytomoderators_desc: "Poruke moderatora
se standardno neće izbrisati ako sadrže zabranjene riječi.\nOznačavanjem ove opcije
će se poruke moderatora također izbrisati.\n"
livechat_configuration_channel_retractation_reason_label: Razlog
slow_mode_info: Spori način rada je omogućen. Korisnici mogu slati poruku svakih %1$s
sekundi.
open_chat: Otvori chat
@ -344,3 +340,88 @@ poll_choice_invalid: Ovaj izbor nije valjan.
poll_anonymous_vote_ok: Tvoje se glasanje uzima u obzir. Glasanja su anonimna, neće
se prikazati drugim sudionicima.
poll_vote_ok: Tvoje se glasanje uzima u obzir. Brojači će se uskoro aktualizirati.
feature_comes_with: Ova je funkcija dostupna s dodatkom za chat uživo verzije X.X.X.
emoji_only_info: Kad je modus „Samo emoji” aktiviran, u svojim porukama možeš koristiti
samo emojije.
emoji_only_mode_title: „Samo emoji” modus
emoji_only_mode_desc_1: "U svojim chat sobama možeš aktivirati „Samo emoji” modus.\n
Kad je taj modus aktiviran, sudionici mogu slati samo emojije (standardne ili prilagođene
emojije kanala).\nOvo ograničenje ne vrijedi za moderatore.\n"
emoji_only_mode_desc_3: "Za aktiviranje ili deaktiviranje ovog modusa možeš koristiti
obrazac za konfiguraciju sobe.\nAko ga želiš aktivirati za sve svoje chat sobe odjednom
koristi donji gumb.\n"
emoji_only_enable_all_rooms: Aktiviraj „Samo emoji” modus u svim chat sobama kanala
task_list_pick_message: "Nakon što odabereš popis zadataka, stvorit će se novi zadatak.\n
Za prikaz zadatka otvori aplikaciju zadataka koristeći gornji izbornik.\nViše informacija
u dokumentaciji dodatka za chat uživo.\n"
livechat_configuration_channel_retractation_reason_desc: Razlog koji se prikazuje
pored izbrisanih poruka
livechat_configuration_applytomoderators_desc: "Ova funkcija standardno neće utjecati
na poruke moderatora.\nOznačavanjem ove opcije će se izbrisati i poruke moderatora.\n"
livechat_configuration_channel_special_chars_label: Zabrani posebne znakove
livechat_configuration_channel_special_chars_tolerance_label: Tolerancija
livechat_configuration_channel_special_chars_tolerance_desc: Dozvoljen broj posebnih
znakova u poruci bez da se poruka izbriše.
livechat_configuration_channel_no_duplicate_label: Bez duplih poruka
livechat_configuration_channel_no_duplicate_delay_label: Vremenski interval
livechat_configuration_channel_no_duplicate_delay_desc: "Interval u sekundama, tijekom
kojeg korisnik ne može ponovo poslati istu poruku.\n"
livechat_configuration_channel_no_duplicate_desc: "Aktiviranjem ove opcije, bot za
moderiranje će automatski moderirati duple poruke.\nTo znači da ako korisnik pošalje
istu poruku dva puta unutar X sekundi, druga će se poruka izbrisati.\n"
livechat_configuration_channel_anonymize_moderation_label: Anonimiziraj radnje moderiranja
livechat_configuration_channel_anonymize_moderation_desc: "Standardna vrijednost za
anonimiziranje radnji moderiranja za nove sobe.\nKada je ovo aktivirano, radnje
moderiranja bit će anonimizirane, kako bi se izbjeglo otkrivanje tko isključuje/izbacuje/…
sudionike.\n"
announcements_message_type: Vrsta poruke
announcements_message_type_standard: Standardna
announcements_message_type_announcement: Najava
announcements_message_type_highlight: Istaknuto
announcements_message_type_warning: Upozorenje
moderator_notes_create_error: Greška prilikom spremanja bilješke
moderator_note_create: Stvori novu bilješku
moderator_note_delete: Izbriši bilješku
moderator_note_description: Opis
moderator_note_delete_confirm: Stvarno želiš izbrisati ovu bilješku?
moderator_note_create_for_participant: Stvori novu bilješku
moderator_note_search_for_participant: Pretraži bilješke
moderator_note_filters: Filtri pretrage
moderator_note_original_nick: Nadimak sudionika u trenutku izrade bilješke
avatar_set_option_none: Bez
search_occupant_message: Pretraži sve poruke
message_search: Pretraživanje poruka
moderator_notes: Bilješke moderiranja
livechat_configuration_channel_mute_anonymous_desc: "Standardna vrijednost za nove
chat sobe.\nZa postojeće chat sobe funkciju možeš promijeniti u obrascu za konfiguraciju
sobe.\nKada je ova funkcija aktivirana, anonimni korisnici mogu samo čitati chat,
ali ne mogu slati poruke.\n"
emoji_only_mode_desc_2: "Ovaj modus može biti koristan na primjer:\n<ul>\n <li>za
izbjegavanje neželjenih ili uvredljivih poruka kada nisi tu da moderiraš.</li>\n\
\ <li>kada postoji previše sudionika koji govore i ne možeš više ispravno moderirati.</li>\n\
</ul>\n"
livechat_configuration_channel_special_chars_desc: "Aktiviranjem ove opcije, bot za
moderiranje će automatski izbrisati poruke koje sadrže više od X posebnih znakova.\n
Posebni znakovi su znakovi koji se ne uklapaju u nijednu od ovih kategorija: slova,
brojevi, znakovi interpunkcije, znakovi valuta, emojiji.\n"
message_search_original_nick: Nadimak sudionika kada je poruka poslana
prosody_firewall_label: Aktiviraj Prosody mod_firewall
prosody_firewall_description: "<a href=\"https://modules.prosody.im/mod_firewall\"\
\ target=\"_blank\">mod_firewall</a> možeš aktivirati na svom Prosody serveru.\n
Za više informacija pročitaj <a href=\"https://livingston.frama.io/peertube-plugin-livechat/documentation/admin/mod_firewall/\"\
\ target=\"_blank\">dokumentaciju</a>.\n"
prosody_firewall_configure_button: "<a class=\"peertube-button-link orange-button\"\
\ href=\"/p/livechat/admin/firewall\" target=\"_blank\">Kofiguriraj mod_firewall</a>\n"
prosody_firewall_configuration: Prosody mod_firewall konfiguracija
prosody_firewall_configuration_help: "Ovdje možeš konfigurirati modul Prosody <a href=\"\
https://modules.prosody.im/mod_firewall\" target=\"_blank\">mod_firewall</a>.\n
U nastavku možeš stvoriti više konfiguracijskih datoteka i promijeniti njihov redoslijed.\n
Nemoj se ustručavati dijeliti svoje konfiguracije sa zajednicom (na primjer dodavanjem
nekih primjera u dokumentaciju dodatka).\n"
prosody_firewall_file_enabled: Aktivirano
prosody_firewall_name: Ime
prosody_firewall_content: Sadržaj datoteke
share_chat_dock: Dock
chat: Chat
prosody_firewall_name_desc: "Smije sadržati samo alfanumeričke znakove, podvlake i
crtice.\nSkripta će se učitavati abecednim redom.\n"

View File

@ -154,8 +154,6 @@ auto_ban_anonymous_ip_description: "このオプションを有効にすると
livechat_configuration_channel_desc: このチャンネルの設定を開始できます(モデレートポリシーなど)。
livechat_configuration_channel_forbidden_words_desc: "Botにより自動的にモデレーションする単語を設定できます単語が含まれるメッセージは即座に削除されます。\n\
削除されたメッセージの箇所に表示する、削除された理由を設定することもできます。\nいくつかの例をドキュメントで提供していますので、必要があれば確認してください。\n"
livechat_configuration_channel_forbidden_words_applytomoderators_desc: "デフォルトでは、モデレーターのメッセージは禁止単語・語句が含まれていても削除されません。\n\
このオプションを有効にすると、モデレーターからのメッセージも削除されるようになります。\n"
save: 保存
prosody_components_interfaces_description: "外部コンポーネント接続のためにリッスンするネットワークインターフェイスです。<br>\n
リッスンするIPのリストをコンマ区切りで入力してください。スペースは除かれます<br>\n全IPv4インタフェースでリッスンする場合は、 «*»、IPv6の場合は、«::»
@ -177,11 +175,11 @@ livechat_configuration_channel_enable_bot_label: モデレーターBotを有効
livechat_configuration_channel_bot_options_title: モデレーターBotの設定
livechat_configuration_channel_forbidden_words_label: 禁止単語または語句
livechat_configuration_channel_forbidden_words_desc2: "単語または語句を1行ごとに記載してください。複数の単語を1行に記載した場合、記載したすべての内容を含むメッセージのみが一致するようになります。\n"
livechat_configuration_channel_forbidden_words_reason_label: 理由
livechat_configuration_channel_forbidden_words_reason_desc: 削除理由
livechat_configuration_channel_retractation_reason_label: 理由
livechat_configuration_channel_retractation_reason_desc: 削除理由
livechat_configuration_channel_forbidden_words_regexp_label: 正規表現
livechat_configuration_channel_forbidden_words_regexp_desc: このオプションを有効にすると、正規表現として処理します。
livechat_configuration_channel_forbidden_words_applytomoderators_label: モデレーターからのメッセージも対象にする
livechat_configuration_applytomoderators_label: モデレーターからのメッセージも対象にする
livechat_configuration_channel_quote_label: タイマー
livechat_configuration_channel_quote_desc2: "メッセージ1つに対して1行で入力してください。\n複数のメッセージが入力されている場合、X分おきにランダムに選択されます。\n"
livechat_configuration_channel_command_message_label: メッセージ
@ -246,7 +244,7 @@ login_remote_peertube_url: あなたのPeerTubeインスタンスURL
login_remote_peertube_url_invalid: 無効なPeerTube URLです。
login_remote_peertube_no_livechat: このPeerTubeインスタンスには、ライブチャットプラグインがインストールされていません。
login_remote_peertube_video_not_found_try_anyway_button: PeerTubeインスタンスで動画を開く
livechat_configuration_channel_anonymize_moderation_label: モデレーション操作を匿名
livechat_configuration_channel_anonymize_moderation_label: モデレーション操作を匿名
moderator_notes_create_error: ノートを保存中にエラーが発生しました
moderator_note_create: 新規ノートを作成
moderator_note_description: 概要
@ -377,3 +375,29 @@ invalid_value_duplicate: 重複した値です
livechat_configuration_channel_emojis_desc: "チャンネルのカスタム絵文字を設定できます。\nこれらの絵文字は、絵文字ピッカーから利用できます。\n\
絵文字は、短縮名でも使用できます。(例えば\":shortname:\"\n"
chat: チャット
emoji_only_mode_title: 絵文字のみモード
emoji_only_mode_desc_1: "チャットルームで\"絵文字のみモード\"を有効にできます。\nこのモードが有効な時は、参加者は絵文字標準またはチャンネル専用のカスタム絵文字のみを送信できます。\n\
モデレーターはこの制限の影響を受けません。\n"
emoji_only_enable_all_rooms: 絵文字のみモードを全てのチャンネルのチャットルームに適用する
emoji_only_info: 絵文字のみモードが有効な場合、メッセージ内で絵文字のみ使用できます。
emoji_only_mode_desc_2: "このモードは次のような場合に効果があります:\n<ul>\n <li>スパムまたはモデレーターがいない場合に攻撃的なメッセージを抑止するため。</li>\n\
\ <li>多数の参加者が存在し、正確なモデレーションが実施できない場合のため。</li>\n</ul>\n"
livechat_configuration_channel_special_chars_tolerance_label: 許容範囲
emoji_only_mode_desc_3: "この機能の設定切り替えは、ルーム設定フォームから可能です。\n一度にあなたの全てのチャットルームにこの設定を適用する場合は、以下のボタンをクリックしてください。\n"
livechat_configuration_channel_special_chars_desc: "このオプションを有効にすると、モデレーションBotは、X文字以上の特殊文字が含まれるメッセージを自動的に削除します。\n\
特殊文字は、文字、数字、句読点記号、通貨記号、絵文字のいずれにも当てはまらないものを指します。\n"
livechat_configuration_applytomoderators_desc: "初期状態では、モデレーターメッセージはこの機能の影響を受けません。\n\
このオプションを有効にすると、モデレーターからのメッセージも削除されるようになります。\n"
livechat_configuration_channel_special_chars_label: 特殊文字を禁止する
livechat_configuration_channel_special_chars_tolerance_desc: 削除しない特殊文字の文字数を指定してください。
feature_comes_with: この機能は、livechatプラグイン バージョン X.X.Xにて登場します。
livechat_configuration_channel_no_duplicate_delay_label: 間隔
livechat_configuration_channel_no_duplicate_delay_desc: "ユーザーが同じメッセージを再度送信するために空けなければいけない時間を秒単位で指定してください。\n"
livechat_configuration_channel_no_duplicate_label: 重複したメッセージはありません
livechat_configuration_channel_no_duplicate_desc: "このオプションを有効にすると、モデレーションBotが重複したメッセージに自動的に対応します。\n\
これは、もしユーザーが同じメッセージX秒以内に2回送信した場合に、2回目のメッセージを削除します。\n"
announcements_message_type: メッセージタイプ
announcements_message_type_standard: 標準
announcements_message_type_announcement: アナウンス
announcements_message_type_highlight: ハイライト
announcements_message_type_warning: 警告

View File

@ -61,7 +61,7 @@ task_name: Nazwa zadania
avatar_set_option_cat: Koty
task_description: Opis
task_delete: Usuń zadanie
livechat_configuration_channel_forbidden_words_reason_label: Powód
livechat_configuration_channel_retractation_reason_label: Powód
livechat_configuration_channel_forbidden_words_label_label: Etykieta
copied: Skopiowano
autocolors_label: Automatyczne wykrywanie kolorów
@ -72,3 +72,52 @@ poll: Ankieta
poll_question: Pytanie
poll_anonymous_results: Anonimowe wyniki
poll_choice_n: 'Wybór {{N}}:'
external_auth_custom_oidc_title: <h4>OpenID Connect</h4>
external_auth_custom_oidc_label: Użycie dostawcy OpenID Connect
external_auth_custom_oidc_button_label_label: Etykieta przycisku połączenia
federation_dont_publish_remotely_label: Nie publikuj informacji o czacie
federation_no_remote_chat_description: "Zaznaczenie tego ustawienia spowoduje, że
instancja nigdy nie będzie wyświetlać czatów ze zdalnych filmów.\n"
transparent_background: Przezroczyste tło (do integracji z transmisją, na przykład
z OBS)
external_auth_custom_oidc_button_label_description: Ta etykieta będzie wyświetlana
użytkownikom jako etykieta przycisku do uwierzytelniania u tego dostawcy OIDC.
chat: Czat
external_auth_description: "<h3>Uwierzytelnianie zewnętrzne</h3>\nDla użytkowników,
którzy nie mają konta Peertube, można włączyć różne tryby uwierzytelniania w oparciu
o zdalnych dostawców uwierzytelniania.\n"
external_auth_custom_oidc_description: "Można skonfigurować zewnętrznego dostawcę
OpenID Connect, który może być używany do logowania się do czatu.\nWięcej informacji
można znaleźć w dokumentacji:\n<a href=\"https://livingston.frama.io/peertube-plugin-livechat/documentation/admin/settings/\"\
\ target=\"_blank\">Ustawienia</a>.\n"
chat_terms_label: Regulamin
chat_terms_description: "Poniższy regulamin będzie wyświetlany wszystkim użytkownikom
po dołączeniu do pokojów rozmów.\nStreamerzy mogą również skonfigurować swoje regulaminy
dla swoich kanałów, które będą wyświetlane zaraz po tych globalnych zasadach i warunkach.\n"
use_current_theme_color: Użyj bieżących kolorów motywu
federation_dont_publish_remotely_description: "Zaznaczenie tego ustawienia spowoduje,
że Twoja instancja nie będzie publikować informacji o czacie na fediwersum.\nZdalne
instancje Peertube nie będą wiedziały, że są czatami powiązanymi z Twoimi filmami.<br>\n
<b>Uwaga</b>: jeśli już istnieją czaty, możliwe, że informacje zostały już opublikowane.\n
Będziesz musiał poczekać na następną aktualizację wideo, zanim informacje zostaną
cofnięte.\nPonadto, jeśli wyłączysz to ustawienie, będziesz musiał poczekać na aktualizację
filmów, zanim informacje\nzostaną ponownie opublikowane. Aktualizacja ta następuje
między innymi po wznowieniu lub zakończeniu transmisji na żywo.<br>\n<b>Uwaga</b>:
to ustawienie wpływa tylko na publikację informacji za pośrednictwem protokołu ActivityPub.\n
Nie zapobiegnie ono wykryciu obecności czatu i próby połączenia się z nim przez
zdalną aplikację.\n"
tips_for_streamers: "Wskazówki dla streamerów: Aby osadzić czat w swojej transmisji,
np. w OBS,\nwygeneruj link tylko do odczytu i użyj go jako źródła przeglądarki.\n"
generate_iframe: Generuj kod iframe, aby osadzić czat na stronie internetowej
chat_for_live_stream: 'Czat transmisji:'
web: Przeglądarka
connect_using_xmpp_help: Z pokojem można połączyć się za pomocą zewnętrznego konta
XMPP i ulubionego klienta XMPP.
important_note_text: "Dokumentację wtyczki można znaleźć tutaj:\n<a href=\"https://livingston.frama.io/peertube-plugin-livechat/\"\
\ target=\"_blank\">\n Peertube Plugin Livechat documentation\n</a>.\n"
diagnostic: "Przed zwróceniem się o pomoc, skorzystaj z narzędzia diagnostycznego:\n
<a class=\"peertube-plugin-livechat-launch-diagnostic\">Uruchom diagnostykę</a>\n
(jeśli ten przycisk nie otwiera nowego okna, spróbuj odświeżyć stronę).\n"
federation_no_remote_chat_label: Nie wyświetlaj zdalnych czatów
federation_description: "<h3>Federacja</h3>\nPoniższe ustawienia dotyczą federacji
z innymi instancjami Peertube,\ni innym oprogramowaniem fediwersum.\n"

View File

@ -87,7 +87,7 @@ prosody_muc_expiration_description: "Këtu mund të zgjidhni se për sa kohë mb
të ruhet për 1 <b>vit</b>. 1-shin mund ta zëvendësoni me çfarëdo vlere numër të
plotë.</li>\n <li><b>never</b>: lënda nuk skadon kurrë dhe do të mbahet përgjithnjë.</li>\n\
</ul>\n"
livechat_configuration_channel_forbidden_words_reason_label: Arsye
livechat_configuration_channel_retractation_reason_label: Arsye
livechat_configuration_channel_forbidden_words_comments_label: Komente
livechat_configuration_channel_quote_label2: Mesazhe
livechat_configuration_channel_command_message_desc: Mesazhi për tu dërguar.
@ -246,3 +246,7 @@ poll_anonymous_vote_ok: Vota juaj është marrë parasysh. Votat janë anonime,
poll_vote_ok: Vota juaj është marrë parasysh, numëratorët do të përditësohen në çast.
moderator_notes: Shënime moderimi
chat: Fjalosje
converse_theme_option_cyberpunk: Temë ConverseJS Cyberpunk
livechat_configuration_channel_special_chars_tolerance_desc: Numër shenjash speciale
për tu pranuar në një mesazh, pa e fshirë atë.
announcements_message_type: Lloj mesazhi

View File

@ -37,7 +37,7 @@ menu_configuration_label: Chattrum
livechat_configuration_title: Anpassa din sändnings chattrum
livechat_configuration_channel_title: Kanalinställningar
livechat_configuration_channel_forbidden_words_label: Förbjudna ord och uttryck
livechat_configuration_channel_forbidden_words_reason_label: Anledning
livechat_configuration_channel_retractation_reason_label: Anledning
livechat_configuration_channel_quote_label: Timer
livechat_configuration_channel_quote_delay_label: Skicka var X:e minut
livechat_configuration_channel_command_message_desc: Meddelandet som ska skickas.

Some files were not shown because too many files have changed in this diff Show More