Merge pull request #2772 from nextcloud/enh/card-actions

Add API to register card actions
This commit is contained in:
Julius Härtl
2021-02-15 09:59:36 +01:00
committed by GitHub
5 changed files with 103 additions and 2 deletions

View File

@@ -33,10 +33,17 @@
<ActionButton v-if="cardDetailsInModal" icon="icon-menu-sidebar" @click.stop="showModal()"> <ActionButton v-if="cardDetailsInModal" icon="icon-menu-sidebar" @click.stop="showModal()">
{{ t('deck', 'Open in sidebar view') }} {{ t('deck', 'Open in sidebar view') }}
</ActionButton> </ActionButton>
<ActionButton v-else icon="icon-external" @click.stop="showModal()"> <ActionButton v-else icon="icon-external" @click.stop="showModal()">
{{ t('deck', 'Open in bigger view') }} {{ t('deck', 'Open in bigger view') }}
</ActionButton> </ActionButton>
<ActionButton v-for="action in cardActions"
:key="action.label"
:close-after-click="true"
:icon="action.icon"
@click="action.callback(cardRichObject)">
{{ action.label }}
</ActionButton>
</template> </template>
<AppSidebarTab id="details" <AppSidebarTab id="details"
@@ -73,6 +80,7 @@
<script> <script>
import { ActionButton, AppSidebar, AppSidebarTab } from '@nextcloud/vue' import { ActionButton, AppSidebar, AppSidebarTab } from '@nextcloud/vue'
import { generateUrl } from '@nextcloud/router'
import { mapState, mapGetters } from 'vuex' import { mapState, mapGetters } from 'vuex'
import CardSidebarTabDetails from './CardSidebarTabDetails' import CardSidebarTabDetails from './CardSidebarTabDetails'
import CardSidebarTabAttachments from './CardSidebarTabAttachments' import CardSidebarTabAttachments from './CardSidebarTabAttachments'
@@ -114,7 +122,7 @@ export default {
currentBoard: state => state.currentBoard, currentBoard: state => state.currentBoard,
cardDetailsInModal: state => state.cardDetailsInModal, cardDetailsInModal: state => state.cardDetailsInModal,
}), }),
...mapGetters(['canEdit', 'assignables']), ...mapGetters(['canEdit', 'assignables', 'cardActions', 'stackById']),
title() { title() {
return this.titleEditable ? this.titleEditing : this.currentCard.title return this.titleEditable ? this.titleEditing : this.currentCard.title
}, },
@@ -124,6 +132,15 @@ export default {
subtitle() { subtitle() {
return t('deck', 'Modified') + ': ' + this.relativeDate(this.currentCard.lastModified * 1000) + ' ' + t('deck', 'Created') + ': ' + this.relativeDate(this.currentCard.createdAt * 1000) return t('deck', 'Modified') + ': ' + this.relativeDate(this.currentCard.lastModified * 1000) + ' ' + t('deck', 'Created') + ': ' + this.relativeDate(this.currentCard.createdAt * 1000)
}, },
cardRichObject() {
return {
id: '' + this.currentCard.id,
name: this.currentCard.title,
boardname: this.currentBoard.title,
stackname: this.stackById(this.currentCard.stackId)?.title,
link: window.location.protocol + '//' + window.location.host + generateUrl('/apps/deck/') + `#/board/${this.currentBoard.id}/card/${this.currentCard.id}`,
}
},
}, },
methods: { methods: {
handleUpdateTitleEditable(value) { handleUpdateTitleEditable(value) {

View File

@@ -103,3 +103,40 @@ new Vue({
}, },
render: h => h(App), render: h => h(App),
}) })
if (!window.OCA.Deck) {
window.OCA.Deck = {}
}
/**
* @typedef {Object} CardRichObject
* @property {string} id
* @property {string} name
* @property {string} boardname
* @property {string} stackname
* @property {string} link
*/
/**
* @callback registerActionCallback
* @param {CardRichObject} card
*/
/**
* Frontend message API for adding actions to talk messages.
* @param {*} Object the wrapping object.
* @param {String} label the action label.
* @param {registerActionCallback} callback the callback function. This function will receive
* the card as a parameter and be triggered by a click on the
* action. The card parameter will be of the format of a rich object string
* type "deck-card"
* @param {String} icon the action label. E.g. "icon-reply"
*/
window.OCA.Deck.registerCardAction = ({ label, callback, icon }) => {
const cardAction = {
label,
callback,
icon,
}
store.dispatch('addCardAction', cardAction)
}

42
src/store/actions.js Normal file
View File

@@ -0,0 +1,42 @@
/*
* @copyright Copyright (c) 2021 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @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/>.
*
*/
export default {
state: {
actions: {
card: [],
},
},
getters: {
cardActions: (state) => state.actions.card,
},
mutations: {
ADD_CARD_ACTION(state, action) {
state.actions.card.push(action)
},
},
actions: {
async addCardAction({ commit }, action) {
commit('ADD_CARD_ACTION', action)
},
},
}

View File

@@ -28,6 +28,7 @@ import Vuex from 'vuex'
import axios from '@nextcloud/axios' import axios from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router' import { generateOcsUrl } from '@nextcloud/router'
import { BoardApi } from '../services/BoardApi' import { BoardApi } from '../services/BoardApi'
import actions from './actions'
import stack from './stack' import stack from './stack'
import card from './card' import card from './card'
import comment from './comment' import comment from './comment'
@@ -47,6 +48,7 @@ export const BOARD_FILTERS = {
export default new Vuex.Store({ export default new Vuex.Store({
modules: { modules: {
actions,
stack, stack,
card, card,
comment, comment,

View File

@@ -34,6 +34,9 @@ export default {
stacksByBoard: state => (id) => { stacksByBoard: state => (id) => {
return state.stacks.filter((stack) => stack.boardId === id).sort((a, b) => a.order - b.order) return state.stacks.filter((stack) => stack.boardId === id).sort((a, b) => a.order - b.order)
}, },
stackById: state => (id) => {
return state.stacks.find((stack) => stack.id === id)
},
}, },
mutations: { mutations: {
addStack(state, stack) { addStack(state, stack) {