diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 9f5f6f571ea..87847d0768d 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -116,6 +116,7 @@
use OCA\Talk\Search\CurrentMessageSearch;
use OCA\Talk\Search\MessageSearch;
use OCA\Talk\Search\UnifiedSearchCSSLoader;
+use OCA\Talk\Search\UnifiedSearchFilterPlugin;
use OCA\Talk\Settings\Personal;
use OCA\Talk\Share\Listener as ShareListener;
use OCA\Talk\Signaling\Listener as SignalingListener;
@@ -175,6 +176,7 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(\OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent::class, UnifiedSearchCSSLoader::class);
$context->registerEventListener(\OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent::class, DeckPluginLoader::class);
$context->registerEventListener(\OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent::class, MapsPluginLoader::class);
+ $context->registerEventListener(\OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent::class, UnifiedSearchFilterPlugin::class);
$context->registerEventListener(RegisterOperationsEvent::class, RegisterOperationsListener::class);
$context->registerEventListener(BeforeTemplateRenderedEvent::class, PublicShareTemplateLoader::class);
$context->registerEventListener(BeforeTemplateRenderedEvent::class, PublicShareAuthTemplateLoader::class);
diff --git a/lib/Search/UnifiedSearchFilterPlugin.php b/lib/Search/UnifiedSearchFilterPlugin.php
new file mode 100644
index 00000000000..920942e699c
--- /dev/null
+++ b/lib/Search/UnifiedSearchFilterPlugin.php
@@ -0,0 +1,53 @@
+
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+namespace OCA\Talk\Search;
+
+use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\IRequest;
+use OCP\Util;
+
+/**
+ * @template-implements IEventListener
+ */
+class UnifiedSearchFilterPlugin implements IEventListener {
+
+ public function __construct(
+ private IRequest $request,
+ ) {
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof BeforeTemplateRenderedEvent)) {
+ return;
+ }
+
+ if (!$event->isLoggedIn()) {
+ return;
+ }
+
+ Util::addScript('spreed', 'talk-search');
+ }
+}
diff --git a/src/search.js b/src/search.js
new file mode 100644
index 00000000000..55c9dcb3da2
--- /dev/null
+++ b/src/search.js
@@ -0,0 +1,100 @@
+/*
+ * @copyright Copyright (c) 2024 Fon E. Noel NFEBE
+ *
+ * @author Vincent Petry
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+import Vue from 'vue'
+
+import { getRequestToken } from '@nextcloud/auth'
+import { emit } from '@nextcloud/event-bus'
+import { translate, translatePlural } from '@nextcloud/l10n'
+import { generateFilePath, imagePath } from '@nextcloud/router'
+
+import '@nextcloud/dialogs/style.css'
+
+(function(OC, OCP, t, n) {
+
+ /**
+ *
+ */
+ function init() {
+ if (!OCP.UnifiedSearch) {
+ return
+ }
+ console.debug('Initializing unified search plugin-filters from talk')
+ OCP.UnifiedSearch.registerFilterAction({
+ id: 'talk-message',
+ label: t('spreed', 'In conversation'),
+ icon: imagePath('spreed', 'app.svg'),
+ callback: () => {
+ const container = document.createElement('div')
+ container.id = 'spreed-unified-search-conversation-select'
+ const body = document.getElementById('body-user')
+ body.appendChild(container)
+
+ const RoomSelector = () => import('./components/RoomSelector.vue')
+ const vm = new Vue({
+ el: container,
+ render: h => h(RoomSelector, {
+ props: {
+ dialogTitle: t('spreed', 'Select conversation'),
+ isPlugin: true,
+ },
+ }),
+ })
+
+ vm.$root.$on('close', () => {
+ vm.$el.remove()
+ vm.$destroy()
+ })
+ vm.$root.$on('select', (conversation) => {
+ vm.$el.remove()
+ vm.$destroy()
+
+ emit('nextcloud:unified-search:add-filter', {
+ id: 'talk-message',
+ payload: conversation,
+ filterUpdateText: t('spreed', 'Search in conversation: {conversation}', { conversation: conversation.displayName }),
+ filterParams: { conversation: conversation.token }
+ })
+ })
+ },
+ })
+ }
+
+ // CSP config for webpack dynamic chunk loading
+ // eslint-disable-next-line
+ __webpack_nonce__ = btoa(getRequestToken())
+
+ // Correct the root of the app for chunk loading
+ // OC.linkTo matches the apps folders
+ // OC.generateUrl ensure the index.php (or not)
+ // We do not want the index.php since we're loading files
+ // eslint-disable-next-line
+ __webpack_public_path__ = generateFilePath('spreed', '', 'js/')
+
+ Vue.prototype.t = translate
+ Vue.prototype.n = translatePlural
+ Vue.prototype.OC = OC
+ Vue.prototype.OCP = OCP
+
+ document.addEventListener('DOMContentLoaded', init)
+
+})(window.OC, window.OCP, t, n)
diff --git a/webpack.config.js b/webpack.config.js
index da6e7401590..4f5bf9bdd97 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -32,6 +32,7 @@ module.exports = mergeWithRules({
dashboard: path.join(__dirname, 'src', 'dashboard.js'),
deck: path.join(__dirname, 'src', 'deck.js'),
maps: path.join(__dirname, 'src', 'maps.js'),
+ search: path.join(__dirname, 'src', 'search.js'),
},
output: {