Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add sharing deck card with a conversation
Added deck action for sharing/linking that uses the RoomSelector.
Improved RoomSelector

Co-authored-by: Joas Schilling <[email protected]>
Signed-off-by: Vincent Petry <[email protected]>
  • Loading branch information
PVince81 and nickvergessen committed Feb 12, 2021
commit ddf06db9028910782c2eefe888d7eeb1eba554c3
2 changes: 2 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
54 changes: 54 additions & 0 deletions lib/Deck/DeckPluginLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 Vincent Petry <[email protected]>
*
* @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\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');
}
}
}
2 changes: 1 addition & 1 deletion src/collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
78 changes: 78 additions & 0 deletions src/deck.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* @copyright Copyright (c) 2020 Vincent Petry <[email protected]>
*
* @author Vincent Petry <[email protected]>
*
* @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/>.
*
*/

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 <a href="{link}">conversation</a>.', {
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.'))
}
})
},
})

})
25 changes: 25 additions & 0 deletions src/services/messagesService.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
}
34 changes: 16 additions & 18 deletions src/views/RoomSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
<Modal @close="close">
<div id="modal-inner" class="talk-modal" :class="{ 'icon-loading': loading }">
<div id="modal-content">
<h2>{{ t('spreed', 'Link to a conversation') }}</h2>
<h2>{{ t('spreed', 'Select a conversation') }}</h2>
<div id="room-list">
<ul v-if="!loading">
<ul v-if="!loading && availableRooms.length > 0">
<li v-for="room in availableRooms"
:key="room.token"
:class="{selected: selectedRoom === room.token }"
Expand All @@ -38,9 +38,16 @@
<span>{{ room.displayName }}</span>
</li>
</ul>
<div v-else-if="!loading">
{{ t('spreed', 'No conversations found') }}
</div>
</div>
<div id="modal-buttons">
<button v-if="!loading" class="primary" @click="select">
<button
v-if="!loading && availableRooms.length > 0"
class="primary"
:disabled="!selectedRoom"
@click="select">
{{ t('spreed', 'Select conversation') }}
</button>
</div>
Expand All @@ -53,6 +60,7 @@
import Modal from '@nextcloud/vue/dist/Components/Modal'
import axios from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router'
import { CONVERSATION } from '../constants'
import ConversationIcon from '../components/ConversationIcon'

export default {
Expand All @@ -66,26 +74,12 @@ export default {
rooms: [],
selectedRoom: null,
loading: true,
// TODO: should be included once this is properly available
types: {
ROOM_TYPE_ONE_TO_ONE: 1,
ROOM_TYPE_GROUP: 2,
ROOM_TYPE_PUBLIC: 3,
ROOM_TYPE_CHANGELOG: 4,
},
}
},
computed: {
currentRoom() {
if (OCA.SpreedMe && OCA.SpreedMe.app.activeRoom) {
return OCA.SpreedMe.app.activeRoom.get('token')
}
return null
},
availableRooms() {
return this.rooms.filter((room) => {
return room.token !== this.currentRoom
&& room.type !== this.types.ROOM_TYPE_CHANGELOG
return room.type !== CONVERSATION.TYPE.CHANGELOG
&& room.objectType !== 'file'
&& room.objectType !== 'share:password'
})
Expand Down Expand Up @@ -138,6 +132,7 @@ export default {
#room-list {
overflow-y: auto;
flex: 0 1 auto;
height: 100%;
}

li {
Expand All @@ -159,6 +154,9 @@ li {
& > span {
padding: 5px 5px 5px 10px;
vertical-align: middle;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}

Expand Down
1 change: 1 addition & 0 deletions webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module.exports = {
'talk-public-share-sidebar': path.join(__dirname, 'src', 'mainPublicShareSidebar.js'),
'flow': path.join(__dirname, 'src', 'flow.js'),
'dashboard': path.join(__dirname, 'src', 'dashboard.js'),
'deck': path.join(__dirname, 'src', 'deck.js'),
},
output: {
path: path.resolve(__dirname, './js'),
Expand Down