This commit is contained in:
2024-07-16 15:02:04 -04:00
43 changed files with 291 additions and 110 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
import { _converse, converse } from '../../../src/headless/core.js'
import { _converse, converse } from '../../../src/headless/index.js'
import { getHeadingButtons } from './utils.js'
import { POLL_MESSAGE_TAG, POLL_QUESTION_TAG, POLL_CHOICE_TAG } from './constants.js'
import { __ } from 'i18n'

View File

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

View File

@ -5,6 +5,10 @@
import { converseLocalizedHelpUrl } from '../../../shared/lib/help'
import { html } from 'lit'
import { __ } from 'i18n'
import { converse } from '@converse/headless'
const u = converse.env.utils
export function tplPollForm (el) {
const i18nOk = __('Ok')
// eslint-disable-next-line no-undef
@ -13,10 +17,18 @@ export function tplPollForm (el) {
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`
${el.alert_message ? html`<div class="error">${el.alert_message}</div>` : ''}
${
el.form_fields
formFieldTemplates
? html`
<form class="converse-form" @submit=${ev => el.formSubmit(ev)}>
<p class="title">
@ -30,7 +42,7 @@ export function tplPollForm (el) {
<p class="form-help instructions">${el.instructions}</p>
<div class="form-errors hidden"></div>
${el.form_fields}
${formFieldTemplates}
<fieldset class="buttons form-group">
<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
role="progressbar"
style="width: ${percent}%;"
style=${'width: ' + percent + '%;'}
aria-valuenow="${percent}" aria-valuemin="0" aria-valuemax="100"
></div>
<p>

View File

@ -3,12 +3,12 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { XMLNS_POLL } from './constants.js'
import { _converse, api } from '../../../src/headless/core.js'
import { _converse, api } from '../../../src/headless/index.js'
import { __ } from 'i18n'
export function getHeadingButtons (view, buttons) {
const muc = view.model
if (muc.get('type') !== _converse.CHATROOMS_TYPE) {
if (muc.get('type') !== _converse.constants.CHATROOMS_TYPE) {
// only on MUC.
return buttons
}
@ -32,7 +32,7 @@ export function getHeadingButtons (view, buttons) {
api.modal.show('livechat-converse-poll-form-modal', { model: muc })
},
a_class: '',
icon_class: 'fa-list-check', // FIXME
icon_class: 'fa-square-poll-horizontal',
name: 'muc-create-poll'
})

View File

@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
import { _converse, converse, api } from '../../../src/headless/core.js'
import { _converse, converse, api } from '../../../src/headless/index.js'
/**
* 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
import { api } from '@converse/headless/core'
import { api } from '@converse/headless'
import { CustomElement } from 'shared/components/element.js'
import { tplMUCTaskApp } from '../templates/muc-task-app.js'

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@
import BaseModal from 'plugins/modal/modal.js'
import tplPickTaskList from './templates/pick-task-list.js'
import { api } from '@converse/headless/core'
import { api } from '@converse/headless'
import { __ } from 'i18n'
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.
* @class
* @namespace _converse.ChatRoomTaskList
* @namespace _converse.exports.ChatRoomTaskList
* @memberof _converse
*/
class ChatRoomTaskList extends Model {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@
/* eslint-disable max-len */
import { html } from 'lit'
import tplIcons from '../../../src/shared/templates/icons.js'
import tplIcons from '../../../src/shared/components/templates/icons.js'
export default () => {
// Here we are adding some additonal icons to ConverseJS defaults
@ -23,6 +23,11 @@ export default () => {
<symbol id="icon-circle-question" viewBox="0 0 448 512">
<path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM169.8 165.3c7.9-22.3 29.1-37.3 52.8-37.3h58.3c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24V250.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1H222.6c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/>
</symbol>
<!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<symbol id="icon-square-poll-horizontal" viewBox="0 0 448 512">
<path d="M448 96c0-35.3-28.7-64-64-64L64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-320zM256 160c0 17.7-14.3 32-32 32l-96 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l96 0c17.7 0 32 14.3 32 32zm64 64c17.7 0 32 14.3 32 32s-14.3 32-32 32l-192 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l192 0zM192 352c0 17.7-14.3 32-32 32l-32 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l32 0c17.7 0 32 14.3 32 32z"/>
</symbol>
</svg>
`
}

View File

@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: AGPL-3.0-only
import { converse, _converse, api } from '../../../src/headless/core.js'
import { converse, _converse, api } from '../../../src/headless/index.js'
const { $build, Strophe, $iq, sizzle } = converse.env
/**
@ -50,7 +50,7 @@ export class PubSubManager {
async start () {
// FIXME: handle errors. Find a way to display to user that this failed.
this.stanzaHandler = _converse.connection.addHandler(
this.stanzaHandler = api.connection.get().addHandler(
(message) => {
try {
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.
if (this.stanzaHandler) {
_converse.connection.deleteHandler(this.stanzaHandler)
api.connection.get().deleteHandler(this.stanzaHandler)
this.stanzaHandler = undefined
}
}

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { __ } from 'i18n'
import { _converse, api } from '@converse/headless/core'
import { _converse, api } from '@converse/headless'
import { html } from 'lit'
import tplMucBottomPanel from '../../src/plugins/muc-views/templates/muc-bottom-panel.js'
import { CustomElement } from 'shared/components/element.js'
@ -79,7 +79,7 @@ class SlowMode extends CustomElement {
api.elements.define('livechat-slow-mode', SlowMode)
const tplSlowMode = (o) => {
if (!o.can_edit) { return html`` }
if (!o.can_post) { return html`` }
return html`<livechat-slow-mode jid=${o.model.get('jid')}>`
}
@ -128,17 +128,9 @@ const tplViewerMode = (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
if (
!o.can_edit &&
!o.can_post &&
o.model.features?.get?.('x_peertubelivechat_mute_anonymous') &&
_converse.api.settings.get('livechat_specific_is_anonymous') === true
) {

View File

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

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { html } from 'lit'
import { api } from '@converse/headless/core'
import { api } from '@converse/headless'
import { until } from 'lit/directives/until.js'
import { repeat } from 'lit/directives/repeat.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/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'),