Signed-off-by: Jakob Röhrl <jakob.roehrl@web.de>
This commit is contained in:
Jakob Röhrl
2020-02-07 12:29:39 +01:00
committed by Julius Härtl
parent 27679c43bf
commit 7750621917
6 changed files with 237 additions and 5 deletions

View File

@@ -22,30 +22,98 @@
<template>
<div>
{{ card.attachments }}
<li v-for="attachment in attachments" :key="attachment.id" class="attachment">
<a class="fileicon" :style="mimetypeForAttachment(attachment)" />
<div class="details" :href="attachmentUrl(attachment)">
<div class="filename">
<span class="basename">{{ attachment.data }}</span>
</div>
<span class="filesize">{{ attachment.extendedData.filesize }}</span>
<span class="filedate">{{ attachment.createdAt }}</span>
<span class="filedate">{{ attachment.createdBy }}</span>
</div>
<Actions>
<ActionButton icon="icon-delete" @click="deleteAttachment(attachment)">
{{ t('deck', 'Delete Attachment') }}
</ActionButton>
</Actions>
</li>
<button class="icon-upload" @click="clickAddNewAttachmment()">
{{ t('deck', 'Upload attachment') }}
</button>
<input ref="localAttachments"
type="file"
style="display: none;"
@change="onLocalAttachmentSelected">
</div>
</template>
<script>
import { Actions, ActionButton } from '@nextcloud/vue'
export default {
name: 'CardSidebarTabAttachments',
components: {
Actions,
ActionButton,
},
props: {
card: {
type: Object,
default: null,
},
},
data() {
return {
}
},
computed: {
attachments() {
return this.$store.getters.attachmentsByCard(this.card.id)
},
},
created: function() {
this.$store.dispatch('fetchAttachments', this.card.id)
},
methods: {
clickAddNewAttachmment() {
this.$refs.localAttachments.click()
},
onLocalAttachmentSelected(e) {
const bodyFormData = new FormData()
bodyFormData.append('cardId', this.card.id)
bodyFormData.append('type', 'deck_file')
bodyFormData.append('file', e.target.files[0])
try {
this.$store.dispatch('createAttachment', { cardId: this.card.id, formData: bodyFormData })
} catch (e) {
console.log(e)
}
},
deleteAttachment(attachment) {
this.$store.dispatch('deleteAttachment', attachment)
},
mimetypeForAttachment(attachment) {
const url = OC.MimeType.getIconUrl(attachment.extendedData.mimetype)
const styles = {
'background-image': `url("${url}")`,
}
return styles
},
attachmentUrl(attachment) {
return OC.generateUrl(`/apps/deck/cards/${attachment.cardId}/attachment/${attachment.id}`)
},
},
}
</script>
<style scoped>
.fileicon {
display: inline-block;
min-width: 32px;
width: 32px;
height: 32px;
background-size: contain;
}
</style>

View File

@@ -34,7 +34,9 @@
<span>{{ checkListCheckedCount }}/{{ checkListCount }}</span>
</div>
<div v-if="card.attachments" class="card-files icon icon-files-dark" />
<div v-if="card.attachmentCount > 0" class="icon-attach icon icon-attach-dark">
{{ card.attachmentCount }}
</div>
<AvatarList :users="card.assignedUsers" />
</div>
@@ -99,9 +101,10 @@ export default {
.icon {
opacity: 0.5;
padding: 12px 3px;
padding: 12px 14px;
margin-right: 10px;
background-position: left;
background-size: 16px;
span {
margin-left: 18px;
}

View File

@@ -0,0 +1,68 @@
/*
* @copyright Copyright (c) 2020 Jakob Röhrl <jakob.roehrl@web.de>
*
* @author Jakob Röhrl <jakob.roehrl@web.de>
*
* @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 axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
export class AttachmentApi {
url(url) {
return generateUrl(`/apps/deck${url}`)
}
async fetchAttachments(cardId) {
const response = await axios({
method: 'GET',
url: this.url(`/cards/${cardId}/attachments`),
})
return response.data
}
async createAttachment({ cardId, formData }) {
try {
const response = await axios({
method: 'POST',
url: this.url(`/cards/${cardId}/attachment`),
data: formData,
})
return response.data
} catch (e) {
throw e
}
}
async deleteAttachment(attachment) {
await axios({
method: 'DELETE',
url: this.url(`/cards/${attachment.cardId}/attachment/${attachment.id}`),
})
}
async displayAttachment(attachment) {
const response = await axios({
method: 'GET',
url: this.url(`/cards/${attachment.cardId}/attachment/${attachment.id}`),
})
return response.data
}
}

77
src/store/attachment.js Normal file
View File

@@ -0,0 +1,77 @@
/*
* @copyright Copyright (c) 2020 Jakob Röhrl <jakob.roehrl@web.de>
*
* @author Jakob Röhrl <jakob.roehrl@web.de>
*
* @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 { AttachmentApi } from './../services/AttachmentApi'
import Vue from 'vue'
const apiClient = new AttachmentApi()
export default {
state: {
attachments: {},
},
getters: {
attachmentsByCard: state => (cardId) => {
if (typeof state.attachments[cardId] === 'undefined') {
return []
}
return state.attachments[cardId]
},
},
mutations: {
createAttachment(state, { cardId, attachments }) {
if (typeof state.attachments[cardId] === 'undefined') {
Vue.set(state.attachments, cardId, attachments)
} else {
state.attachments[cardId].push(attachments)
}
},
deleteAttachment(state, deletedAttachment) {
const existingIndex = state.attachments[deletedAttachment.cardId].findIndex(a => a.id === deletedAttachment.id)
if (existingIndex !== -1) {
state.attachments[deletedAttachment.cardId].splice(existingIndex, 1)
}
},
},
actions: {
async fetchAttachments({ commit }, cardId) {
const attachments = await apiClient.fetchAttachments(cardId)
commit('createAttachment', { cardId, attachments })
},
async createAttachment({ commit }, { cardId, formData }) {
const attachments = await apiClient.createAttachment({ cardId, formData })
commit('createAttachment', { cardId, attachments })
commit('cardIncreaseAttachmentCount', cardId)
},
async deleteAttachment({ commit }, attachment) {
await apiClient.deleteAttachment(attachment)
commit('deleteAttachment', attachment)
commit('cardDecreaseAttachmentCount', attachment.cardId)
},
},
}

View File

@@ -140,6 +140,20 @@ export default {
Vue.set(state.cards[existingIndex], property, card[property])
}
},
cardIncreaseAttachmentCount(state, cardId) {
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
if (existingIndex !== -1) {
const existingCard = state.cards.find(_card => _card.id === cardId)
Vue.set(state.cards, existingCard.attachmentCount, existingCard.attachmentCount++)
}
},
cardDecreaseAttachmentCount(state, cardId) {
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
if (existingIndex !== -1) {
const existingCard = state.cards.find(_card => _card.id === cardId)
Vue.set(state.cards, existingCard.attachmentCount, existingCard.attachmentCount--)
}
},
},
actions: {
async addCard({ commit }, card) {

View File

@@ -30,6 +30,7 @@ import stack from './stack'
import card from './card'
import comment from './comment'
import trashbin from './trashbin'
import attachment from './attachment'
Vue.use(Vuex)
@@ -48,6 +49,7 @@ export default new Vuex.Store({
card,
comment,
trashbin,
attachment,
},
strict: debug,
state: {