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: {