diff --git a/CHANGELOG.md b/CHANGELOG.md index 5da0a497..9196ae73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Updating ConverseJS, to use upstream (v11 WIP). This comes with many improvments and new features. * #146: copy message button for moderators. * #137: option to hide moderator name who made actions (kick, ban, message moderation, ...). +* #144: [moderator notes](https://livingston.frama.io/peertube-plugin-livechat/documentation/user/streamers/notes/). ### Minor changes and fixes diff --git a/conversejs/builtin.ts b/conversejs/builtin.ts index c1dd6689..2f6c3928 100644 --- a/conversejs/builtin.ts +++ b/conversejs/builtin.ts @@ -219,9 +219,12 @@ async function initConverse ( // * mode === chat-only + !transparent + !readonly + is using a livechat token // Technically it would work in 'chat-only' mode, but i don't want to add too many things to test // (and i now there is some CSS bugs in the task list). + // Same for the moderator notes app. let enableTask = false + let enableModeratorNotes = false if (chatIncludeMode === 'peertube-video' || chatIncludeMode === 'peertube-fullpage') { enableTask = true + enableModeratorNotes = true } else if ( chatIncludeMode === 'chat-only' && usedLivechatToken && @@ -229,11 +232,16 @@ async function initConverse ( !initConverseParams.forceReadonly ) { enableTask = true + enableModeratorNotes = true } if (enableTask) { params.livechat_task_app_enabled = true params.livechat_task_app_restore = chatIncludeMode === 'peertube-fullpage' || chatIncludeMode === 'chat-only' } + if (enableModeratorNotes) { + params.livechat_note_app_enabled = true + params.livechat_note_app_restore = chatIncludeMode === 'peertube-fullpage' || chatIncludeMode === 'chat-only' + } try { if (window.reconnectConverse) { // this is set in the livechatSpecificsPlugin diff --git a/conversejs/custom/index.js b/conversejs/custom/index.js index 71d2ab6d..29de15ce 100644 --- a/conversejs/custom/index.js +++ b/conversejs/custom/index.js @@ -44,6 +44,7 @@ import './plugins/singleton/index.js' import './plugins/fullscreen/index.js' import '../custom/plugins/size/index.js' +import '../custom/plugins/notes/index.js' import '../custom/plugins/tasks/index.js' import '../custom/plugins/terms/index.js' import '../custom/plugins/poll/index.js' @@ -59,6 +60,7 @@ CORE_PLUGINS.push('livechat-converse-size') CORE_PLUGINS.push('livechat-converse-tasks') CORE_PLUGINS.push('livechat-converse-terms') CORE_PLUGINS.push('livechat-converse-poll') +CORE_PLUGINS.push('livechat-converse-notes') // 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') diff --git a/conversejs/custom/plugins/notes/components/muc-note-app-view.js b/conversejs/custom/plugins/notes/components/muc-note-app-view.js new file mode 100644 index 00000000..6b7368f0 --- /dev/null +++ b/conversejs/custom/plugins/notes/components/muc-note-app-view.js @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2024 John Livingston +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { api } from '@converse/headless' +import { MUCApp } from '../../../shared/components/muc-app.js' +import { tplMUCNoteApp } from '../templates/muc-note-app.js' + +/** + * Custom Element to display the Notes Application. + */ +export default class MUCNoteApp extends MUCApp { + enableSettingName = 'livechat_note_app_restore' + sessionStorangeShowKey = 'livechat-converse-note-app-show' + + render () { + return tplMUCNoteApp(this, this.model) + } +} + +api.elements.define('livechat-converse-muc-note-app', MUCNoteApp) diff --git a/conversejs/custom/plugins/notes/constants.js b/conversejs/custom/plugins/notes/constants.js new file mode 100644 index 00000000..baefcc00 --- /dev/null +++ b/conversejs/custom/plugins/notes/constants.js @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2024 John Livingston +// +// SPDX-License-Identifier: AGPL-3.0-only + +export const XMLNS_NOTE = 'urn:peertube-plugin-livechat:note' diff --git a/conversejs/custom/plugins/notes/index.js b/conversejs/custom/plugins/notes/index.js new file mode 100644 index 00000000..7f3127af --- /dev/null +++ b/conversejs/custom/plugins/notes/index.js @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2024 John Livingston +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { _converse, converse } from '../../../src/headless/index.js' +import { XMLNS_NOTE } from './constants.js' +import { ChatRoomNote } from './note.js' +import { ChatRoomNotes } from './notes.js' +import { initOrDestroyChatRoomNotes, getHeadingButtons, getMessageActionButtons } from './utils.js' + +import './components/muc-note-app-view.js' + +converse.plugins.add('livechat-converse-notes', { + dependencies: ['converse-muc', 'converse-disco', 'converse-pubsub'], + + initialize () { + Object.assign( + _converse.exports, + { + ChatRoomNotes, + ChatRoomNote + } + ) + + _converse.api.settings.extend({ + livechat_note_app_enabled: false, + livechat_note_app_restore: false // should we open the app by default if it was previously oppened? + }) + + _converse.api.listen.on('chatRoomInitialized', muc => { + muc.session.on('change:connection_status', _session => { + // When joining a room, initializing the Notes object (if user has access), + // When disconnected from a room, destroying the Notes object: + initOrDestroyChatRoomNotes(muc) + }) + + // When the current user affiliation changes, we must also delete or initialize the TaskLists object: + muc.occupants.on('change:affiliation', occupant => { + if (occupant.get('jid') !== _converse.bare_jid) { // only for myself + return + } + initOrDestroyChatRoomNotes(muc) + }) + + // To be sure that everything works in any case, we also must listen for addition in muc.features. + muc.features.on('change:' + XMLNS_NOTE, () => { + initOrDestroyChatRoomNotes(muc) + }) + }) + + // adding the "Notes" button in the MUC heading buttons: + _converse.api.listen.on('getHeadingButtons', getHeadingButtons) + + // Adding buttons on message: + _converse.api.listen.on('getMessageActionButtons', getMessageActionButtons) + } +}) diff --git a/conversejs/custom/plugins/notes/note.js b/conversejs/custom/plugins/notes/note.js new file mode 100644 index 00000000..67cd679c --- /dev/null +++ b/conversejs/custom/plugins/notes/note.js @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2024 John Livingston +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { Model } from '@converse/skeletor/src/model.js' + +/** + * A chat room note. + * @class + * @namespace _converse.exports.ChatRoomNote + * @memberof _converse + */ +class ChatRoomNote extends Model { + idAttribute = 'id' + + async saveItem () { + console.log('Saving note ' + this.get('id') + '...') + await this.collection.chatroom.noteManager.saveItem(this) + console.log('Note ' + this.get('id') + ' saved.') + } + + async deleteItem () { + return this.collection.chatroom.noteManager.deleteItems([this]) + } +} + +export { + ChatRoomNote +} diff --git a/conversejs/custom/plugins/notes/notes.js b/conversejs/custom/plugins/notes/notes.js new file mode 100644 index 00000000..32a4d9e3 --- /dev/null +++ b/conversejs/custom/plugins/notes/notes.js @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2024 John Livingston +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { Collection } from '@converse/skeletor/src/collection.js' +import { ChatRoomNote } from './note' +import { initStorage } from '@converse/headless/utils/storage.js' + +/** + * A list of {@link _converse.exports.ChatRoomNote} instances, representing notes associated to a MUC. + * @class + * @namespace _converse.exports.ChatRoomNotes + * @memberOf _converse + */ +class ChatRoomNotes extends Collection { + model = ChatRoomNote + comparator = 'order' + + initialize (models, options) { + this.model = ChatRoomNote // don't know why, must do it again here + super.initialize(arguments) + this.chatroom = options.chatroom + + const id = `converse-livechat-notes-${this.chatroom.get('jid')}` + initStorage(this, id, 'session') + + this.on('change:order', () => this.sort()) + } + + // async createNote (data) { + // console.log('Creating note...') + // await this.chatroom.NoteManager.createItem(this, Object.assign({}, data)) + // console.log('Note created.') + // } +} + +export { + ChatRoomNotes +} diff --git a/conversejs/custom/plugins/notes/templates/muc-note-app.js b/conversejs/custom/plugins/notes/templates/muc-note-app.js new file mode 100644 index 00000000..ec409bcb --- /dev/null +++ b/conversejs/custom/plugins/notes/templates/muc-note-app.js @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: 2024 John Livingston +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { converseLocalizedHelpUrl } from '../../../shared/lib/help' +import { html } from 'lit' +import { __ } from 'i18n' + +export function tplMUCNoteApp (el, mucModel) { + if (!mucModel) { + // should not happen + el.classList.add('hidden') // we must do this, otherwise will have CSS side effects + return html`` + } + if (!mucModel.notes) { + // too soon, not initialized yet (this will happen) + el.classList.add('hidden') // we must do this, otherwise will have CSS side effects + return html`` + } + + if (!el.show) { + el.classList.add('hidden') + return html`` + } + + el.classList.remove('hidden') + + // eslint-disable-next-line no-undef + const i18nNotes = __(LOC_moderator_notes) + // eslint-disable-next-line no-undef + const i18nHelp = __(LOC_online_help) + const helpUrl = converseLocalizedHelpUrl({ + page: 'documentation/user/streamers/notes' + }) + + return html` +
+
${i18nNotes}
+ + +
+
+ +
` +} diff --git a/conversejs/custom/plugins/notes/utils.js b/conversejs/custom/plugins/notes/utils.js new file mode 100644 index 00000000..e425d9e3 --- /dev/null +++ b/conversejs/custom/plugins/notes/utils.js @@ -0,0 +1,146 @@ +// SPDX-FileCopyrightText: 2024 John Livingston +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { XMLNS_NOTE } from './constants.js' +import { PubSubManager } from '../../shared/lib/pubsub-manager.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.constants.CHATROOMS_TYPE) { + // only on MUC. + return buttons + } + + if (!muc.notes) { // this is defined only if user has access (see initOrDestroyChatRoomNotes) + return buttons + } + + // Adding a "Open moderator noteds" button. + buttons.unshift({ + // eslint-disable-next-line no-undef + i18n_text: __(LOC_moderator_notes), + handler: async (ev) => { + ev.preventDefault() + // opening or closing the muc notes: + const NoteAppEl = ev.target.closest('converse-root').querySelector('livechat-converse-muc-note-app') + NoteAppEl.toggleApp() + }, + a_class: '', + icon_class: 'fa-note-sticky', + name: 'muc-notes' + }) + + return buttons +} + +export function getMessageActionButtons (messageActionsEl, buttons) { + const messageModel = messageActionsEl.model + if (messageModel.get('type') !== 'groupchat') { + // only on groupchat message. + return buttons + } + + const muc = messageModel.collection?.chatbox + if (!muc?.notes) { + return buttons + } + + // TODO: button to create a note from a message. + // // eslint-disable-next-line no-undef + // const i18nCreate = __(LOC_task_create) + + // buttons.push({ + // i18n_text: i18nCreate, + // handler: async (ev) => { + // ev.preventDefault() + // api.modal.show('livechat-converse-pick-task-list-modal', { + // muc, + // message: messageModel + // }, ev) + // }, + // button_class: '', + // icon_class: 'fa fa-list-check', + // name: 'muc-task-create-from-message' + // }) + + return buttons +} + +function _initChatRoomNotes (mucModel) { + if (mucModel.noteManager) { + // already initiliazed + return + } + + mucModel.notes = new _converse.exports.ChatRoomNotes(undefined, { chatroom: mucModel }) + + mucModel.noteManager = new PubSubManager( + mucModel.get('jid'), + 'livechat-notes', // the node name + { + note: { + itemTag: 'note', + xmlns: XMLNS_NOTE, + collection: mucModel.notes, + fields: { + name: String, + description: String + }, + attributes: { + order: Number + } + } + } + ) + mucModel.noteManager.start().catch(err => console.log(err)) + + // We must requestUpdate for all message actions, to add the "create note" button. + // FIXME: this should not be done here (but it is simplier for now) + document.querySelectorAll('converse-message-actions').forEach(el => el.requestUpdate()) +} + +function _destroyChatRoomNotes (mucModel) { + if (!mucModel.noteManager) { return } + + mucModel.noteManager.stop().catch(err => console.log(err)) + mucModel.noteManager = undefined + + mucModel.notes = undefined + + // We must requestUpdate for all message actions, to remove the "create note" button. + // FIXME: this should not be done here (but it is simplier for now) + document.querySelectorAll('converse-message-actions').forEach(el => el.requestUpdate()) +} + +export function initOrDestroyChatRoomNotes (mucModel) { + if (mucModel.get('type') !== _converse.constants.CHATROOMS_TYPE) { + // only on MUC. + return _destroyChatRoomNotes(mucModel) + } + + if (!api.settings.get('livechat_note_app_enabled')) { + // Feature disabled, no need to handle notes. + return _destroyChatRoomNotes(mucModel) + } + + if (mucModel.session.get('connection_status') !== converse.ROOMSTATUS.ENTERED) { + return _destroyChatRoomNotes(mucModel) + } + + // We must check disco features + // (if the chat is remote, the server could use a livechat version that does not support this feature) + if (!mucModel.features?.get?.(XMLNS_NOTE)) { + return _destroyChatRoomNotes(mucModel) + } + + const myself = mucModel.getOwnOccupant() + if (!myself || !['admin', 'owner'].includes(myself.get('affiliation'))) { + // User must be admin or owner + return _destroyChatRoomNotes(mucModel) + } + + return _initChatRoomNotes(mucModel) +} diff --git a/conversejs/custom/plugins/size/index.js b/conversejs/custom/plugins/size/index.js index d02ce408..3ac9b54c 100644 --- a/conversejs/custom/plugins/size/index.js +++ b/conversejs/custom/plugins/size/index.js @@ -4,6 +4,8 @@ import { _converse, converse, api } from '../../../src/headless/index.js' +let currentSize + /** * This plugin computes the available width of converse-root, and adds classes * and events so we can adapt the display of some elements to the current @@ -16,6 +18,27 @@ converse.plugins.add('livechat-converse-size', { dependencies: [], initialize () { + Object.assign(api, { + livechat_size: { + current: () => { + return currentSize + }, + width_is: (sizes) => { + if (!Array.isArray(sizes)) { + sizes = [sizes] + } + if (!currentSize) { return false } + return sizes.includes(currentSize.width) + }, + height_is: (sizes) => { + if (!Array.isArray(sizes)) { + sizes = [sizes] + } + if (!currentSize) { return false } + return sizes.includes(currentSize.height) + } + } + }) _converse.api.listen.on('connected', start) _converse.api.listen.on('reconnected', start) _converse.api.listen.on('disconnected', stop) @@ -42,6 +65,7 @@ function start () { } function stop () { + currentSize = undefined rootResizeObserver.disconnect() const root = document.querySelector('converse-root') if (root) { @@ -60,8 +84,9 @@ function handle (el) { el.setAttribute('livechat-converse-root-width', width) el.setAttribute('livechat-converse-root-height', height) - api.trigger('livechatSizeChanged', { + currentSize = { height: height, width: width - }) + } + api.trigger('livechatSizeChanged', Object.assign({}, currentSize)) // cloning... } diff --git a/conversejs/custom/plugins/tasks/components/muc-task-app-view.js b/conversejs/custom/plugins/tasks/components/muc-task-app-view.js index 32e3c7ce..ebadd97b 100644 --- a/conversejs/custom/plugins/tasks/components/muc-task-app-view.js +++ b/conversejs/custom/plugins/tasks/components/muc-task-app-view.js @@ -3,35 +3,19 @@ // SPDX-License-Identifier: AGPL-3.0-only import { api } from '@converse/headless' -import { CustomElement } from 'shared/components/element.js' +import { MUCApp } from '../../../shared/components/muc-app.js' import { tplMUCTaskApp } from '../templates/muc-task-app.js' -import '../styles/muc-task-app.scss' - /** * Custom Element to display the Task Application. */ -export default class MUCTaskApp extends CustomElement { - static get properties () { - return { - model: { type: Object, attribute: true }, // mucModel - show: { type: Boolean, attribute: false } - } - } - - async initialize () { - this.show = api.settings.get('livechat_task_app_restore') && - (window.sessionStorage?.getItem?.('livechat-converse-task-app-show') === '1') - } +export default class MUCTaskApp extends MUCApp { + enableSettingName = 'livechat_task_app_restore' + sessionStorangeShowKey = 'livechat-converse-task-app-show' render () { return tplMUCTaskApp(this, this.model) } - - toggleApp () { - this.show = !this.show - window.sessionStorage?.setItem?.('livechat-converse-task-app-show', this.show ? '1' : '') - } } api.elements.define('livechat-converse-muc-task-app', MUCTaskApp) diff --git a/conversejs/custom/shared/components/font-awesome.js b/conversejs/custom/shared/components/font-awesome.js index 2713ea23..d3ab7ede 100644 --- a/conversejs/custom/shared/components/font-awesome.js +++ b/conversejs/custom/shared/components/font-awesome.js @@ -28,6 +28,11 @@ export default () => { + + + + + ` } diff --git a/conversejs/custom/shared/components/muc-app.js b/conversejs/custom/shared/components/muc-app.js new file mode 100644 index 00000000..8ab03c5e --- /dev/null +++ b/conversejs/custom/shared/components/muc-app.js @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2024 John Livingston +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { CustomElement } from 'shared/components/element.js' +import { api, _converse } from '@converse/headless' +import './styles/muc-app.scss' + +/** + * Base class for MUC App custom elements (task app, notes app, ...). + * This is an abstract class, should not be called directly. + */ +export class MUCApp extends CustomElement { + enableSettingName = undefined // must be overloaded + sessionStorangeShowKey = undefined // must be overloaded + + static get properties () { + return { + model: { type: Object, attribute: true }, // mucModel + show: { type: Boolean, attribute: false } + } + } + + async initialize () { + this.classList.add('livechat-converse-muc-app') + this.show = this.enableSettingName && + api.settings.get(this.enableSettingName) && + this.sessionStorangeShowKey && + (window.sessionStorage?.getItem?.(this.sessionStorangeShowKey) === '1') + + // we listen for livechatSizeChanged event, + // and close all apps except the first if small or medium width. + // Note: this will also be triggered when we first open the page + this.listenTo(_converse, 'livechatSizeChanged', () => { + if (!this.show || !api.livechat_size?.width_is(['small', 'medium'])) { + return + } + // are we the first opened app? + for (const el of document.querySelectorAll('.livechat-converse-muc-app')) { + if (el === this) { break } + if (!el.show) { continue } + console.debug('The livechat size is small or medium, there is already an opened app, so closing myself', this) + // ok, there is already an opened app. + this.toggleApp() // we know we are open + break + } + }) + } + + render () { // must be overloaded. + return '' + } + + toggleApp () { + this.show = !this.show + if (this.sessionStorangeShowKey) { + window.sessionStorage?.setItem?.(this.sessionStorangeShowKey, this.show ? '1' : '') + } + + if ( + this.show && + api.livechat_size?.width_is(['small', 'medium']) + ) { + // When showing an App, if the screen width is small or medium, we hide the others. + this._closeOtherApps() + } + } + + _closeOtherApps () { + document.querySelectorAll('.livechat-converse-muc-app').forEach((el) => { + if (el !== this && el.show) { + console.debug('Closing another app, because livechat width is small or medium', el) + el.toggleApp() + } + }) + } +} diff --git a/conversejs/custom/plugins/tasks/styles/muc-task-app.scss b/conversejs/custom/shared/components/styles/muc-app.scss similarity index 89% rename from conversejs/custom/plugins/tasks/styles/muc-task-app.scss rename to conversejs/custom/shared/components/styles/muc-app.scss index e636a57e..59eb23a7 100644 --- a/conversejs/custom/plugins/tasks/styles/muc-task-app.scss +++ b/conversejs/custom/shared/components/styles/muc-app.scss @@ -5,7 +5,7 @@ */ .conversejs { - livechat-converse-muc-task-app { + .livechat-converse-muc-app { border: var(--occupants-border-left); display: flex; flex-flow: column nowrap; @@ -42,8 +42,8 @@ &[livechat-converse-root-width="small"], &[livechat-converse-root-width="medium"] { - converse-muc-chatarea livechat-converse-muc-task-app:not(.hidden) ~ * { - // on small and medium width, we hide all subsequent siblings of the task app + converse-muc-chatarea .livechat-converse-muc-app:not(.hidden) ~ * { + // on small and medium width, we hide all subsequent siblings of the app // (when app is not hidden) display: none !important; } diff --git a/conversejs/custom/templates/muc-chatarea.js b/conversejs/custom/templates/muc-chatarea.js index d709ca56..6980b17f 100644 --- a/conversejs/custom/templates/muc-chatarea.js +++ b/conversejs/custom/templates/muc-chatarea.js @@ -13,5 +13,10 @@ export default (o) => { ? html`` : '' } + ${ + o?.model && api.settings.get('livechat_note_app_enabled') + ? html`` + : '' + } ${tplMUCChatarea(o)}` } diff --git a/conversejs/loc.keys.js b/conversejs/loc.keys.js index 7a6f9283..bb811c3f 100644 --- a/conversejs/loc.keys.js +++ b/conversejs/loc.keys.js @@ -49,7 +49,8 @@ const locKeys = [ 'poll_vote_instructions_xmpp', 'poll_is_over', 'poll_choice_invalid', - 'poll_anonymous_vote_ok' + 'poll_anonymous_vote_ok', + 'moderator_notes' ] module.exports = locKeys diff --git a/languages/en.yml b/languages/en.yml index eeded6d1..b0133c1e 100644 --- a/languages/en.yml +++ b/languages/en.yml @@ -593,3 +593,5 @@ livechat_configuration_channel_anonymize_moderation_label: "Anonymize moderation livechat_configuration_channel_anonymize_moderation_desc: | Anonymize moderation actions default value for new rooms. When this is enabled, moderation actions will be anonymized, to avoid disclosing who is banning/kicking/… occupants. + +moderator_notes: Moderator notes diff --git a/prosody-modules/mod_pubsub_peertubelivechat/README.md b/prosody-modules/mod_pubsub_peertubelivechat/README.md index b0525942..62d143cc 100644 --- a/prosody-modules/mod_pubsub_peertubelivechat/README.md +++ b/prosody-modules/mod_pubsub_peertubelivechat/README.md @@ -7,13 +7,16 @@ SPDX-License-Identifier: AGPL-3.0-only This module is a custom module that provide some pubsub services associated to a MUC room. This module is entended to be used in the peertube-plugin-livechat project. -For each MUC room, there will be an associated pubsub node. -This node in only accessible by the ROOM admin/owner. +For each MUC room, there will be a associated pubsub nodes. +These nodes are only accessible by the ROOM admins/owners. -This node can contains various objects: +Here are a description of existing nodes, and objects they can contain: -* task lists -* tasks +* livechat-tasks: + * task lists + * tasks +* livechat-notes: + * notes * ... (more to come) These objects are meant te be shared between admin/owner. diff --git a/prosody-modules/mod_pubsub_peertubelivechat/mod_pubsub_peertubelivechat.lua b/prosody-modules/mod_pubsub_peertubelivechat/mod_pubsub_peertubelivechat.lua index 95465bf1..5b004436 100644 --- a/prosody-modules/mod_pubsub_peertubelivechat/mod_pubsub_peertubelivechat.lua +++ b/prosody-modules/mod_pubsub_peertubelivechat/mod_pubsub_peertubelivechat.lua @@ -15,6 +15,7 @@ -- Implemented nodes: -- * livechat-tasks: contains tasklist and task items, specific to livechat plugin. +-- * livechat-notes: contains notes, specific to livechat plugin. -- There are some other tricks in this module: -- * unsubscribing users that have left the room (the front-end will subscribe again when needed) @@ -39,7 +40,8 @@ local xmlns_pubsub = "http://jabber.org/protocol/pubsub"; local xmlns_pubsub_event = "http://jabber.org/protocol/pubsub#event"; local xmlns_pubsub_owner = "http://jabber.org/protocol/pubsub#owner"; local xmlns_tasklist = "urn:peertube-plugin-livechat:tasklist"; -local xmlns_task = "urn:peertube-plugin-livechat:task" +local xmlns_task = "urn:peertube-plugin-livechat:task"; +local xmlns_note = "urn:peertube-plugin-livechat:note"; local lib_pubsub = module:require "pubsub"; @@ -389,4 +391,5 @@ end); module:hook("muc-disco#info", function (event) event.reply:tag("feature", { var = xmlns_task }):up(); event.reply:tag("feature", { var = xmlns_tasklist }):up(); + event.reply:tag("feature", { var = xmlns_note }):up(); end);