Skip to content

Commit

Permalink
Add sharing deck card with a conversation
Browse files Browse the repository at this point in the history
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 10, 2021
1 parent 066087b commit 685ef1d
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 19 deletions.
1 change: 1 addition & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ protected function registerCollaborationResourceProvider(IServerContainer $serve
$resourceManager->registerResourceProvider(ConversationProvider::class);
$server->getEventDispatcher()->addListener('\OCP\Collaboration\Resources::loadAdditionalScripts', function () {
\OCP\Util::addScript(self::APP_ID, 'collections');
\OCP\Util::addScript(self::APP_ID, 'deck');
});
}

Expand Down
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,
}
35 changes: 17 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,13 @@ 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.readOnly !== CONVERSATION.STATE.READ_ONLY
&& room.type !== CONVERSATION.TYPE.CHANGELOG
&& room.objectType !== 'file'
&& room.objectType !== 'share:password'
})
Expand Down Expand Up @@ -138,6 +133,7 @@ export default {
#room-list {
overflow-y: auto;
flex: 0 1 auto;
height: 100%;
}

li {
Expand All @@ -159,6 +155,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

0 comments on commit 685ef1d

Please sign in to comment.