New features: announcements WIP (#518):
* Front-end implementation finished. * Refactoring.
This commit is contained in:
parent
8944bb95d8
commit
d92bf9073e
@ -13,13 +13,6 @@
|
||||
--livechat-announcement-color: #000;
|
||||
--livechat-announcement-background-color: #dbf2d8;
|
||||
--livechat-announcement-border-color: #2ab218;
|
||||
|
||||
converse-chat-message-body::first-line {
|
||||
// Different color for the title line
|
||||
color: #FFF;
|
||||
background-color: #2ab218;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&.livechat-highlight {
|
||||
@ -28,15 +21,39 @@
|
||||
--livechat-announcement-border-color: #3075e5;
|
||||
}
|
||||
|
||||
&.livechat-warning {
|
||||
--livechat-announcement-color: #000;
|
||||
--livechat-announcement-background-color: #fadede;
|
||||
--livechat-announcement-border-color: #e03e3e;
|
||||
}
|
||||
|
||||
&.livechat-announcement,
|
||||
&.livechat-highlight {
|
||||
&.livechat-highlight,
|
||||
&.livechat-warning {
|
||||
converse-chat-message-body {
|
||||
border: 2px solid var(--livechat-announcement-border-color);
|
||||
color: var(--livechat-announcement-color);
|
||||
background-color: var(--livechat-announcement-background-color);
|
||||
min-width: 50%;
|
||||
padding: 2em;
|
||||
padding: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.livechat-announcements-form {
|
||||
label {
|
||||
// only for screen readers
|
||||
border: 0 !important;
|
||||
clip: rect(1px, 1px, 1px, 1px) !important;
|
||||
/* stylelint-disable-next-line property-no-vendor-prefix */
|
||||
-webkit-clip-path: inset(50%) !important;
|
||||
clip-path: inset(50%) !important;
|
||||
height: 1px !important;
|
||||
overflow: hidden !important;
|
||||
padding: 0 !important;
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
conversejs/lib/@types/global.d.ts
vendored
2
conversejs/lib/@types/global.d.ts
vendored
@ -4,6 +4,8 @@
|
||||
|
||||
// Important note: loc segments that are declared here must also be in loc.keys.js (for now).
|
||||
|
||||
declare const LOC_ANNOUNCEMENTS_MESSAGE_TYPE: string
|
||||
declare const LOC_ANNOUNCEMENTS_MESSAGE_TYPE_STANDARD: string
|
||||
declare const LOC_ANNOUNCEMENTS_MESSAGE_TYPE_ANNOUNCEMENT: string
|
||||
declare const LOC_ANNOUNCEMENTS_MESSAGE_TYPE_HIGHLIGHT: string
|
||||
declare const LOC_ANNOUNCEMENTS_MESSAGE_TYPE_WARNING: string
|
||||
|
@ -2,6 +2,10 @@
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
interface Current {
|
||||
announcementType: string | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* livechat announcements ConverseJS plugin:
|
||||
* with this plugin, moderators can send highlighted/announcements messages.
|
||||
@ -15,108 +19,60 @@ export const livechatAnnouncementsPlugin = {
|
||||
dependencies: ['converse-muc', 'converse-muc-views'],
|
||||
initialize: function (this: any) {
|
||||
const _converse = this._converse
|
||||
const { __ } = _converse
|
||||
|
||||
// This is a closure variable, to get the current form status when sending a message.
|
||||
let currentAnnouncementType: string | undefined
|
||||
|
||||
// Overloading the MUCMessageForm to handle the announcement type field (if present).
|
||||
const MUCMessageForm = _converse.api.elements.registry['converse-muc-message-form']
|
||||
if (MUCMessageForm) {
|
||||
class MUCMessageFormloaded extends MUCMessageForm {
|
||||
async onFormSubmitted (ev?: Event): Promise<void> {
|
||||
const announcementSelect = this.querySelector('[name=livechat-announcements]')
|
||||
currentAnnouncementType = announcementSelect?.selectedOptions?.[0]?.value || undefined
|
||||
try {
|
||||
await super.onFormSubmitted(ev)
|
||||
if (announcementSelect) { announcementSelect.selectedIndex = 0 } // set back to default
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
currentAnnouncementType = undefined
|
||||
}
|
||||
}
|
||||
_converse.api.elements.define('converse-muc-message-form', MUCMessageFormloaded)
|
||||
const current: Current = {
|
||||
announcementType: undefined
|
||||
}
|
||||
|
||||
// Toolbar: adding the announcement type field (if user has rights).
|
||||
overrideMUCMessageForm(_converse, current)
|
||||
|
||||
_converse.api.listen.on('getToolbarButtons', getToolbarButtons.bind(this))
|
||||
|
||||
// When current user affiliation changes, we must refresh the toolbar.
|
||||
_converse.api.listen.on('chatRoomInitialized', (muc: any) => {
|
||||
muc.occupants.on('change:affiliation', (occupant: any) => {
|
||||
if (occupant.get('jid') !== _converse.bare_jid) { // only for myself
|
||||
return
|
||||
}
|
||||
document.querySelectorAll('converse-chat-toolbar').forEach(e => (e as any).requestUpdate?.())
|
||||
})
|
||||
})
|
||||
_converse.api.listen.on('chatRoomInitialized', (muc: any) => onAffiliationChange(_converse, muc))
|
||||
|
||||
_converse.api.listen.on('getOutgoingMessageAttributes', (chatbox: any, attrs: any) => {
|
||||
// For outgoing message, adding the announcement type if there is a current one.
|
||||
if (!currentAnnouncementType) { return attrs }
|
||||
|
||||
attrs.livechat_announcement_type = currentAnnouncementType
|
||||
if (currentAnnouncementType === 'announcement') {
|
||||
attrs.body = '* ' + __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_ANNOUNCEMENT) + ' * \n' + attrs.body
|
||||
}
|
||||
return attrs
|
||||
return onGetOutgoingMessageAttributes(current, _converse, chatbox, attrs)
|
||||
})
|
||||
|
||||
_converse.api.listen.on('createMessageStanza', async (chat: any, data: any) => {
|
||||
// Outgoing messages: adding an attribute on body for announcements.
|
||||
const { message, stanza } = data
|
||||
const announcementType = message.get('livechat_announcement_type')
|
||||
if (!announcementType) {
|
||||
return data
|
||||
}
|
||||
_converse.api.listen.on('createMessageStanza', createMessageStanza)
|
||||
|
||||
stanza.tree().querySelector('message body')?.setAttribute('x-livechat-announcement-type', announcementType)
|
||||
return data
|
||||
})
|
||||
_converse.api.listen.on('parseMUCMessage', parseMUCMessage)
|
||||
|
||||
_converse.api.listen.on('parseMUCMessage', (stanza: any, attrs: any) => {
|
||||
// Incoming messages: checking if there is an announcement attribute
|
||||
const { sizzle } = window.converse.env
|
||||
const body = sizzle('message body', stanza)?.[0]
|
||||
if (!body) { return attrs }
|
||||
|
||||
const announcementType = body.getAttribute('x-livechat-announcement-type')
|
||||
if (!announcementType) { return attrs }
|
||||
|
||||
// Note: we don't check the value here. Will be done in getExtraMessageClasses.
|
||||
// Moreover, the backend server will ensure that only admins/owners can send this attribute.
|
||||
attrs.livechat_announcement_type = announcementType
|
||||
return attrs
|
||||
})
|
||||
|
||||
// Overloading the Message class to add CSS for announcements.
|
||||
const Message = _converse.api.elements.registry['converse-chat-message']
|
||||
if (Message) {
|
||||
class MessageOverloaded extends Message {
|
||||
getExtraMessageClasses (this: any): string {
|
||||
// Adding CSS class if the message is an announcement.
|
||||
let extraClasses = super.getExtraMessageClasses() ?? ''
|
||||
const announcementType = this.model.get('livechat_announcement_type')
|
||||
if (!announcementType) {
|
||||
return extraClasses
|
||||
}
|
||||
switch (announcementType) {
|
||||
case 'announcement':
|
||||
extraClasses += ' livechat-announcement'
|
||||
break
|
||||
case 'highlight':
|
||||
extraClasses += ' livechat-highlight'
|
||||
break
|
||||
}
|
||||
return extraClasses
|
||||
}
|
||||
}
|
||||
_converse.api.elements.define('converse-chat-message', MessageOverloaded)
|
||||
}
|
||||
overrideMessage(_converse)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloads the MUCMessageForm to handle the announcement type field (if present) when sending a message.
|
||||
*/
|
||||
function overrideMUCMessageForm (_converse: any, current: Current): void {
|
||||
const MUCMessageForm = _converse.api.elements.registry['converse-muc-message-form']
|
||||
if (MUCMessageForm) {
|
||||
class MUCMessageFormloaded extends MUCMessageForm {
|
||||
async onFormSubmitted (ev?: Event): Promise<void> {
|
||||
const announcementSelect = this.querySelector('[name=livechat-announcements]')
|
||||
current.announcementType = announcementSelect?.selectedOptions?.[0]?.value || undefined
|
||||
try {
|
||||
await super.onFormSubmitted(ev)
|
||||
if (announcementSelect) { announcementSelect.selectedIndex = 0 } // set back to default
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
current.announcementType = undefined
|
||||
}
|
||||
}
|
||||
_converse.api.elements.define('converse-muc-message-form', MUCMessageFormloaded)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the announcement selector in the toolbar for owner/admin.
|
||||
* @param this the plugin
|
||||
* @param toolbarEl the toolbar element
|
||||
* @param buttons the button list
|
||||
* @returns the updated "button" list
|
||||
*/
|
||||
function getToolbarButtons (this: any, toolbarEl: any, buttons: any[]): Parameters<typeof getToolbarButtons>[1] {
|
||||
const _converse = this._converse
|
||||
const mucModel = toolbarEl.model
|
||||
@ -131,15 +87,26 @@ function getToolbarButtons (this: any, toolbarEl: any, buttons: any[]): Paramete
|
||||
const { __ } = _converse
|
||||
const { html } = window.converse.env
|
||||
|
||||
const i18n = __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE)
|
||||
const i18nStandard = __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_STANDARD)
|
||||
const i18nAnnouncement = __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_ANNOUNCEMENT)
|
||||
const i18nHighlight = __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_HIGHLIGHT)
|
||||
const i18nWarning = __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_WARNING)
|
||||
|
||||
const select = html`<select name="livechat-announcements">
|
||||
<option value="">${i18nStandard}</option>
|
||||
<option value="announcement">${i18nAnnouncement}</option>
|
||||
<option value="highlight">${i18nHighlight}</option>
|
||||
</select>`
|
||||
const select = html`<span class="livechat-announcements-form form-inline">
|
||||
<label for="livechat-announcements-select">${i18n}</label>
|
||||
<select
|
||||
name="livechat-announcements"
|
||||
id="livechat-announcements-select"
|
||||
class="form-control form-control-sm"
|
||||
title=${i18n}
|
||||
>
|
||||
<option value="">${i18nStandard}</option>
|
||||
<option value="highlight">${i18nHighlight}</option>
|
||||
<option value="announcement">${i18nAnnouncement}</option>
|
||||
<option value="warning">${i18nWarning}</option>
|
||||
</select>
|
||||
</span>`
|
||||
|
||||
if (_converse.api.settings.get('visible_toolbar_buttons').emoji) {
|
||||
// Emojis should be the first entry, so adding select in second place.
|
||||
@ -154,3 +121,106 @@ function getToolbarButtons (this: any, toolbarEl: any, buttons: any[]): Paramete
|
||||
}
|
||||
return buttons
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshed the toolbar when current user affiliation changes.
|
||||
* @param _converse _converse object
|
||||
* @param muc the current muc
|
||||
*/
|
||||
function onAffiliationChange (_converse: any, muc: any): void {
|
||||
muc.occupants.on('change:affiliation', (occupant: any) => {
|
||||
if (occupant.get('jid') !== _converse.bare_jid) { // only for myself
|
||||
return
|
||||
}
|
||||
document.querySelectorAll('converse-chat-toolbar').forEach(e => (e as any).requestUpdate?.())
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* For outgoing message, adding the announcement type if there is a current one.
|
||||
* @param current current object
|
||||
* @param _converse _converse object
|
||||
* @param chatbox the chatbox
|
||||
* @param attrs message attributes
|
||||
* @returns
|
||||
*/
|
||||
function onGetOutgoingMessageAttributes (
|
||||
current: Current,
|
||||
_converse: any,
|
||||
chatbox: any,
|
||||
attrs: any
|
||||
): Parameters<typeof onGetOutgoingMessageAttributes>[3] {
|
||||
if (!current.announcementType) { return attrs }
|
||||
|
||||
const { __ } = _converse
|
||||
attrs.livechat_announcement_type = current.announcementType
|
||||
if (current.announcementType === 'announcement') {
|
||||
attrs.body = '* ' + __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_ANNOUNCEMENT) + ' * \n' + attrs.body
|
||||
} else if (current.announcementType === 'warning') {
|
||||
attrs.body = '* ' + __(LOC_ANNOUNCEMENTS_MESSAGE_TYPE_WARNING) + ' *\n' + attrs.body
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
/**
|
||||
* Outgoing messages: adding an attribute on body for announcements.
|
||||
* @param chat
|
||||
* @param data
|
||||
*/
|
||||
async function createMessageStanza (
|
||||
chat: any,
|
||||
data: any
|
||||
): Promise<Parameters<typeof createMessageStanza>[1]> {
|
||||
const { message, stanza } = data
|
||||
const announcementType = message.get('livechat_announcement_type')
|
||||
if (!announcementType) {
|
||||
return data
|
||||
}
|
||||
|
||||
stanza.tree().querySelector('message body')?.setAttribute('x-livechat-announcement-type', announcementType)
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* Incoming messages: checking if there is an announcement attribute, and adding it in computed attributes.
|
||||
* @param stanza
|
||||
* @param attrs
|
||||
*/
|
||||
function parseMUCMessage (stanza: any, attrs: any): Parameters<typeof parseMUCMessage>[1] {
|
||||
const { sizzle } = window.converse.env
|
||||
const body = sizzle('message body', stanza)?.[0]
|
||||
if (!body) { return attrs }
|
||||
|
||||
const announcementType = body.getAttribute('x-livechat-announcement-type')
|
||||
if (!announcementType) { return attrs }
|
||||
|
||||
// Note: we don't check the value here. Will be done in getExtraMessageClasses.
|
||||
// Moreover, the backend server will ensure that only admins/owners can send this attribute.
|
||||
attrs.livechat_announcement_type = announcementType
|
||||
return attrs
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloading the Message class to add CSS for announcements.
|
||||
* @param _converse
|
||||
*/
|
||||
function overrideMessage (_converse: any): void {
|
||||
const Message = _converse.api.elements.registry['converse-chat-message']
|
||||
if (Message) {
|
||||
class MessageOverloaded extends Message {
|
||||
getExtraMessageClasses (this: any): string {
|
||||
// Adding CSS class if the message is an announcement.
|
||||
let extraClasses = super.getExtraMessageClasses() ?? ''
|
||||
const announcementType: string | undefined = this.model.get('livechat_announcement_type')
|
||||
if (!announcementType) {
|
||||
return extraClasses
|
||||
}
|
||||
if (['announcement', 'highlight', 'warning'].includes(announcementType)) {
|
||||
extraClasses += ' livechat-' + announcementType
|
||||
}
|
||||
return extraClasses
|
||||
}
|
||||
}
|
||||
_converse.api.elements.define('converse-chat-message', MessageOverloaded)
|
||||
}
|
||||
}
|
||||
|
@ -64,9 +64,11 @@ const locKeys = [
|
||||
'message_search',
|
||||
'message_search_original_nick',
|
||||
'emoji_only_info',
|
||||
'announcements_message_type',
|
||||
'announcements_message_type_standard',
|
||||
'announcements_message_type_announcement',
|
||||
'announcements_message_type_highlight'
|
||||
'announcements_message_type_highlight',
|
||||
'announcements_message_type_warning'
|
||||
]
|
||||
|
||||
module.exports = locKeys
|
||||
|
@ -671,6 +671,8 @@ livechat_configuration_channel_no_duplicate_delay_label: Time interval
|
||||
livechat_configuration_channel_no_duplicate_delay_desc: |
|
||||
The interval, in seconds, during which a user can't send again the same message.
|
||||
|
||||
announcements_message_type_standard: Standard message
|
||||
announcements_message_type: Message type
|
||||
announcements_message_type_standard: Standard
|
||||
announcements_message_type_announcement: Announcement
|
||||
announcements_message_type_highlight: Highlighted message
|
||||
announcements_message_type_highlight: Highlight
|
||||
announcements_message_type_warning: Warning
|
||||
|
Loading…
Reference in New Issue
Block a user