Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
71 changes: 22 additions & 49 deletions src/collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,28 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import Vue from 'vue'
import { t } from '@nextcloud/l10n'

// eslint-disable-next-line no-unexpected-multiline
(function(OCP, OC) {
import { requestRoomSelection } from './utils/requestRoomSelection.js'

// eslint-disable-next-line
__webpack_nonce__ = btoa(OC.requestToken)
// eslint-disable-next-line
__webpack_public_path__ = OC.linkTo('spreed', 'js/')
// eslint-disable-next-line
__webpack_nonce__ = btoa(OC.requestToken)
// eslint-disable-next-line
__webpack_public_path__ = OC.linkTo('spreed', 'js/')

Vue.prototype.t = t
Vue.prototype.n = n
Vue.prototype.OC = OC

OCP.Collaboration.registerType('room', {
action: () => {
return new Promise((resolve, reject) => {
const container = document.createElement('div')
container.id = 'spreed-room-select'
const body = document.getElementById('body-user')
body.appendChild(container)

const RoomSelector = () => import('./components/RoomSelector.vue')
const ComponentVM = new Vue({
el: container,
render: h => h(RoomSelector, {
props: {
// Even if it is used from Talk the Collections menu is
// independently loaded, so the properties that depend
// on the store need to be explicitly injected.
container: window.store ? window.store.getters.getMainContainerSelector() : undefined,
isPlugin: true,
},
}),
})

ComponentVM.$root.$on('close', () => {
ComponentVM.$el.remove()
ComponentVM.$destroy()
reject(new Error('User cancelled resource selection'))
})
ComponentVM.$root.$on('select', ({ token }) => {
resolve(token)
ComponentVM.$el.remove()
ComponentVM.$destroy()
})
})
},
typeString: t('spreed', 'Link to a conversation'),
typeIconClass: 'icon-talk',
})
})(window.OCP, window.OC)
window.OCP.Collaboration.registerType('room', {
action: async () => {
const conversation = await requestRoomSelection('spreed-room-select', {
// Even if it is used from Talk the Collections menu is
// independently loaded, so the properties that depend
// on the store need to be explicitly injected.
container: window.store ? window.store.getters.getMainContainerSelector() : undefined,
})
if (!conversation) {
throw new Error('User cancelled resource selection')
}
return conversation.token
},
typeString: t('spreed', 'Link to a conversation'),
typeIconClass: 'icon-talk',
})
152 changes: 62 additions & 90 deletions src/deck.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,109 +4,81 @@
*/

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 { t } from '@nextcloud/l10n'
import { generateFilePath, generateUrl } from '@nextcloud/router'

import { postRichObjectToConversation } from './services/messagesService.ts'
import { requestRoomSelection } from './utils/requestRoomSelection.js'

import '@nextcloud/dialogs/style.css'

(function(OC, OCA, t, n) {
/**
* @param {object} card The card object given by the deck app
* @param {object} conversation The conversation object given by the RoomSelector
* @param {string} conversation.token The conversation token
* @param {string} conversation.displayName The conversation display name
*/
async function postCardToRoom(card, { token, displayName }) {
try {
const response = await postRichObjectToConversation(token, {
objectType: 'deck-card',
objectId: card.id,
metaData: JSON.stringify(card),
})
/**
* @param {object} card The card object given by the deck app
* @param {object} conversation The conversation object given by the RoomSelector
* @param {string} conversation.token The conversation token
* @param {string} conversation.displayName The conversation display name
*/
async function postCardToRoom(card, { token, displayName }) {
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 })
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 {conversation}')
.replace(/\{conversation}/g, `<a target="_blank" class="external" href="${targetUrl}">${escapeHtml(displayName)} ↗</a>`),
{
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'))
}
showSuccess(t('spreed', 'Deck card has been posted to {conversation}')
.replace(/\{conversation}/g, `<a target="_blank" class="external" href="${targetUrl}">${escapeHtml(displayName)} ↗</a>`),
{
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() {
if (!OCA.Deck) {
return
}

OCA.Deck.registerCardAction({
label: t('spreed', 'Post to a conversation'),
icon: 'icon-talk',
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 RoomSelector = () => import('./components/RoomSelector.vue')
const vm = new Vue({
el: container,
render: h => h(RoomSelector, {
props: {
dialogTitle: t('spreed', 'Post to conversation'),
showPostableOnly: true,
isPlugin: true,
},
}),
})

vm.$root.$on('close', () => {
vm.$el.remove()
vm.$destroy()
})
vm.$root.$on('select', (conversation) => {
vm.$el.remove()
vm.$destroy()

postCardToRoom(card, conversation)
})
},
})
/**
*
*/
function init() {
if (!window.OCA.Deck) {
return
}

// 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)
window.OCA.Deck.registerCardAction({
label: t('spreed', 'Post to a conversation'),
icon: 'icon-talk',
callback: async (card) => {
const conversation = await requestRoomSelection('spreed-post-card-to-room-select', {
dialogTitle: t('spreed', 'Post to conversation'),
showPostableOnly: true,
})
if (conversation) {
postCardToRoom(card, conversation)
}
},
})
}

// 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/')

document.addEventListener('DOMContentLoaded', init)
Loading