From d599e159abd4fae8c333b16efce5387f214b6d59 Mon Sep 17 00:00:00 2001
From: fenn-cs <fenn25.fn@gmail.com>
Date: Sat, 10 Feb 2024 22:55:34 +0100
Subject: [PATCH] Feat: Plug-in talk specific filters for unified search

Signed-off-by: fenn-cs <fenn25.fn@gmail.com>
---
 lib/AppInfo/Application.php              |   2 +
 lib/Search/UnifiedSearchFilterPlugin.php |  53 ++++++++++++
 src/search.js                            | 103 +++++++++++++++++++++++
 webpack.config.js                        |   1 +
 4 files changed, 159 insertions(+)
 create mode 100644 lib/Search/UnifiedSearchFilterPlugin.php
 create mode 100644 src/search.js

diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 9f5f6f571ea0..87847d0768df 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 000000000000..920942e699c5
--- /dev/null
+++ b/lib/Search/UnifiedSearchFilterPlugin.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2024 Fon E. Noel NFEBE <me@nfebe.com>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+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<Event>
+ */
+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 000000000000..87e8e3030802
--- /dev/null
+++ b/src/search.js
@@ -0,0 +1,103 @@
+/*
+ * @copyright Copyright (c) 2024 Fon E. Noel NFEBE <me@nfebe.com>
+ *
+ * @author Vincent Petry <me@nfebe.com>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+import Vue from 'vue'
+
+import { getRequestToken } from '@nextcloud/auth'
+import { emit } from '@nextcloud/event-bus'
+import { translate, translatePlural } from '@nextcloud/l10n'
+import { generateFilePath } from '@nextcloud/router'
+
+import RoomSelector from './components/RoomSelector.vue'
+
+import { fetchConversation } from './services/conversationsService.js'
+
+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: '/apps/spreed/img/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 ComponentVM = Vue.extend(RoomSelector)
+				const vm = new ComponentVM({
+					el: container,
+					propsData: {
+						dialogTitle: t('spreed', 'Select conversation'),
+					},
+				})
+
+				vm.$root.$on('close', () => {
+					vm.$el.remove()
+					vm.$destroy()
+				})
+				vm.$root.$on('select', (token) => {
+					vm.$el.remove()
+					vm.$destroy()
+					fetchConversation(token).then(response => {
+						const conversation = response.data.ocs.data
+						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 da6e74015903..4f5bf9bdd971 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: {