diff --git a/assets/styles/elements/_spinner.scss b/assets/styles/elements/_spinner.scss
index 672bf01c..9fa9e39d 100644
--- a/assets/styles/elements/_spinner.scss
+++ b/assets/styles/elements/_spinner.scss
@@ -15,9 +15,9 @@ livechat-spinner,
height: 48px;
margin: 20px;
/* stylelint-disable-next-line custom-property-pattern */
- border: 5px solid var(--greyBackgroundColor);
+ border: 5px solid var(--greyBackgroundColor) !important; // !important is required for it to work in ConverseJS
/* stylelint-disable-next-line custom-property-pattern */
- border-bottom-color: var(--mainColor);
+ border-bottom-color: var(--mainColor) !important; // !important is required for it to work in ConverseJS
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
diff --git a/conversejs/builtin.ts b/conversejs/builtin.ts
index 2f6c3928..9cb1cc5a 100644
--- a/conversejs/builtin.ts
+++ b/conversejs/builtin.ts
@@ -220,27 +220,23 @@ async function initConverse (
// 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
+ let enableApps = false
if (chatIncludeMode === 'peertube-video' || chatIncludeMode === 'peertube-fullpage') {
- enableTask = true
- enableModeratorNotes = true
+ enableApps = true
} else if (
chatIncludeMode === 'chat-only' &&
usedLivechatToken &&
!initConverseParams.transparent &&
!initConverseParams.forceReadonly
) {
- enableTask = true
- enableModeratorNotes = true
+ enableApps = true
}
- if (enableTask) {
+ if (enableApps) {
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'
+ params.livechat_mam_search_app_enabled = true
}
try {
diff --git a/conversejs/custom/plugins/mam-search/api.js b/conversejs/custom/plugins/mam-search/api.js
index 93f7e2be..77fe71fe 100644
--- a/conversejs/custom/plugins/mam-search/api.js
+++ b/conversejs/custom/plugins/mam-search/api.js
@@ -95,6 +95,18 @@ async function query (options) {
return { messages }
}
-export default {
- query
+async function showMessagesFrom (occupant) {
+ const appElement = document.querySelector('livechat-converse-muc-mam-search-app')
+ if (!appElement) {
+ throw new Error('Cant find Search App Element')
+ }
+ appElement.searchFrom(occupant)
+ await appElement.showApp()
+ await appElement.updateComplete // waiting for the app to be open
+ return appElement
+}
+
+export default {
+ query,
+ showMessagesFrom
}
diff --git a/conversejs/custom/plugins/mam-search/components/muc-mam-search-app-view.js b/conversejs/custom/plugins/mam-search/components/muc-mam-search-app-view.js
new file mode 100644
index 00000000..3b3585f0
--- /dev/null
+++ b/conversejs/custom/plugins/mam-search/components/muc-mam-search-app-view.js
@@ -0,0 +1,55 @@
+// SPDX-FileCopyrightText: 2024 John Livingston
+//
+// SPDX-License-Identifier: AGPL-3.0-only
+
+import { api } from '@converse/headless'
+import { Collection } from '@converse/skeletor'
+import { parseMUCMessage } from '@converse/headless/plugins/muc/parsers.js'
+import { MUCApp } from '../../../shared/components/muc-app/index.js'
+import { tplMamSearchApp } from '../templates/muc-mam-search-app.js'
+
+/**
+ * Custom Element to display the Mam Search Application.
+ */
+export default class MUCMamSearchApp extends MUCApp {
+ restoreSettingName = undefined
+ sessionStorageRestoreKey = undefined
+
+ static get properties () {
+ return {
+ model: { type: Object, attribute: true }, // the muc model
+ occupant: { type: Object, attribute: true }, // the occupant to search (can be undefined if no current search)
+ results: { type: Object, attribute: true } // a Collection with the results.
+ }
+ }
+
+ render () {
+ return tplMamSearchApp(this, this.model, this.occupant)
+ }
+
+ searchFrom (occupant) {
+ this.results = undefined
+ this.occupant = occupant
+ const p = api.livechat_mam_search.query({
+ room: this.model.get('jid'),
+ // FIXME: shouldn't we escape the nick? cant see any code that escapes it in Converse.
+ from: occupant.get('from') || this.model.get('jid') + '/' + (occupant.get('nick') ?? ''),
+ occupant_id: occupant.get('occupant_id')
+ })
+
+ // don't wait the result to show something! (there will be a spinner)
+ p.then(async (results) => {
+ this.occupant = occupant // in case user did simultaneous requests
+
+ const messages = await Promise.all(results.messages.map(s => parseMUCMessage(s, this.model)))
+ const col = new Collection()
+ for (const message of messages) {
+ // FIXME: this does not work for now, the collection is not properly initiated (no storage engine)
+ col.create(message)
+ }
+ this.results = col
+ })
+ }
+}
+
+api.elements.define('livechat-converse-muc-mam-search-app', MUCMamSearchApp)
diff --git a/conversejs/custom/plugins/mam-search/components/muc-mam-search-occupant-view.js b/conversejs/custom/plugins/mam-search/components/muc-mam-search-occupant-view.js
new file mode 100644
index 00000000..c88f60dd
--- /dev/null
+++ b/conversejs/custom/plugins/mam-search/components/muc-mam-search-occupant-view.js
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: 2024 John Livingston
+//
+// SPDX-License-Identifier: AGPL-3.0-only
+
+import { CustomElement } from 'shared/components/element.js'
+import { tplMucMamSearchOccupant } from '../templates/muc-mam-search-occupant'
+import { api } from '@converse/headless'
+
+import '../styles/muc-mam-search-occupant.scss'
+
+export default class MUCMamSearchOccupantView extends CustomElement {
+ static get properties () {
+ return {
+ model: { type: Object, attribute: true }
+ }
+ }
+
+ async initialize () {
+ this.listenTo(this.model, 'change', () => this.requestUpdate())
+ }
+
+ render () {
+ return tplMucMamSearchOccupant(this, this.model)
+ }
+}
+
+api.elements.define('livechat-converse-muc-mam-search-occupant', MUCMamSearchOccupantView)
diff --git a/conversejs/custom/plugins/mam-search/index.js b/conversejs/custom/plugins/mam-search/index.js
index 17ca2551..d5efd510 100644
--- a/conversejs/custom/plugins/mam-search/index.js
+++ b/conversejs/custom/plugins/mam-search/index.js
@@ -3,9 +3,11 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { api, converse } from '../../../src/headless/index.js'
-import { XMLNS_MAM_SEARCH } from './constants.js'
+import { getMessageActionButtons } from './utils.js'
import mamSearchApi from './api.js'
-import { __ } from 'i18n'
+
+import './components/muc-mam-search-app-view.js'
+import './components/muc-mam-search-occupant-view.js'
converse.plugins.add('livechat-converse-mam-search', {
dependencies: ['converse-muc', 'converse-muc-views'],
@@ -16,56 +18,13 @@ converse.plugins.add('livechat-converse-mam-search', {
livechat_mam_search: mamSearchApi
})
+ _converse.api.settings.extend({
+ livechat_mam_search_app_enabled: false
+ })
+
// Adding buttons on message:
_converse.api.listen.on('getMessageActionButtons', getMessageActionButtons)
// FIXME: should we listen to any event (feature/affiliation change?, mam_enabled?) to refresh messageActionButtons?
}
})
-
-function getMessageActionButtons (messageActionsEl, buttons) {
- const messageModel = messageActionsEl.model
- if (messageModel.get('type') !== 'groupchat') {
- // only on groupchat message.
- return buttons
- }
-
- if (!messageModel.occupant) {
- return buttons
- }
-
- const muc = messageModel.collection?.chatbox
- if (!muc) {
- return buttons
- }
-
- if (!muc.features?.get?.(XMLNS_MAM_SEARCH)) {
- return buttons
- }
-
- const myself = muc.getOwnOccupant()
- if (!myself || !['admin', 'owner'].includes(myself.get('affiliation'))) {
- return buttons
- }
-
- // eslint-disable-next-line no-undef
- const i18nSearch = __(LOC_search_occupant_message)
-
- buttons.push({
- i18n_text: i18nSearch,
- handler: async (ev) => {
- ev.preventDefault()
- console.log(await api.livechat_mam_search.query({
- room: muc.get('jid'),
- // FIXME: shouldn't we escape the nick? cant see any code that escapes it in Converse.
- from: messageModel.occupant.get('from') || muc.get('jid') + '/' + (messageModel.occupant.get('nick') ?? ''),
- occupant_id: messageModel.occupant.get('occupant_id')
- }))
- },
- button_class: '',
- icon_class: 'fa fa-magnifying-glass',
- name: 'muc-mam-search'
- })
-
- return buttons
-}
diff --git a/conversejs/custom/plugins/mam-search/styles/muc-mam-search-occupant.scss b/conversejs/custom/plugins/mam-search/styles/muc-mam-search-occupant.scss
new file mode 100644
index 00000000..2678b926
--- /dev/null
+++ b/conversejs/custom/plugins/mam-search/styles/muc-mam-search-occupant.scss
@@ -0,0 +1,36 @@
+/*
+ * SPDX-FileCopyrightText: 2024 John Livingston
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+.conversejs {
+ livechat-converse-muc-mam-search-occupant {
+ display: flex;
+ flex-flow: row nowrap;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.25em;
+
+ & > a {
+ display: flex;
+ flex-flow: row nowrap;
+ align-items: center;
+
+ span {
+ font-weight: bold;
+ margin-left: 0.5em;
+ max-width: 200px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+
+ & > ul {
+ font-weight: lighter;
+ font-size: 0.75em;
+ list-style: none;
+ text-align: right;
+ }
+ }
+}
diff --git a/conversejs/custom/plugins/mam-search/templates/muc-mam-search-app.js b/conversejs/custom/plugins/mam-search/templates/muc-mam-search-app.js
new file mode 100644
index 00000000..c4ee4371
--- /dev/null
+++ b/conversejs/custom/plugins/mam-search/templates/muc-mam-search-app.js
@@ -0,0 +1,63 @@
+// SPDX-FileCopyrightText: 2024 John Livingston
+//
+// SPDX-License-Identifier: AGPL-3.0-only
+
+import { converseLocalizedHelpUrl } from '../../../shared/lib/help'
+import { tplMUCApp } from '../../../shared/components/muc-app/templates/muc-app.js'
+import { html } from 'lit'
+import { repeat } from 'lit/directives/repeat.js'
+import { __ } from 'i18n'
+
+function tplContent (el, mucModel, occupantModel) {
+ return html`
+ ${
+ occupantModel
+ ? html`
+
+ `
+ : ''
+ }
+ ${
+ el.results
+ ? repeat(el.results, (message) => message.id, message => tplMessage(message))
+ : html``
+ }
+ `
+}
+
+function tplMessage (model) {
+ return html`
+ `
+}
+
+export function tplMamSearchApp (el, mucModel, occupantModel) {
+ if (!mucModel) {
+ // should not happen
+ return html``
+ }
+
+ if (!el.show) {
+ return html``
+ }
+
+ // eslint-disable-next-line no-undef
+ const i18nSearch = __(LOC_message_search)
+ // eslint-disable-next-line no-undef
+ const i18nHelp = __(LOC_online_help)
+ const helpUrl = converseLocalizedHelpUrl({
+ page: 'documentation/user/streamers/moderation'
+ })
+
+ return tplMUCApp(
+ el,
+ i18nSearch,
+ helpUrl,
+ i18nHelp,
+ tplContent(el, mucModel, occupantModel)
+ )
+}
diff --git a/conversejs/custom/plugins/mam-search/templates/muc-mam-search-occupant.js b/conversejs/custom/plugins/mam-search/templates/muc-mam-search-occupant.js
new file mode 100644
index 00000000..408dcb18
--- /dev/null
+++ b/conversejs/custom/plugins/mam-search/templates/muc-mam-search-occupant.js
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: 2024 John Livingston
+//
+// SPDX-License-Identifier: AGPL-3.0-only
+
+import { html } from 'lit'
+import { api } from '@converse/headless'
+import { getAuthorStyle } from '../../../../src/utils/color.js'
+import { __ } from 'i18n'
+
+export function tplMucMamSearchOccupant (el, occupant) {
+ const authorStyle = getAuthorStyle(occupant)
+ const jid = occupant.get('jid')
+ const occupantId = occupant.get('occupant_id')
+
+ return html`
+ {
+ api.modal.show('converse-muc-occupant-modal', { model: occupant }, ev)
+ }}>
+
+
+ ${occupant.getDisplayName()}
+
+
+ ${jid ? html`- ${jid}
` : ''}
+ ${occupantId ? html`- ${occupantId}
` : ''}
+
`
+}
diff --git a/conversejs/custom/plugins/mam-search/utils.js b/conversejs/custom/plugins/mam-search/utils.js
new file mode 100644
index 00000000..baaf7788
--- /dev/null
+++ b/conversejs/custom/plugins/mam-search/utils.js
@@ -0,0 +1,57 @@
+// SPDX-FileCopyrightText: 2024 John Livingston
+//
+// SPDX-License-Identifier: AGPL-3.0-only
+
+import { api } from '../../../src/headless/index.js'
+import { XMLNS_MAM_SEARCH } from './constants.js'
+import { __ } from 'i18n'
+
+function getMessageActionButtons (messageActionsEl, buttons) {
+ const messageModel = messageActionsEl.model
+ if (!api.settings.get('livechat_mam_search_app_enabled')) {
+ return buttons
+ }
+
+ if (messageModel.get('type') !== 'groupchat') {
+ // only on groupchat message.
+ return buttons
+ }
+
+ if (!messageModel.occupant) {
+ return buttons
+ }
+
+ const muc = messageModel.collection?.chatbox
+ if (!muc) {
+ return buttons
+ }
+
+ if (!muc.features?.get?.(XMLNS_MAM_SEARCH)) {
+ return buttons
+ }
+
+ const myself = muc.getOwnOccupant()
+ if (!myself || !['admin', 'owner'].includes(myself.get('affiliation'))) {
+ return buttons
+ }
+
+ // eslint-disable-next-line no-undef
+ const i18nSearch = __(LOC_search_occupant_message)
+
+ buttons.push({
+ i18n_text: i18nSearch,
+ handler: async (ev) => {
+ ev.preventDefault()
+ api.livechat_mam_search.showMessagesFrom(messageModel.occupant)
+ },
+ button_class: '',
+ icon_class: 'fa fa-magnifying-glass',
+ name: 'muc-mam-search'
+ })
+
+ return buttons
+}
+
+export {
+ getMessageActionButtons
+}
diff --git a/conversejs/custom/templates/muc-chatarea.js b/conversejs/custom/templates/muc-chatarea.js
index 6980b17f..d2067c4e 100644
--- a/conversejs/custom/templates/muc-chatarea.js
+++ b/conversejs/custom/templates/muc-chatarea.js
@@ -18,5 +18,10 @@ export default (o) => {
? html``
: ''
}
+ ${
+ o?.model && api.settings.get('livechat_mam_search_app_enabled')
+ ? html``
+ : ''
+ }
${tplMUCChatarea(o)}`
}
diff --git a/conversejs/loc.keys.js b/conversejs/loc.keys.js
index e761bdc6..8cdd6edc 100644
--- a/conversejs/loc.keys.js
+++ b/conversejs/loc.keys.js
@@ -60,7 +60,8 @@ const locKeys = [
'moderator_note_search_for_participant',
'moderator_note_filters',
'moderator_note_original_nick',
- 'search_occupant_message'
+ 'search_occupant_message',
+ 'message_search'
]
module.exports = locKeys
diff --git a/languages/en.yml b/languages/en.yml
index 4db677b6..47c850d4 100644
--- a/languages/en.yml
+++ b/languages/en.yml
@@ -605,4 +605,5 @@ moderator_note_search_for_participant: 'Search notes about this participant'
moderator_note_filters: 'Search filters'
moderator_note_original_nick: 'Nickname of the participant at the time of the note creation'
-search_occupant_message: 'Search all message from this participant'
+search_occupant_message: 'Search all messages from this participant'
+message_search: 'Message search'
\ No newline at end of file