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..4cefbcb301ac
--- /dev/null
+++ b/lib/Search/UnifiedSearchFilterPlugin.php
@@ -0,0 +1,56 @@
+
+ *
+ * @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 {
+ Util::addScript('spreed', 'search');
+ if (!($event instanceof BeforeTemplateRenderedEvent)) {
+ return;
+ }
+
+ if (!$event->isLoggedIn()) {
+ return;
+ }
+
+ if (str_starts_with($this->request->getPathInfo(), '/apps/spreed')) {
+ Util::addScript('spreed', 'search');
+ }
+ }
+}
diff --git a/src/deck.js b/src/deck.js
index 5c333f5e3311..ce05f1c49eb9 100644
--- a/src/deck.js
+++ b/src/deck.js
@@ -20,6 +20,8 @@
*
*/
+console.debug("DECK TEST")
+
import escapeHtml from 'escape-html'
import Vue from 'vue'
diff --git a/src/search.js b/src/search.js
new file mode 100644
index 000000000000..0a5a63d3a4cf
--- /dev/null
+++ b/src/search.js
@@ -0,0 +1,136 @@
+/*
+ * @copyright Copyright (c) 2020 Vincent Petry
+ *
+ * @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 .
+ *
+ */
+
+console.debug("SEARCH IN TALK FIRST")
+
+import escapeHtml from 'escape-html'
+import Vue from 'vue'
+
+import { getRequestToken } from '@nextcloud/auth'
+import { showSuccess, showError } from '@nextcloud/dialogs'
+import { translate, translatePlural } from '@nextcloud/l10n'
+import { generateFilePath, generateUrl } from '@nextcloud/router'
+
+import RoomSelector from './components/RoomSelector.vue'
+
+import { fetchConversation } from './services/conversationsService.js'
+
+import '@nextcloud/dialogs/style.css'
+
+(function(OC, OCA, t, n) {
+ console.debug("SEARCH IN TALK")
+ /**
+ * @param {object} card The card object given by the deck app
+ * @param {string} token The conversation to post to
+ */
+ async function postCardToRoom(card, token) {
+ try {
+ const [responsePostCard, responseGetConversation] = await Promise.allSettled([
+ postRichObjectToConversation(token, {
+ objectType: 'deck-card',
+ objectId: card.id,
+ metaData: JSON.stringify(card),
+ }),
+ fetchConversation(token),
+ ])
+
+ const messageId = responsePostCard.value.data.ocs.data.id
+ const conversation = responseGetConversation.value.data.ocs.data.displayName
+ const targetUrl = generateUrl('/call/{token}#message_{messageId}', { token, messageId })
+
+ showSuccess(t('spreed', 'Deck card has been posted to {conversation}')
+ .replace(/\{conversation}/g, `${escapeHtml(conversation)} ↗`),
+ {
+ isHTML: true,
+ })
+ } catch (exception) {
+ console.error('Error posting deck card to conversation', exception, exception.response?.status)
+ if (exception.response?.status === 403) {
+ showError(t('spreed', 'No permission to post messages in this conversation'))
+ } else {
+ showError(t('spreed', 'An error occurred while posting deck card to conversation'))
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ function init() {
+ console.debug("Initializing search plugin-filters from talk")
+ if (!OCA.Core) {
+ return
+ }
+
+ OCA.Core.UnifiedSearch.registerExternalFilter({
+ id: 'inConversation',
+ label: t('spreed', 'In Conversation'),
+ name: this.label,
+ icon: '/apps/spreed/img/app.svg',
+ callback: (card) => {
+ const container = document.createElement('div')
+ container.id = 'spreed-post-card-to-room-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'),
+ showPostableOnly: true,
+ },
+ })
+
+ vm.$root.$on('close', () => {
+ vm.$el.remove()
+ vm.$destroy()
+ })
+ vm.$root.$on('select', (token) => {
+ vm.$el.remove()
+ vm.$destroy()
+
+ postCardToRoom(card, 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.OCA = OCA
+
+ document.addEventListener('DOMContentLoaded', init)
+
+})(window.OC, window.OCA, 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: {