diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 819cb4cc48bf..56a25854ab92 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -38,6 +38,7 @@
use OCA\Talk\Dashboard\TalkWidget;
use OCA\Talk\Events\ChatEvent;
use OCA\Talk\Events\RoomEvent;
+use OCA\Talk\Deck\DeckPluginLoader;
use OCA\Talk\Files\Listener as FilesListener;
use OCA\Talk\Files\TemplateLoader as FilesTemplateLoader;
use OCA\Talk\Flow\Operation;
@@ -99,6 +100,7 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(BeforeTemplateRenderedEvent::class, PublicShareAuthTemplateLoader::class);
$context->registerEventListener(\OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent::class, UnifiedSearchCSSLoader::class);
$context->registerEventListener(UserChangedEvent::class, UserDisplayNameListener::class);
+ $context->registerEventListener(\OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent::class, DeckPluginLoader::class);
$context->registerSearchProvider(ConversationSearch::class);
$context->registerSearchProvider(CurrentMessageSearch::class);
diff --git a/lib/Deck/DeckPluginLoader.php b/lib/Deck/DeckPluginLoader.php
new file mode 100644
index 000000000000..7fd27226aeb8
--- /dev/null
+++ b/lib/Deck/DeckPluginLoader.php
@@ -0,0 +1,54 @@
+
+ *
+ * @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\Deck;
+
+use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\IRequest;
+use OCP\Util;
+
+class DeckPluginLoader implements IEventListener {
+ /** @var IRequest */
+ private $request;
+
+ public function __construct(IRequest $request) {
+ $this->request = $request;
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof BeforeTemplateRenderedEvent)) {
+ return;
+ }
+
+ if (!$event->isLoggedIn()) {
+ return;
+ }
+
+ if (strpos($this->request->getPathInfo(), '/apps/deck') === 0) {
+ Util::addScript('spreed', 'collections');
+ Util::addScript('spreed', 'deck');
+ }
+ }
+}
diff --git a/lib/Listener/BeforeTemplateRenderedListener.php b/lib/Listener/BeforeTemplateRenderedListener.php
new file mode 100644
index 000000000000..c3aa084e23f3
--- /dev/null
+++ b/lib/Listener/BeforeTemplateRenderedListener.php
@@ -0,0 +1,55 @@
+
+ *
+ * @author Julius Härtl
+ *
+ * @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 .
+ *
+ */
+
+declare(strict_types=1);
+
+
+namespace OCA\Deck\Listeners;
+
+use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\IRequest;
+use OCP\Util;
+
+class BeforeTemplateRenderedListener implements IEventListener {
+ private $request;
+
+ public function __construct(IRequest $request) {
+ $this->request = $request;
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof BeforeTemplateRenderedEvent)) {
+ return;
+ }
+
+ if (!$event->isLoggedIn()) {
+ return;
+ }
+
+ if (strpos($this->request->getPathInfo(), '/apps/deck') === 0) {
+ Util::addScript('deck', 'calendar');
+ }
+ }
+}
diff --git a/src/collections.js b/src/collections.js
index 8d6b9cd79750..27d79afd6c71 100644
--- a/src/collections.js
+++ b/src/collections.js
@@ -49,7 +49,7 @@ import RoomSelector from './views/RoomSelector'
ComponentVM.$root.$on('close', () => {
ComponentVM.$el.remove()
ComponentVM.$destroy()
- reject(new Error('User canceled resource selection'))
+ reject(new Error('User cancelled resource selection'))
})
ComponentVM.$root.$on('select', (id) => {
resolve(id)
diff --git a/src/deck.js b/src/deck.js
new file mode 100644
index 000000000000..582d71bf3a5e
--- /dev/null
+++ b/src/deck.js
@@ -0,0 +1,78 @@
+/*
+ * @copyright Copyright (c) 2020 Vincent Petry
+ *
+ * @author Vincent Petry
+ *
+ * @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 .
+ *
+ */
+
+import Vue from 'vue'
+import { generateFilePath, generateUrl } from '@nextcloud/router'
+import { getRequestToken } from '@nextcloud/auth'
+import { translate, translatePlural } from '@nextcloud/l10n'
+import { showSuccess, showError } from '@nextcloud/dialogs'
+import { postRichObjectToConversation } from './services/messagesService'
+
+// 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', function() {
+
+ if (!window.OCA.Deck) {
+ return
+ }
+
+ window.OCA.Deck.registerCardAction({
+ label: t('spreed', 'Post to a conversation'),
+ icon: 'icon-talk',
+ callback: (card) => {
+ OCP.Collaboration.trigger('room').then(async(token) => {
+ try {
+ const response = await postRichObjectToConversation(token, {
+ objectType: 'deck-card',
+ objectId: card.id,
+ metaData: JSON.stringify(card),
+ })
+ const messageId = response.data.ocs.data.id
+ const targetUrl = generateUrl('/call/{token}#message_{messageId}', { token, messageId })
+ showSuccess(t('spreed', 'Deck card has been posted to the selected conversation.', {
+ link: targetUrl,
+ }), {
+ isHTML: true,
+ })
+ } catch (exception) {
+ console.error('Error posting deck card to conversation', exception, exception.response?.status)
+ showError(t('spreed', 'An error occurred while posting deck card to conversation.'))
+ }
+ })
+ },
+ })
+
+})
diff --git a/src/services/messagesService.js b/src/services/messagesService.js
index 02d4af8fcd0c..8a2b14951b8f 100644
--- a/src/services/messagesService.js
+++ b/src/services/messagesService.js
@@ -23,6 +23,8 @@
import axios from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router'
import store from '../store/index'
+import SHA1 from 'crypto-js/sha1'
+import Hex from 'crypto-js/enc-hex'
/**
* Fetches messages that belong to a particular conversation
@@ -114,9 +116,32 @@ const deleteMessage = async function({ token, id }) {
return axios.delete(generateOcsUrl('apps/spreed/api/v1/chat', 2) + token + '/' + id)
}
+/**
+ * Post a rich object to a conversation
+ *
+ * @param {string} token conversation token
+ * @param {string} objectType object type
+ * @param {string} objectId object id
+ * @param {string} metaData JSON metadata of the rich object encoded as string
+ * @param {string} referenceId generated reference id, leave empty to generate it based on the other args
+ */
+const postRichObjectToConversation = async function(token, { objectType, objectId, metaData, referenceId }) {
+ if (!referenceId) {
+ const tempId = 'richobject-' + objectType + '-' + objectId + '-' + token + '-' + (new Date().getTime())
+ referenceId = Hex.stringify(SHA1(tempId))
+ }
+ return axios.post(generateOcsUrl('apps/spreed/api/v1', 2) + `chat/${token}/share`, {
+ objectType,
+ objectId,
+ metaData,
+ referenceId,
+ })
+}
+
export {
fetchMessages,
lookForNewMessages,
postNewMessage,
deleteMessage,
+ postRichObjectToConversation,
}
diff --git a/src/views/RoomSelector.vue b/src/views/RoomSelector.vue
index 7b0842aa685a..dbfe28713207 100644
--- a/src/views/RoomSelector.vue
+++ b/src/views/RoomSelector.vue
@@ -24,9 +24,9 @@