20 Commits

Author SHA1 Message Date
21ecdf77c6 I don't think it worked 2024-08-05 21:49:12 -04:00
cfe2ac0607 maybeee?? 2024-08-05 21:46:02 -04:00
74566a895a fix duplicate requires 2024-08-05 21:28:09 -04:00
587334a3e1 separate the nigbot generation and normal avatar generation 2024-08-05 21:26:11 -04:00
30cce2ec03 probably fix issue with Nigbot avatar generation 2024-08-05 21:10:10 -04:00
24f48788ab wheee 2024-08-05 21:04:55 -04:00
bed00aa4c5 niggers are so black tbh 2024-08-05 20:44:04 -04:00
fd16c95b8f add nigbot build job 2024-08-05 20:29:46 -04:00
39f6e4c637 maybe fr fr? 2024-08-05 20:12:46 -04:00
f8c34213cb maybe fr fr fix it? 2024-08-05 19:46:09 -04:00
eb889711f5 fix invalid-avatar set 2024-08-05 19:32:57 -04:00
8dd6ed7888 hopefully fix avatar generation for nigbot avatar 2024-08-05 19:25:45 -04:00
0cc38ac575 update index.md 2024-08-05 19:20:28 -04:00
cde9b3f74a add options in config 2024-08-05 17:54:22 -04:00
75245c0858 add nigbot avatar 2024-08-05 17:42:01 -04:00
be59329581 from 30 to 800 kb for emojis 2024-08-01 21:17:31 -04:00
6d103af5c9 Merge tag 'v10.3.3' of https://github.com/JohnXLivingston/peertube-plugin-livechat
v10.3.3

Minor changes and fixes:

* Fix #481: Moderation bot was not able to connect when remote chat was disabled.
* Some cleaning in code generating Prosody configuration file.
2024-07-29 19:44:23 -04:00
e57d39c8ab Fix #481:
* Moderation bot was not able to connect when remote chat was disabled.
* Some cleaning in code generating Prosody configuration file.
* Bump version 10.3.3
2024-07-25 12:09:55 +02:00
9526a19aab what the fuck 2024-07-20 22:45:58 -04:00
db1993f97e Fix #477: ended polls never disappear when archiving is disabled (and no more than 20 new messages). 2024-07-18 10:24:54 +02:00
55 changed files with 210 additions and 314 deletions

View File

@ -1,10 +1,17 @@
# Changelog # Changelog
## 11.0.0 (Not Released Yet) ## 10.3.3
### New features ### Minor changes and fixes
* Updating ConverseJS, to use upstream (v11 WIP). This comes with many improvments and new features. * Fix #481: Moderation bot was not able to connect when remote chat was disabled.
* Some cleaning in code generating Prosody configuration file.
## 10.3.2
### Minor changes and fixes
* Fix #477: ended polls never disappear when archiving is disabled (and no more than 20 new messages).
## 10.3.1 ## 10.3.1

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
#peertube-plugin-livechat-container { #peertube-plugin-livechat-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
@ -28,7 +28,7 @@
height: 100%; height: 100%;
converse-muc { converse-muc {
min-height: max(58vh, 400px); min-height: max(59vh, 400px);
} }
} }

View File

@ -58,6 +58,9 @@ const avatarPartsDef = {
fur: 10, fur: 10,
eyes: 15, eyes: 15,
mouth: 10 mouth: 10
},
'nctv': {
body: null,
} }
} }
@ -169,6 +172,23 @@ async function generateAvatars (part) {
} }
} }
const generateNigbotAvatar = async () => {
console.log('Starting generating nigbot avatar');
const inputDir = './assets/images/avatars/nctv/';
const outputDir = './dist/server/avatars/nctv/';
fs.mkdirSync(outputDir, { recursive: true });
const buff = await sharp(path.join(inputDir, 'nigbot.png')).toBuffer();
await sharp(buff)
.resize(60, 60)
.png({ palette: true })
.toFile(path.join(outputDir, '1.png'));
}
async function generateBotsAvatars () { async function generateBotsAvatars () {
{ {
// Moderation bot avatar: choosing some parts, and turning it so it is facing left. // Moderation bot avatar: choosing some parts, and turning it so it is facing left.
@ -294,6 +314,21 @@ async function generateBotsAvatars () {
}) })
.toFile(path.join(botOutputDir, '1.png')) .toFile(path.join(botOutputDir, '1.png'))
} }
{
// Nigbot avatar for users
const inputDir = './assets/images/avatars/nctv'
const botOutputDir = './dist/server/bot_avatars/nctv/'
fs.mkdirSync(botOutputDir, { recursive: true })
const buff = await sharp(path.join(inputDir, 'nigbot.png'))
.toBuffer()
await sharp(buff)
// .resize(60, 60)
.png()
.toFile(path.join(botOutputDir, '1.png'))
}
} }
if (isMainThread) { if (isMainThread) {
@ -337,6 +372,9 @@ if (isMainThread) {
throw err throw err
} }
) )
} else if (part === 'nctv') {
generateNigbotAvatar();
parentPort.postMessage('done');
} else { } else {
generateAvatars(part).then( generateAvatars(part).then(
() => { () => {

View File

@ -15,21 +15,32 @@ set -x
# Set CONVERSE_VERSION and CONVERSE_REPO to select which repo and tag/commit/branch use. # Set CONVERSE_VERSION and CONVERSE_REPO to select which repo and tag/commit/branch use.
# Defaults values: # Defaults values:
CONVERSE_VERSION="v11.0.0" CONVERSE_VERSION="v10.1.6"
CONVERSE_REPO="https://github.com/conversejs/converse.js.git" CONVERSE_REPO="https://github.com/conversejs/converse.js.git"
# You can eventually set CONVERSE_COMMIT to a specific commit ID, if you want to apply some patches. # You can eventually set CONVERSE_COMMIT to a specific commit ID, if you want to apply some patches.
# 2024-07-15: using Converse upstream (v11 WIP). CONVERSE_COMMIT=""
CONVERSE_COMMIT="46313ad92c1a861bcb50b9653859cfa9a960ae4a"
# 2024-07-15, FIXME: the following commit includes a quick fix for Converse/#3443, waiting for upstream to be fixed.
CONVERSE_COMMIT="7d65ef8d30a1f3949dbc590b6d27a9d786bf819f"
# It is possible to use another repository, if we want some customization that are not upstream (yet): # 2014-01-16: we are using a custom version, to wait for some PR to be apply upstream.
# CONVERSE_VERSION="livechat" # This version includes following changes:
# # CONVERSE_COMMIT="4402fcc3fc60f6c9334f86528c33a0b463371d12" # - #converse.js/3300: Adding the maxWait option for `debouncedPruneHistory`
# - #converse.js/3302: debounce MUC sidebar rendering
# - Fix: refresh the MUC sidebar when participants collection is sorted
# - Fix: MUC occupant list does not sort itself on nicknames or roles changes
# - Fix inconsistency between browsers on textarea outlines
# - Fix: room information not correctly refreshed when modifications are made by other users
# This version already includes following changes that will not be merged in ConverseJS upstream:
# - Don't load vCards for all room occupants when the right menu is closed
# - Changing the default avatar, for something very light (to mitigate blinking effect when vCards are loaded)
# - Custom settings livechat_load_all_vcards for the readonly mode
# - Adding "users" icon in the menu toggle button
# - Removing unecessary plugins: headless/pubsub, minimize, notifications, profile, omemo, push, roomlist, dragresize.
# - Destroy room: remove the challenge, and the new JID
# - New config option [colorize_username](https://conversejs.org/docs/html/configuration.html#colorize_username)
# - New loadEmojis hook, to customize emojis at runtime.
# - Fix custom emojis path when assets_path is not the default path.
CONVERSE_VERSION="livechat-10.1.0"
# CONVERSE_COMMIT="4402fcc3fc60f6c9334f86528c33a0b463371d12"
CONVERSE_REPO="https://github.com/JohnXLivingston/converse.js" CONVERSE_REPO="https://github.com/JohnXLivingston/converse.js"
# 2024-07-15, fix MUC save.
CONVERSE_COMMIT="58c682b9ba09038beb961e9d8f804c270408ea69"
CONVERSE_COMMIT="bbee0e4e8d2dc43636385cf4cca34f3604f59520"
rootdir="$(pwd)" rootdir="$(pwd)"
src_dir="$rootdir/conversejs" src_dir="$rootdir/conversejs"

View File

@ -8,11 +8,12 @@
* @description This files will override the original ConverseJS index.js file. * @description This files will override the original ConverseJS index.js file.
*/ */
import '@converse/headless'
import './i18n/index.js' import './i18n/index.js'
import 'shared/registry.js' import 'shared/registry.js'
import { CustomElement } from 'shared/components/element' import { CustomElement } from 'shared/components/element'
import { VIEW_PLUGINS } from './shared/constants.js' import { VIEW_PLUGINS } from './shared/constants.js'
import { _converse, converse } from '@converse/headless' import { _converse, converse } from '@converse/headless/core'
import 'shared/styles/index.scss' import 'shared/styles/index.scss'
@ -49,9 +50,6 @@ import '../custom/plugins/terms/index.js'
import '../custom/plugins/poll/index.js' import '../custom/plugins/poll/index.js'
/* END: Removable components */ /* END: Removable components */
// Running some specific livechat patches:
import '../custom/livechat-patch-vcard.js'
import { CORE_PLUGINS } from './headless/shared/constants.js' import { CORE_PLUGINS } from './headless/shared/constants.js'
import { ROOM_FEATURES } from './headless/plugins/muc/constants.js' import { ROOM_FEATURES } from './headless/plugins/muc/constants.js'
// We must add our custom plugins to CORE_PLUGINS (so it is white listed): // We must add our custom plugins to CORE_PLUGINS (so it is white listed):
@ -63,7 +61,7 @@ CORE_PLUGINS.push('livechat-converse-poll')
// (see headless/plugins/muc, getDiscoInfoFeatures, which loops on this const) // (see headless/plugins/muc, getDiscoInfoFeatures, which loops on this const)
ROOM_FEATURES.push('x_peertubelivechat_mute_anonymous') ROOM_FEATURES.push('x_peertubelivechat_mute_anonymous')
_converse.exports.CustomElement = CustomElement _converse.CustomElement = CustomElement
const initialize = converse.initialize const initialize = converse.initialize

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { api } from '@converse/headless/index.js' import { api } from '@converse/headless/core.js'
import { CustomElement } from 'shared/components/element.js' import { CustomElement } from 'shared/components/element.js'
import { tplExternalLoginModal } from 'templates/livechat-external-login-modal.js' import { tplExternalLoginModal } from 'templates/livechat-external-login-modal.js'
import { __ } from 'i18n' import { __ } from 'i18n'

View File

@ -1,61 +0,0 @@
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
//
// SPDX-License-Identifier: AGPL-3.0-only
// Here we are patching the vCard plugin, to add some specific optimizations.
import { _converse, api } from '@converse/headless/index.js'
import {
onOccupantAvatarChanged,
setVCardOnModel,
setVCardOnOccupant
} from '@converse/headless/plugins/vcard/utils.js'
const pluginDefinition = _converse.pluggable.plugins['converse-vcard']
const originalInitialize = pluginDefinition.initialize
pluginDefinition.initialize = function initialize () {
const previousListeners = _converse._events.chatRoomInitialized ?? []
originalInitialize.apply(this)
_converse.api.settings.extend({
livechat_load_all_vcards: false
})
// Now we must detect the new chatRoomInitialized listener, and remove it:
const listenersToRemove = []
for (const def of _converse._events.chatRoomInitialized ?? []) {
if (def.callback && !previousListeners.includes(def.callback)) {
listenersToRemove.push(def.callback)
}
}
for (const callback of listenersToRemove) {
console.debug('Livechat patching vcard: we must remove this listener', callback)
api.listen.not('chatRoomInitialized', callback)
}
// Adding the new listener:
api.listen.on('chatRoomInitialized', (m) => {
console.debug('Patched version of the vcard chatRoomInitialized event.')
setVCardOnModel(m)
// loadAll: when in readonly mode (ie: OBS integration), always load all avatars.
const loadAll = api.settings.get('livechat_load_all_vcards') === true
let hiddenOccupants = m.get('hidden_occupants')
if (hiddenOccupants !== true || loadAll) {
m.occupants.forEach(setVCardOnOccupant)
}
m.listenTo(m.occupants, 'add', (occupant) => {
if (hiddenOccupants !== true || loadAll) {
setVCardOnOccupant(occupant)
}
})
m.on('change:hidden_occupants', () => {
hiddenOccupants = m.get('hidden_occupants')
if (hiddenOccupants !== true || loadAll) {
m.occupants.forEach(setVCardOnOccupant)
}
})
m.listenTo(m.occupants, 'change:image_hash', o => onOccupantAvatarChanged(o))
})
}

View File

@ -4,7 +4,7 @@
import { XMLNS_POLL } from '../constants.js' import { XMLNS_POLL } from '../constants.js'
import { tplPollForm } from '../templates/poll-form.js' import { tplPollForm } from '../templates/poll-form.js'
import { CustomElement } from 'shared/components/element.js' import { CustomElement } from 'shared/components/element.js'
import { converse, api, parsers } from '@converse/headless' import { converse, api } from '@converse/headless/core'
import { webForm2xForm } from '@converse/headless/utils/form' import { webForm2xForm } from '@converse/headless/utils/form'
import { __ } from 'i18n' import { __ } from 'i18n'
import '../styles/poll-form.scss' import '../styles/poll-form.scss'
@ -18,6 +18,7 @@ export default class MUCPollFormView extends CustomElement {
return { return {
model: { type: Object, attribute: true }, model: { type: Object, attribute: true },
modal: { type: Object, attribute: true }, modal: { type: Object, attribute: true },
form_fields: { type: Object, attribute: false },
alert_message: { type: Object, attribute: false }, alert_message: { type: Object, attribute: false },
title: { type: String, attribute: false }, title: { type: String, attribute: false },
instructions: { type: String, attribute: false } instructions: { type: String, attribute: false }
@ -26,8 +27,6 @@ export default class MUCPollFormView extends CustomElement {
_fieldTranslationMap = new Map() _fieldTranslationMap = new Map()
xform = undefined
async initialize () { async initialize () {
this.alert_message = undefined this.alert_message = undefined
if (!this.model) { if (!this.model) {
@ -37,18 +36,20 @@ export default class MUCPollFormView extends CustomElement {
try { try {
this._initFieldTranslations() this._initFieldTranslations()
const stanza = await this._fetchPollForm() const stanza = await this._fetchPollForm()
const xform = parsers.parseXForm(stanza) const query = stanza.querySelector('query')
const xform = sizzle(`x[xmlns="${Strophe.NS.XFORM}"]`, query)[0]
if (!xform) { if (!xform) {
throw Error('Missing xform in stanza') throw Error('Missing xform in stanza')
} }
xform.fields?.map(f => this._translateField(f))
this.xform = xform
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
this.title = __(LOC_poll_title) // xform.querySelector('title')?.textContent ?? '' this.title = __(LOC_poll_title) // xform.querySelector('title')?.textContent ?? ''
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
this.instructions = __(LOC_poll_instructions) // xform.querySelector('instructions')?.textContent ?? '' this.instructions = __(LOC_poll_instructions) // xform.querySelector('instructions')?.textContent ?? ''
this.form_fields = Array.from(xform.querySelectorAll('field')).map(field => {
this._translateField(field)
return u.xForm2TemplateResult(field, stanza)
})
} catch (err) { } catch (err) {
console.error(err) console.error(err)
this.alert_message = __('Error') this.alert_message = __('Error')
@ -85,10 +86,10 @@ export default class MUCPollFormView extends CustomElement {
} }
_translateField (field) { _translateField (field) {
const v = field.var const v = field.getAttribute('var')
const label = this._fieldTranslationMap.get(v) const label = this._fieldTranslationMap.get(v)
if (label) { if (label) {
field.label = label field.setAttribute('label', label)
} }
} }

View File

@ -4,7 +4,7 @@
import { tplPoll } from '../templates/poll.js' import { tplPoll } from '../templates/poll.js'
import { CustomElement } from 'shared/components/element.js' import { CustomElement } from 'shared/components/element.js'
import { converse, _converse, api } from '@converse/headless' import { converse, _converse, api } from '@converse/headless/core'
import '../styles/poll.scss' import '../styles/poll.scss'
export default class MUCPollView extends CustomElement { export default class MUCPollView extends CustomElement {

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { _converse, converse } from '../../../src/headless/index.js' import { _converse, converse } from '../../../src/headless/core.js'
import { getHeadingButtons } from './utils.js' import { getHeadingButtons } from './utils.js'
import { POLL_MESSAGE_TAG, POLL_QUESTION_TAG, POLL_CHOICE_TAG } from './constants.js' import { POLL_MESSAGE_TAG, POLL_QUESTION_TAG, POLL_CHOICE_TAG } from './constants.js'
import { __ } from 'i18n' import { __ } from 'i18n'
@ -12,6 +12,8 @@ import './components/poll-form-view.js'
const { sizzle } = converse.env const { sizzle } = converse.env
const delayedTimeout = 2 // for delayed poll message, how long must the be considered as valid.
converse.plugins.add('livechat-converse-poll', { converse.plugins.add('livechat-converse-poll', {
dependencies: ['converse-muc', 'converse-disco'], dependencies: ['converse-muc', 'converse-disco'],
@ -97,6 +99,23 @@ converse.plugins.add('livechat-converse-poll', {
if (attrs.is_archived) { if (attrs.is_archived) {
return this.__super__.onMessage(attrs) return this.__super__.onMessage(attrs)
} }
if (attrs.is_delayed) {
// When archiving is disabled, the "history" mechanism is still available:
// Last X (20 by default) messages will be kept, and sent to users.
// The only thing that differentiates such messages is that they are delayed.
// We can't just ignore all delayed messages, because if one day we enable SMACKS
// (to handle deconnections on poor network), there could be some legitimate delayed messages.
// So we will only ignore the poll if it was sent more than X minutes ago.
console.debug('Got a delayed poll message, checking if old or not')
const d = new Date()
d.setMinutes(d.getMinutes() - delayedTimeout)
if (attrs.time < d.toISOString()) {
console.debug(
`Poll message was delayed fore more than ${delayedTimeout} minutes (${attrs.time} < ${d.toISOString()}).`
)
return this.__super__.onMessage(attrs)
}
}
console.info('Got a poll message, setting it as the current_poll') console.info('Got a poll message, setting it as the current_poll')
// this will be displayed by the livechat-converse-muc-poll custom element, // this will be displayed by the livechat-converse-muc-poll custom element,

View File

@ -4,7 +4,7 @@
import { __ } from 'i18n' import { __ } from 'i18n'
import BaseModal from 'plugins/modal/modal.js' import BaseModal from 'plugins/modal/modal.js'
import { api } from '@converse/headless' import { api } from '@converse/headless/core'
import { modal_close_button as ModalCloseButton } from 'plugins/modal/templates/buttons.js' import { modal_close_button as ModalCloseButton } from 'plugins/modal/templates/buttons.js'
import { html } from 'lit' import { html } from 'lit'

View File

@ -5,10 +5,6 @@
import { converseLocalizedHelpUrl } from '../../../shared/lib/help' import { converseLocalizedHelpUrl } from '../../../shared/lib/help'
import { html } from 'lit' import { html } from 'lit'
import { __ } from 'i18n' import { __ } from 'i18n'
import { converse } from '@converse/headless'
const u = converse.env.utils
export function tplPollForm (el) { export function tplPollForm (el) {
const i18nOk = __('Ok') const i18nOk = __('Ok')
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
@ -17,18 +13,10 @@ export function tplPollForm (el) {
page: 'documentation/user/streamers/polls' page: 'documentation/user/streamers/polls'
}) })
let formFieldTemplates
if (el.xform) {
const fields = el.xform.fields
formFieldTemplates = fields.map(field => {
return u.xFormField2TemplateResult(field)
})
}
return html` return html`
${el.alert_message ? html`<div class="error">${el.alert_message}</div>` : ''} ${el.alert_message ? html`<div class="error">${el.alert_message}</div>` : ''}
${ ${
formFieldTemplates el.form_fields
? html` ? html`
<form class="converse-form" @submit=${ev => el.formSubmit(ev)}> <form class="converse-form" @submit=${ev => el.formSubmit(ev)}>
<p class="title"> <p class="title">
@ -42,7 +30,7 @@ export function tplPollForm (el) {
<p class="form-help instructions">${el.instructions}</p> <p class="form-help instructions">${el.instructions}</p>
<div class="form-errors hidden"></div> <div class="form-errors hidden"></div>
${formFieldTemplates} ${el.form_fields}
<fieldset class="buttons form-group"> <fieldset class="buttons form-group">
<input type="submit" class="btn btn-primary" value="${i18nOk}" /> <input type="submit" class="btn btn-primary" value="${i18nOk}" />

View File

@ -63,7 +63,7 @@ function _tplChoice (el, currentPoll, choice, canVote) {
<div class="livechat-progress-bar"> <div class="livechat-progress-bar">
<div <div
role="progressbar" role="progressbar"
style=${'width: ' + percent + '%;'} style="width: ${percent}%;"
aria-valuenow="${percent}" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${percent}" aria-valuemin="0" aria-valuemax="100"
></div> ></div>
<p> <p>

View File

@ -3,12 +3,12 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { XMLNS_POLL } from './constants.js' import { XMLNS_POLL } from './constants.js'
import { _converse, api } from '../../../src/headless/index.js' import { _converse, api } from '../../../src/headless/core.js'
import { __ } from 'i18n' import { __ } from 'i18n'
export function getHeadingButtons (view, buttons) { export function getHeadingButtons (view, buttons) {
const muc = view.model const muc = view.model
if (muc.get('type') !== _converse.constants.CHATROOMS_TYPE) { if (muc.get('type') !== _converse.CHATROOMS_TYPE) {
// only on MUC. // only on MUC.
return buttons return buttons
} }

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { _converse, converse, api } from '../../../src/headless/index.js' import { _converse, converse, api } from '../../../src/headless/core.js'
/** /**
* This plugin computes the available width of converse-root, and adds classes * This plugin computes the available width of converse-root, and adds classes

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { api } from '@converse/headless' import { api } from '@converse/headless/core'
import { CustomElement } from 'shared/components/element.js' import { CustomElement } from 'shared/components/element.js'
import { tplMUCTaskApp } from '../templates/muc-task-app.js' import { tplMUCTaskApp } from '../templates/muc-task-app.js'

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { CustomElement } from 'shared/components/element.js' import { CustomElement } from 'shared/components/element.js'
import { api } from '@converse/headless' import { api } from '@converse/headless/core'
import tplMucTaskList from '../templates/muc-task-list' import tplMucTaskList from '../templates/muc-task-list'
import { __ } from 'i18n' import { __ } from 'i18n'

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { CustomElement } from 'shared/components/element.js' import { CustomElement } from 'shared/components/element.js'
import { api } from '@converse/headless' import { api } from '@converse/headless/core'
import tplMucTaskLists from '../templates/muc-task-lists' import tplMucTaskLists from '../templates/muc-task-lists'
import { __ } from 'i18n' import { __ } from 'i18n'

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { CustomElement } from 'shared/components/element.js' import { CustomElement } from 'shared/components/element.js'
import { api } from '@converse/headless' import { api } from '@converse/headless/core'
import { tplMucTask } from '../templates/muc-task' import { tplMucTask } from '../templates/muc-task'
import { __ } from 'i18n' import { __ } from 'i18n'

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { _converse, converse } from '../../../src/headless/index.js' import { _converse, converse } from '../../../src/headless/core.js'
import { ChatRoomTaskLists } from './task-lists.js' import { ChatRoomTaskLists } from './task-lists.js'
import { ChatRoomTaskList } from './task-list.js' import { ChatRoomTaskList } from './task-list.js'
import { ChatRoomTasks } from './tasks.js' import { ChatRoomTasks } from './tasks.js'
@ -18,14 +18,9 @@ converse.plugins.add('livechat-converse-tasks', {
dependencies: ['converse-muc', 'converse-disco', 'converse-pubsub'], dependencies: ['converse-muc', 'converse-disco', 'converse-pubsub'],
initialize () { initialize () {
Object.assign( _converse.ChatRoomTaskLists = ChatRoomTaskLists
_converse.exports, _converse.ChatRoomTaskList = ChatRoomTaskList
{ _converse.ChatRoomTasks = ChatRoomTasks
ChatRoomTaskLists,
ChatRoomTaskList,
ChatRoomTasks
}
)
_converse.api.settings.extend({ _converse.api.settings.extend({
livechat_task_app_enabled: false, livechat_task_app_enabled: false,

View File

@ -4,7 +4,7 @@
import BaseModal from 'plugins/modal/modal.js' import BaseModal from 'plugins/modal/modal.js'
import tplPickTaskList from './templates/pick-task-list.js' import tplPickTaskList from './templates/pick-task-list.js'
import { api } from '@converse/headless' import { api } from '@converse/headless/core'
import { __ } from 'i18n' import { __ } from 'i18n'
export default class PickTaskListModal extends BaseModal { export default class PickTaskListModal extends BaseModal {

View File

@ -7,7 +7,7 @@ import { Model } from '@converse/skeletor/src/model.js'
/** /**
* A chat room task list. * A chat room task list.
* @class * @class
* @namespace _converse.exports.ChatRoomTaskList * @namespace _converse.ChatRoomTaskList
* @memberof _converse * @memberof _converse
*/ */
class ChatRoomTaskList extends Model { class ChatRoomTaskList extends Model {

View File

@ -7,9 +7,9 @@ import { ChatRoomTaskList } from './task-list'
import { initStorage } from '@converse/headless/utils/storage.js' import { initStorage } from '@converse/headless/utils/storage.js'
/** /**
* A list of {@link _converse.exports.ChatRoomTaskList} instances, representing task lists associated to a MUC. * A list of {@link _converse.ChatRoomTaskList} instances, representing task lists associated to a MUC.
* @class * @class
* @namespace _converse.exports.ChatRoomTaskLists * @namespace _converse.ChatRoomTaskLists
* @memberOf _converse * @memberOf _converse
*/ */
class ChatRoomTaskLists extends Collection { class ChatRoomTaskLists extends Collection {

View File

@ -7,7 +7,7 @@ import { Model } from '@converse/skeletor/src/model.js'
/** /**
* A chat room task. * A chat room task.
* @class * @class
* @namespace _converse.exports.ChatRoomTask * @namespace _converse.ChatRoomTask
* @memberof _converse * @memberof _converse
*/ */
class ChatRoomTask extends Model { class ChatRoomTask extends Model {

View File

@ -7,9 +7,9 @@ import { ChatRoomTask } from './task'
import { initStorage } from '@converse/headless/utils/storage.js' import { initStorage } from '@converse/headless/utils/storage.js'
/** /**
* A list of {@link _converse.exports.ChatRoomTask} instances, representing all tasks associated to a MUC. * A list of {@link _converse.ChatRoomTask} instances, representing all tasks associated to a MUC.
* @class * @class
* @namespace _converse.exports.ChatRoomTasks * @namespace _converse.ChatRoomTasks
* @memberOf _converse * @memberOf _converse
*/ */
class ChatRoomTasks extends Collection { class ChatRoomTasks extends Collection {

View File

@ -4,12 +4,12 @@
import { XMLNS_TASKLIST, XMLNS_TASK } from './constants.js' import { XMLNS_TASKLIST, XMLNS_TASK } from './constants.js'
import { PubSubManager } from '../../shared/lib/pubsub-manager.js' import { PubSubManager } from '../../shared/lib/pubsub-manager.js'
import { converse, _converse, api } from '../../../src/headless/index.js' import { converse, _converse, api } from '../../../src/headless/core.js'
import { __ } from 'i18n' import { __ } from 'i18n'
export function getHeadingButtons (view, buttons) { export function getHeadingButtons (view, buttons) {
const muc = view.model const muc = view.model
if (muc.get('type') !== _converse.constants.CHATROOMS_TYPE) { if (muc.get('type') !== _converse.CHATROOMS_TYPE) {
// only on MUC. // only on MUC.
return buttons return buttons
} }
@ -74,8 +74,8 @@ function _initChatRoomTaskLists (mucModel) {
return return
} }
mucModel.tasklists = new _converse.exports.ChatRoomTaskLists(undefined, { chatroom: mucModel }) mucModel.tasklists = new _converse.ChatRoomTaskLists(undefined, { chatroom: mucModel })
mucModel.tasks = new _converse.exports.ChatRoomTasks(undefined, { chatroom: mucModel }) mucModel.tasks = new _converse.ChatRoomTasks(undefined, { chatroom: mucModel })
mucModel.taskManager = new PubSubManager( mucModel.taskManager = new PubSubManager(
mucModel.get('jid'), mucModel.get('jid'),
@ -127,7 +127,7 @@ function _destroyChatRoomTaskLists (mucModel) {
} }
export function initOrDestroyChatRoomTaskLists (mucModel) { export function initOrDestroyChatRoomTaskLists (mucModel) {
if (mucModel.get('type') !== _converse.constants.CHATROOMS_TYPE) { if (mucModel.get('type') !== _converse.CHATROOMS_TYPE) {
// only on MUC. // only on MUC.
return _destroyChatRoomTaskLists(mucModel) return _destroyChatRoomTaskLists(mucModel)
} }

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { CustomElement } from 'shared/components/element.js' import { CustomElement } from 'shared/components/element.js'
import { api } from '@converse/headless' import { api } from '@converse/headless/core'
import { html } from 'lit' import { html } from 'lit'
import { __ } from 'i18n' import { __ } from 'i18n'

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { converse, api } from '../../../src/headless/index.js' import { converse, api } from '../../../src/headless/core.js'
import './components/muc-terms.js' import './components/muc-terms.js'
const { sizzle } = converse.env const { sizzle } = converse.env

View File

@ -4,7 +4,7 @@
/* eslint-disable max-len */ /* eslint-disable max-len */
import { html } from 'lit' import { html } from 'lit'
import tplIcons from '../../../src/shared/components/templates/icons.js' import tplIcons from '../../../src/shared/templates/icons.js'
export default () => { export default () => {
// Here we are adding some additonal icons to ConverseJS defaults // Here we are adding some additonal icons to ConverseJS defaults

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { converse, _converse, api } from '../../../src/headless/index.js' import { converse, _converse, api } from '../../../src/headless/core.js'
const { $build, Strophe, $iq, sizzle } = converse.env const { $build, Strophe, $iq, sizzle } = converse.env
/** /**
@ -50,7 +50,7 @@ export class PubSubManager {
async start () { async start () {
// FIXME: handle errors. Find a way to display to user that this failed. // FIXME: handle errors. Find a way to display to user that this failed.
this.stanzaHandler = api.connection.get().addHandler( this.stanzaHandler = _converse.connection.addHandler(
(message) => { (message) => {
try { try {
this._handleMessage(message) this._handleMessage(message)
@ -79,7 +79,7 @@ export class PubSubManager {
// Note: no need to unsubscribe from the pubsub node, the backend will do when users leave the room. // Note: no need to unsubscribe from the pubsub node, the backend will do when users leave the room.
if (this.stanzaHandler) { if (this.stanzaHandler) {
api.connection.get().deleteHandler(this.stanzaHandler) _converse.connection.deleteHandler(this.stanzaHandler)
this.stanzaHandler = undefined this.stanzaHandler = undefined
} }
} }

View File

@ -4,7 +4,7 @@
import { __ } from 'i18n' import { __ } from 'i18n'
import BaseModal from 'plugins/modal/modal.js' import BaseModal from 'plugins/modal/modal.js'
import { api } from '@converse/headless' import { api } from '@converse/headless/core'
import { html } from 'lit' import { html } from 'lit'
import 'livechat-external-login-content.js' import 'livechat-external-login-content.js'

View File

@ -52,16 +52,15 @@
.emoji-picker__header { .emoji-picker__header {
color: var(--peertube-main-background); color: var(--peertube-main-background);
background-color: var(--peertube-main-background); background-color: var(--peertube-main-foreground);
ul { ul {
.emoji-category { .emoji-category {
color: var(--peertube-main-background); color: var(--peertube-main-background);
background-color: #2d2d2d; background-color: var(--peertube-main-foreground);
border-bottom: 1px solid var(--peertube-main-foreground);
a { a {
color: white; color: currentcolor;
} }
&.picked { &.picked {
@ -135,19 +134,17 @@
// Changing size for emojis, to have bigger custom emojis // Changing size for emojis, to have bigger custom emojis
img.emoji { img.emoji {
width: 3.5rem !important; width: unset !important;
height: 3.5rem !important; height: unset !important;
// max-height: 4.25em !important; // and no max-width max-height: 3em !important; // and no max-width
} }
// underline links in chat messages // underline links in chat messages
a[href] { a[href] {
text-decoration: none !important; text-decoration: underline;
color: #f57200 !important;
&:hover { &:hover {
text-decoration: underline !important; text-decoration: underline;
color: #D16100 !important;
} }
} }
} }
@ -191,12 +188,6 @@
} }
} }
/* NCTV custom */
.emoji-category__heading {
color: white !important;
}
// Bigger occupants sidebar when width is not big enough. // Bigger occupants sidebar when width is not big enough.
@media screen and (max-width: 576px) { @media screen and (max-width: 576px) {
.chatroom .box-flyout .chatroom-body .occupants { .chatroom .box-flyout .chatroom-body .occupants {

View File

@ -64,8 +64,8 @@ body.converse-embedded converse-root.theme-peertube {
--message-avatar-height: 36px; --message-avatar-height: 36px;
--chat-background-color: var(--peertube-main-background); --chat-background-color: var(--peertube-main-background);
--chat-textarea-color: var(--peertube-input-foreground); --chat-textarea-color: var(--peertube-input-foreground);
--chat-textarea-background-color: var(--peertube-main-background); --chat-textarea-background-color: var(--peertube-input-background);
--chat-textarea-height: 38px; --chat-textarea-height: 60px;
--send-button-height: 27px; --send-button-height: 27px;
--send-button-margin: 3px; --send-button-margin: 3px;
--inline-action-margin: 0.75em; --inline-action-margin: 0.75em;
@ -140,8 +140,8 @@ body.converse-embedded converse-root.theme-peertube {
--fullpage-chatbox-button-size: 24px; --fullpage-chatbox-button-size: 24px;
--font-size-tiny: 10px; --font-size-tiny: 10px;
--font-size-small: 12px; --font-size-small: 12px;
--font-size: 16px; --font-size: 14px;
--font-size-large: 18px; --font-size-large: 16px;
--font-size-huge: 20px; --font-size-huge: 20px;
--message-font-size: var(--font-size); --message-font-size: var(--font-size);
--separator-text-color: var(--peertube-grey-foreground); --separator-text-color: var(--peertube-grey-foreground);

View File

@ -171,9 +171,7 @@ body.converse-embedded {
#peertube-plugin-livechat-container { #peertube-plugin-livechat-container {
converse-muc-message-form { converse-muc-message-form {
// For an unknown reason, message field in truncated... so adding a bottom margin. // For an unknown reason, message field in truncated... so adding a bottom margin.
max-height: unset !important; margin-bottom: 6px;
margin-bottom: 0.5rem !important;
margin-top: 0.4rem !important;
} }
} }

View File

@ -5,7 +5,7 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { html } from 'lit' import { html } from 'lit'
import { api } from '@converse/headless/index.js' import { api } from '@converse/headless/core.js'
export default () => html` export default () => html`
<div class="inner-content converse-brand row"> <div class="inner-content converse-brand row">

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { _converse, api } from '@converse/headless' import { _converse, api } from '@converse/headless/core'
import { __ } from 'i18n' import { __ } from 'i18n'
import { html } from 'lit' import { html } from 'lit'

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { __ } from 'i18n' import { __ } from 'i18n'
import { _converse, api } from '@converse/headless' import { _converse, api } from '@converse/headless/core'
import { html } from 'lit' import { html } from 'lit'
import tplMucBottomPanel from '../../src/plugins/muc-views/templates/muc-bottom-panel.js' import tplMucBottomPanel from '../../src/plugins/muc-views/templates/muc-bottom-panel.js'
import { CustomElement } from 'shared/components/element.js' import { CustomElement } from 'shared/components/element.js'
@ -79,7 +79,7 @@ class SlowMode extends CustomElement {
api.elements.define('livechat-slow-mode', SlowMode) api.elements.define('livechat-slow-mode', SlowMode)
const tplSlowMode = (o) => { const tplSlowMode = (o) => {
if (!o.can_post) { return html`` } if (!o.can_edit) { return html`` }
return html`<livechat-slow-mode jid=${o.model.get('jid')}>` return html`<livechat-slow-mode jid=${o.model.get('jid')}>`
} }
@ -128,9 +128,17 @@ const tplViewerMode = (o) => {
} }
export default (o) => { export default (o) => {
// ConverseJS 10.x does not handle properly the visitor role in unmoderated rooms.
// See https://github.com/conversejs/converse.js/issues/3428 for more info.
// So we will do a dirty hack here to fix this case.
// Note: ConverseJS 11.x has changed the code, and could be fixed more cleanly (or will be fixed if #3428 is fixed).
if (o.can_edit && o.model.getOwnRole() === 'visitor') {
o.can_edit = false
}
let mutedAnonymousMessage let mutedAnonymousMessage
if ( if (
!o.can_post && !o.can_edit &&
o.model.features?.get?.('x_peertubelivechat_mute_anonymous') && o.model.features?.get?.('x_peertubelivechat_mute_anonymous') &&
_converse.api.settings.get('livechat_specific_is_anonymous') === true _converse.api.settings.get('livechat_specific_is_anonymous') === true
) { ) {

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { api } from '@converse/headless' import { api } from '@converse/headless/core'
import tplMUCChatarea from '../../src/plugins/muc-views/templates/muc-chatarea.js' import tplMUCChatarea from '../../src/plugins/muc-views/templates/muc-chatarea.js'
import { html } from 'lit' import { html } from 'lit'

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { html } from 'lit' import { html } from 'lit'
import { api } from '@converse/headless' import { api } from '@converse/headless/core'
import { until } from 'lit/directives/until.js' import { until } from 'lit/directives/until.js'
import { repeat } from 'lit/directives/repeat.js' import { repeat } from 'lit/directives/repeat.js'
import { unsafeHTML } from 'lit/directives/unsafe-html.js' import { unsafeHTML } from 'lit/directives/unsafe-html.js'

View File

@ -44,7 +44,7 @@ module.exports = merge(prod, {
'../../templates/background_logo.js$': path.resolve(__dirname, 'custom/templates/background_logo.js'), '../../templates/background_logo.js$': path.resolve(__dirname, 'custom/templates/background_logo.js'),
'./templates/muc-chatarea.js': path.resolve('custom/templates/muc-chatarea.js'), './templates/muc-chatarea.js': path.resolve('custom/templates/muc-chatarea.js'),
'./templates/icons.js': path.resolve(__dirname, 'custom/shared/components/font-awesome.js'), '../templates/icons.js': path.resolve(__dirname, 'custom/shared/components/font-awesome.js'),
'shared/styles/index.scss$': path.resolve(__dirname, 'custom/shared/styles/livechat.scss'), 'shared/styles/index.scss$': path.resolve(__dirname, 'custom/shared/styles/livechat.scss'),

View File

@ -97,7 +97,6 @@ function defaultConverseParams (
pruning_behavior: 'unscrolled', pruning_behavior: 'unscrolled',
colorize_username: true, colorize_username: true,
send_chat_markers: [], send_chat_markers: [],
reuse_scram_keys: false, // for now we don't use this.
// This is a specific settings, that is used in ConverseJS customization, to force avatars loading in readonly mode. // This is a specific settings, that is used in ConverseJS customization, to force avatars loading in readonly mode.
livechat_load_all_vcards: !!forceReadonly, livechat_load_all_vcards: !!forceReadonly,

View File

@ -16,7 +16,7 @@ export const livechatMiniMucHeadPlugin = {
}) })
_converse.api.listen.on('getHeadingButtons', (view: any, buttons: any[]) => { _converse.api.listen.on('getHeadingButtons', (view: any, buttons: any[]) => {
if (view.model.get('type') !== _converse.constants.CHATROOMS_TYPE) { if (view.model.get('type') !== _converse.CHATROOMS_TYPE) {
// only on MUC. // only on MUC.
return buttons return buttons
} }

View File

@ -15,7 +15,7 @@ export const livechatSpecificsPlugin = {
}) })
_converse.api.listen.on('getHeadingButtons', (view: any, buttons: any[]) => { _converse.api.listen.on('getHeadingButtons', (view: any, buttons: any[]) => {
if (view.model.get('type') !== _converse.constants.CHATROOMS_TYPE) { if (view.model.get('type') !== _converse.CHATROOMS_TYPE) {
// only on MUC. // only on MUC.
return buttons return buttons
} }
@ -53,71 +53,6 @@ export const livechatSpecificsPlugin = {
return buttons return buttons
}) })
_converse.api.listen.on('getToolbarButtons', (toolbarEl: any, buttons: any[]) => {
// We will replace the toggle occupant button, to change its appearance.
// First, we must find it. We search from the end, because usually it is the last one.
let toggleOccupantButton: any
for (const button of buttons.reverse()) {
if (button.strings?.find((s: string) => s.includes('toggle_occupants'))) { // searching the classname
console.debug('[livechatSpecificsPlugin] found the toggle occupants button', button)
toggleOccupantButton = button
break
}
}
if (!toggleOccupantButton) {
console.debug('[livechatSpecificsPlugin] Did not found the toggle occupants button')
return buttons
}
buttons = buttons.filter(b => b !== toggleOccupantButton)
// Replacing by the new button...
// Note: we don't need to test conditions, we know the button was here.
const i18nHideOccupants = _converse.__('Hide participants')
const i18nShowOccupants = _converse.__('Show participants')
const html = window.converse.env.html
const icon = toolbarEl.hidden_occupants
? html`<converse-icon
color="var(--muc-toolbar-btn-color)"
class="fa fa-angle-double-left"
size="1em">
</converse-icon>
<converse-icon
color="var(--muc-toolbar-btn-color)"
class="fa users"
size="1em">
</converse-icon>`
: html`<converse-icon
color="var(--muc-toolbar-btn-color)"
class="fa users"
size="1em">
</converse-icon>
<converse-icon
color="var(--muc-toolbar-btn-color)"
class="fa fa-angle-double-right"
size="1em">
</converse-icon>`
buttons.push(html`
<button class="toggle_occupants right"
title="${toolbarEl.hidden_occupants ? i18nShowOccupants : i18nHideOccupants}"
@click=${toolbarEl.toggleOccupants}>
${icon}
</button>`
)
return buttons
})
// Overriding the MUCHeading custom element, to customize the destroyMUC function:
const MUCHeading = _converse.api.elements.registry['converse-muc-heading']
if (MUCHeading) {
class MUCHeadingOverloaded extends MUCHeading {
async destroy (ev: Event): Promise<void> {
ev.preventDefault()
await destroyMUC(_converse, this.model) // here we call a custom version of destroyMUC
}
}
_converse.api.elements.define('converse-muc-heading', MUCHeadingOverloaded)
}
_converse.api.listen.on('chatRoomViewInitialized', function (this: any, _model: any): void { _converse.api.listen.on('chatRoomViewInitialized', function (this: any, _model: any): void {
// Remove the spinner if present... // Remove the spinner if present...
document.getElementById('livechat-loading-spinner')?.remove() document.getElementById('livechat-loading-spinner')?.remove()
@ -182,7 +117,7 @@ export const livechatSpecificsPlugin = {
}, },
overrides: { overrides: {
ChatRoom: { ChatRoom: {
getActionInfoMessage: function getActionInfoMessage (this: any, code: string, nick: string, actor: any): any { getActionInfoMessage: function (this: any, code: string, nick: string, actor: any): any {
if (code === '303') { if (code === '303') {
// When there is numerous anonymous users joining at the same time, // When there is numerous anonymous users joining at the same time,
// they can all change their nicknames at the same time, generating a log of action messages. // they can all change their nicknames at the same time, generating a log of action messages.
@ -195,12 +130,6 @@ export const livechatSpecificsPlugin = {
} }
} }
return this.__super__.getActionInfoMessage(code, nick, actor) return this.__super__.getActionInfoMessage(code, nick, actor)
},
canPostMessages: function canPostMessages (this: any) {
// ConverseJS does not handle properly the visitor role in unmoderated rooms.
// See https://github.com/conversejs/converse.js/issues/3428 for more info.
// FIXME: if #3428 is fixed, remove this override.
return this.isEntered() && this.getOwnRole() !== 'visitor'
} }
}, },
ChatRoomMessage: { ChatRoomMessage: {
@ -254,26 +183,3 @@ function getOpenPromise (): any {
) )
return promise return promise
} }
async function destroyMUC (_converse: any, model: any): Promise<void> {
const __ = _converse.__
const messages = [__('Are you sure you want to destroy this groupchat?')]
// Note: challenge and newjid make no sens for peertube-plugin-livechat,
// we remove them comparing to the original function.
let fields = [
{
name: 'reason',
label: __('Optional reason for destroying this groupchat'),
placeholder: __('Reason'),
value: undefined
}
]
try {
fields = await _converse.api.confirm(__('Confirm'), messages, fields)
const reason = fields.filter(f => f.name === 'reason').pop()?.value
const newjid = undefined
return model.sendDestroyIQ(reason, newjid).then(() => model.close())
} catch (e) {
console.error(e)
}
}

View File

@ -17,11 +17,11 @@ export const livechatViewerModePlugin = {
livechat_external_auth_reconnect_mode: undefined livechat_external_auth_reconnect_mode: undefined
}) })
const originalGetDefaultMUCNickname = _converse.exports.getDefaultMUCNickname const originalGetDefaultMUCNickname = _converse.getDefaultMUCNickname
if (!originalGetDefaultMUCNickname) { if (!originalGetDefaultMUCNickname) {
console.error('[livechatViewerModePlugin] getDefaultMUCNickname is not initialized.') console.error('[livechatViewerModePlugin] getDefaultMUCNickname is not initialized.')
} else { } else {
Object.assign(_converse.exports, { Object.assign(_converse, {
getDefaultMUCNickname: function (this: any): any { getDefaultMUCNickname: function (this: any): any {
if (!_converse.api.settings.get('livechat_enable_viewer_mode')) { if (!_converse.api.settings.get('livechat_enable_viewer_mode')) {
return originalGetDefaultMUCNickname.apply(this, arguments) return originalGetDefaultMUCNickname.apply(this, arguments)

View File

@ -175,6 +175,7 @@ avatar_set_description: |
Please refer to the documentation: Please refer to the documentation:
<a href="https://livingston.frama.io/peertube-plugin-livechat/documentation/admin/settings/" target="_blank">Settings</a>. <a href="https://livingston.frama.io/peertube-plugin-livechat/documentation/admin/settings/" target="_blank">Settings</a>.
avatar_set_option_sepia: "Sepia (Peertube mascot)" avatar_set_option_sepia: "Sepia (Peertube mascot)"
avatar_set_option_nctv: "NCTV Branded"
avatar_set_option_cat: "Cats" avatar_set_option_cat: "Cats"
avatar_set_option_bird: "Birds" avatar_set_option_bird: "Birds"
avatar_set_option_fenec: "Fenecs (Mobilizon mascot)" avatar_set_option_fenec: "Fenecs (Mobilizon mascot)"

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "peertube-plugin-livechat", "name": "peertube-plugin-livechat",
"version": "10.3.1", "version": "10.3.3",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "peertube-plugin-livechat", "name": "peertube-plugin-livechat",
"version": "10.3.1", "version": "10.3.3",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@xmpp/jid": "^0.13.1", "@xmpp/jid": "^0.13.1",

View File

@ -1,13 +1,13 @@
{ {
"name": "peertube-plugin-livechat", "name": "peertube-plugin-livechat",
"description": "NCTV fork of the peertube-plugin-livechat plugin, containing styling and other shit. This will be maintained with upstream.", "description": "PeerTube plugin livechat: create chat rooms for your Peertube lives! Comes with many features: federation, moderation tools, chat bot, chat persistence, OBS integration, ...",
"version": "10.3.1", "version": "10.3.3",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"author": { "author": {
"name": "Matty Boombalatty", "name": "John Livingston",
"url": "https://gitea.nicecrew.digital/matty" "url": "https://github.com/JohnXLivingston"
}, },
"bugs": "https://gitea.nicecrew.digital/matty/peertube-plugin-livechat/issues", "bugs": "https://github.com/JohnXLivingston/peertube-plugin-livechat/issues",
"clientScripts": [ "clientScripts": [
{ {
"script": "dist/client/common-client-plugin.js", "script": "dist/client/common-client-plugin.js",
@ -82,8 +82,8 @@
"engines": { "engines": {
"npm": ">=7" "npm": ">=7"
}, },
"homepage": "https://gitea.nicecrew.digital/matty/peertube-plugin-livechat", "homepage": "https://livingston.frama.io/peertube-plugin-livechat/",
"repository": "https://gitea.nicecrew.digital/matty/peertube-plugin-livechat", "repository": "github:JohnXLivingston/peertube-plugin-livechat",
"keywords": [ "keywords": [
"peertube", "peertube",
"plugin" "plugin"

View File

@ -102,7 +102,7 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise<Pro
} }
let avatarSet: AvatarSet = (settings['avatar-set'] ?? 'sepia') as AvatarSet let avatarSet: AvatarSet = (settings['avatar-set'] ?? 'sepia') as AvatarSet
if (!['sepia', 'cat', 'bird', 'fenec', 'abstract', 'legacy'].includes(avatarSet)) { if (!['sepia', 'cat', 'bird', 'fenec', 'abstract', 'legacy', 'nctv'].includes(avatarSet)) {
logger.error('Invalid avatar-set setting, using sepia as default') logger.error('Invalid avatar-set setting, using sepia as default')
avatarSet = 'sepia' avatarSet = 'sepia'
} }

View File

@ -93,7 +93,7 @@ abstract class ProsodyConfigBlock {
this.entries.set(name, value) this.entries.set(name, value)
} }
add (name: string, value: ConfigEntryValue): void { add (name: string, value: ConfigEntryValue, allowDuplicate?: boolean): void {
if (!this.entries.has(name)) { if (!this.entries.has(name)) {
this.entries.set(name, []) this.entries.set(name, [])
} }
@ -101,6 +101,9 @@ abstract class ProsodyConfigBlock {
if (!Array.isArray(entry)) { if (!Array.isArray(entry)) {
entry = [entry] entry = [entry]
} }
if (!allowDuplicate && entry.includes(value)) {
return
}
entry.push(value) entry.push(value)
this.entries.set(name, entry) this.entries.set(name, entry)
} }
@ -239,7 +242,7 @@ class ProsodyConfigContent {
this.muc.set('muc_room_default_history_length', 20) this.muc.set('muc_room_default_history_length', 20)
this.muc.add('modules_enabled', 'muc_slow_mode') this.muc.add('modules_enabled', 'muc_slow_mode')
this.muc.add('slow_mode_duration_form_position', 120) this.muc.set('slow_mode_duration_form_position', 120)
this.muc.add('modules_enabled', 'pubsub_peertubelivechat') this.muc.add('modules_enabled', 'pubsub_peertubelivechat')
this.muc.add('modules_enabled', 'muc_peertubelivechat_roles') this.muc.add('modules_enabled', 'muc_peertubelivechat_roles')
@ -251,7 +254,7 @@ class ProsodyConfigContent {
} }
this.muc.add('modules_enabled', 'muc_moderation_delay') this.muc.add('modules_enabled', 'muc_moderation_delay')
this.muc.add('moderation_delay_form_position', 118) this.muc.set('moderation_delay_form_position', 118)
} }
useAnonymous (autoBanIP: boolean): void { useAnonymous (autoBanIP: boolean): void {
@ -445,7 +448,7 @@ class ProsodyConfigContent {
useMucHttpDefault (url: string): void { useMucHttpDefault (url: string): void {
this.muc.add('modules_enabled', 'muc_http_defaults') this.muc.add('modules_enabled', 'muc_http_defaults')
this.muc.add('muc_create_api_url', url) this.muc.set('muc_create_api_url', url)
// restrict_room_creation: we can override the 'local' value. // restrict_room_creation: we can override the 'local' value.
// Indeed, when muc_http_default is used, room creation will be managed by api. // Indeed, when muc_http_default is used, room creation will be managed by api.
@ -517,7 +520,7 @@ class ProsodyConfigContent {
*/ */
useBotsVirtualHost (botAvatarPath: string, botAvatarFiles: string[]): void { useBotsVirtualHost (botAvatarPath: string, botAvatarFiles: string[]): void {
this.bot = new ProsodyConfigVirtualHost('bot.' + this.prosodyDomain) this.bot = new ProsodyConfigVirtualHost('bot.' + this.prosodyDomain)
this.bot.set('modules_enabled', ['ping']) this.bot.set('modules_enabled', ['ping', 'tls'])
this.bot.set('authentication', 'peertubelivechat_bot') this.bot.set('authentication', 'peertubelivechat_bot')
// For now, just using random_vcard_peertubelivechat to set bot avatar // For now, just using random_vcard_peertubelivechat to set bot avatar

View File

@ -13,7 +13,7 @@ import { LivechatProsodyAuth } from './prosody/auth'
import { loc } from './loc' import { loc } from './loc'
const escapeHTML = require('escape-html') const escapeHTML = require('escape-html')
type AvatarSet = 'sepia' | 'cat' | 'bird' | 'fenec' | 'abstract' | 'legacy' type AvatarSet = 'sepia' | 'cat' | 'bird' | 'fenec' | 'abstract' | 'legacy' | 'nctv'
async function initSettings (options: RegisterServerOptions): Promise<void> { async function initSettings (options: RegisterServerOptions): Promise<void> {
const { peertubeHelpers, settingsManager } = options const { peertubeHelpers, settingsManager } = options
@ -506,6 +506,7 @@ function initThemingSettings ({ registerSetting }: RegisterServerOptions): void
private: true, private: true,
options: [ options: [
{ value: 'sepia', label: loc('avatar_set_option_sepia') }, { value: 'sepia', label: loc('avatar_set_option_sepia') },
{ value: 'nctv', label: loc('avatar_set_option_nctv') },
{ value: 'cat', label: loc('avatar_set_option_cat') }, { value: 'cat', label: loc('avatar_set_option_cat') },
{ value: 'bird', label: loc('avatar_set_option_bird') }, { value: 'bird', label: loc('avatar_set_option_bird') },
{ value: 'fenec', label: loc('avatar_set_option_fenec') }, { value: 'fenec', label: loc('avatar_set_option_fenec') },

View File

@ -5,7 +5,7 @@
// Note: API request body size is limited to 100Kb (expressjs body-parser defaut limit, and Peertube nginx config). // Note: API request body size is limited to 100Kb (expressjs body-parser defaut limit, and Peertube nginx config).
// So we must be sure to never send more than 100Kb. The front end sends new emojis by batch, but maxSize must remain // So we must be sure to never send more than 100Kb. The front end sends new emojis by batch, but maxSize must remain
// as little as possible, so that we never reach 100Kb in JSON/base64 format. // as little as possible, so that we never reach 100Kb in JSON/base64 format.
export const maxSize: number = 30 * 1024 export const maxSize: number = 800 * 1024
export const allowedExtensions = ['png', 'jpg', 'jpeg', 'gif'] export const allowedExtensions = ['png', 'jpg', 'jpeg', 'gif']
export const inputFileAccept = ['image/jpg', 'image/png', 'image/gif'] export const inputFileAccept = ['image/jpg', 'image/png', 'image/gif']
export const allowedMimeTypes = ['image/jpg', 'image/png', 'image/gif'] export const allowedMimeTypes = ['image/jpg', 'image/png', 'image/gif']

View File

@ -28,6 +28,7 @@ These files are used to generate multiple avatars (see the `build-avatars.js` sc
* `assets/images/avatars/bird` contains new birds avatar set, based on the work of David Revoy (see copyright informations) * `assets/images/avatars/bird` contains new birds avatar set, based on the work of David Revoy (see copyright informations)
* `assets/images/avatars/fenec` contains new fenecs avatar set, based on the work of David Revoy (see copyright informations) * `assets/images/avatars/fenec` contains new fenecs avatar set, based on the work of David Revoy (see copyright informations)
* `assets/images/avatars/abstract` contains new abstract avatar set, based on the work of David Revoy (see copyright informations) * `assets/images/avatars/abstract` contains new abstract avatar set, based on the work of David Revoy (see copyright informations)
* `assets/images/avatars/nctv` contains NCTV avatar, based on the work of The Almighty Kek
### assets/styles ### assets/styles

View File

@ -8,11 +8,9 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-07-10 16:54+0200\n" "POT-Creation-Date: 2024-07-10 16:54+0200\n"
"PO-Revision-Date: 2024-07-12 09:10+0000\n" "PO-Revision-Date: 2024-07-05 19:12+0000\n"
"Last-Translator: Victor Hampel <v.hampel@users.noreply.weblate.framasoft.org>" "Last-Translator: Victor Hampel <v.hampel@users.noreply.weblate.framasoft.org>\n"
"\n" "Language-Team: German <https://weblate.framasoft.org/projects/peertube-livechat/peertube-plugin-livechat-documentation/de/>\n"
"Language-Team: German <https://weblate.framasoft.org/projects/"
"peertube-livechat/peertube-plugin-livechat-documentation/de/>\n"
"Language: de\n" "Language: de\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -3274,30 +3272,25 @@ msgstr "Um den Wert für einen bereits bestehenden Raum zu ändern, öffnen Sie
#: support/documentation/content/en/documentation/user/streamers/moderation_delay.md #: support/documentation/content/en/documentation/user/streamers/moderation_delay.md
msgid "Currently, this feature has one known bug: users that join the chat will get all messages, even messages that are still pending for other participants. However, messages sent after they joined will be delayed correctly." msgid "Currently, this feature has one known bug: users that join the chat will get all messages, even messages that are still pending for other participants. However, messages sent after they joined will be delayed correctly."
msgstr "" msgstr ""
"Derzeit gibt es bei dieser Funktion einen bekannten Fehler: Benutzer, die "
"dem Chat beitreten, erhalten alle Nachrichten, auch solche, die noch für "
"andere Teilnehmer ausstehen. Allerdings werden Nachrichten, die nach dem "
"Beitritt gesendet werden, korrekt verzögert."
#. type: Title ## #. type: Title ##
#: support/documentation/content/en/documentation/user/streamers/moderation_delay.md #: support/documentation/content/en/documentation/user/streamers/moderation_delay.md
#, no-wrap #, fuzzy, no-wrap
#| msgid "Share the chat"
msgid "In the chat" msgid "In the chat"
msgstr "Im Chat" msgstr "Teilen Sie den Chat"
#. type: Plain text #. type: Plain text
#: support/documentation/content/en/documentation/user/streamers/moderation_delay.md #: support/documentation/content/en/documentation/user/streamers/moderation_delay.md
msgid "As a moderator, you will see the remaining time (in seconds) before the message is broadcasted, just besides the message datetime." msgid "As a moderator, you will see the remaining time (in seconds) before the message is broadcasted, just besides the message datetime."
msgstr "" msgstr ""
"Als Moderator sehen Sie neben dem Datum der Nachricht auch die verbleibende "
"Zeit (in Sekunden), bevor die Nachricht veröffentlicht wird."
#. type: Plain text #. type: Plain text
#: support/documentation/content/en/documentation/user/streamers/moderation_delay.md #: support/documentation/content/en/documentation/user/streamers/moderation_delay.md
#, fuzzy
#| msgid "![Channel configuration / Moderation delay](/peertube-plugin-livechat/images/moderation_delay_channel_option.png?classes=shadow,border&height=400px)"
msgid "![Moderation delay timer](/peertube-plugin-livechat/images/moderation_delay_timer.png?classes=shadow,border)" msgid "![Moderation delay timer](/peertube-plugin-livechat/images/moderation_delay_timer.png?classes=shadow,border)"
msgstr "" msgstr "![Kanalkonfiguration / Moderationsverzögerung](/peertube-plugin-livechat/images/moderation_delay_channel_option.png?classes=shadow,border&height=400px)"
"![Moderationsverzögerungstimer](/peertube-plugin-livechat/images/"
"moderation_delay_timer.png?classes=shadow,border)"
#. type: Yaml Front Matter Hash Value: description #. type: Yaml Front Matter Hash Value: description
#: build/documentation/pot_in/documentation/user/streamers/moderation.md #: build/documentation/pot_in/documentation/user/streamers/moderation.md
@ -3344,11 +3337,10 @@ msgstr "Über das [Chat Dropdown Menü](/peertube-plugin-livechat/de/documentati
#. type: Plain text #. type: Plain text
#: build/documentation/pot_in/documentation/user/streamers/moderation.md #: build/documentation/pot_in/documentation/user/streamers/moderation.md
#, fuzzy
#| msgid "The video owner will be owner of the chat room. This means he can configure the room, delete it, promote other users as admins, ..."
msgid "The video owner will be owner of the chat room. This means they can configure the room, delete it, promote other users as admins, ..." msgid "The video owner will be owner of the chat room. This means they can configure the room, delete it, promote other users as admins, ..."
msgstr "" msgstr "Der Videobesitzer ist der Besitzer des Chatraums. Das bedeutet, er kann den Raum konfigurieren, löschen, andere Benutzer als Administratoren befördern, ..."
"Der Videobesitzer ist der Besitzer des Chatraums. Das bedeutet, er kann den "
"Raum konfigurieren, löschen, andere Benutzer als Administratoren befördern, "
"..."
#. type: Plain text #. type: Plain text
#: build/documentation/pot_in/documentation/user/streamers/moderation.md #: build/documentation/pot_in/documentation/user/streamers/moderation.md