Skip to content

Commit

Permalink
Add attachments to events
Browse files Browse the repository at this point in the history
Signed-off-by: Mikhail Sazanov <m@sazanof.ru>
  • Loading branch information
Mikhail Sazanov committed Jun 1, 2022
1 parent 0d333a7 commit 72e07ca
Show file tree
Hide file tree
Showing 6 changed files with 480 additions and 3 deletions.
222 changes: 222 additions & 0 deletions src/components/Editor/Attachments/AttachmentsList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
<template>
<div id="attachments">
<input ref="localAttachments"
type="file"
multiple
style="display:none"
@change="onLocalAttachmentSelected">
<div v-if="attachments.length > 0">
<ul>
<ListItem v-for="attachment in attachments"
:key="attachment.value"
:force-display-actions="true"
:title="attachment.fileName">
<template #icon>
<Paperclip :size="24" />
</template>
<template #subtitle>
{{ attachment.formatType }}
</template>
<template #actions>
<ActionLink :href="attachment.value" target="_blank">
<template #icon>
<Download :size="20" />
</template>
{{ t('calendar', 'View file') }}
</ActionLink>
<ActionButton @click="deleteAttachmentFromEvent(attachment)">
<template #icon>
<TrashCan :size="20" />
</template>
{{ t('calendar', 'Delete file') }}
</ActionButton>
</template>
</ListItem>
</ul>
<div class="actions-absolute">
<Actions>
<ActionButton
@click="openFilesModal()">
<template #icon>
<Folder :size="20" />
</template>
{{ t('calendar', 'Choose') }}
</ActionButton>
<ActionButton @click="clickOnUploadButton">
<template #icon>
<Download :size="20" />
</template>
{{ t('calendar', 'Upload') }}
</ActionButton>
</Actions>
</div>
</div>

<EmptyContent v-else>
<CloudDownloadOutline :size="80" />
<template #desc>
<p>{{ t('calendar', 'This event has no attachments.') }}</p>
<div class="button-group">
<Button
@click="openFilesModal()"
type="primary">
<template #icon>
<Folder :size="20" />
</template>
{{ t('calendar', 'Choose') }}
</Button>
<Button
@click="clickOnUploadButton">
<template #icon>
<Download :size="20" />
</template>
{{ t('calendar', 'Upload') }}
</Button>
</div>
</template>
</EmptyContent>
</div>
</template>

<script>
import ListItem from '@nextcloud/vue/dist/Components/ListItem'
import Actions from '@nextcloud/vue/dist/Components/Actions'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import ActionLink from '@nextcloud/vue/dist/Components/ActionLink'
import Button from '@nextcloud/vue/dist/Components/Button'
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
import Paperclip from 'vue-material-design-icons/Paperclip'
import Download from 'vue-material-design-icons/Download'
import TrashCan from 'vue-material-design-icons/TrashCan'
import CloudDownloadOutline from 'vue-material-design-icons/CloudDownloadOutline'
import Folder from 'vue-material-design-icons/Folder'
import { getFilePickerBuilder } from '@nextcloud/dialogs'
import { getRequestToken } from '@nextcloud/auth'
import { shareFile, shareWith, uploadLocalAttachment } from './../../../services/attachmentService'
export default {
name: 'AttachmentsList',
components: {
ListItem,
Actions,
ActionButton,
ActionLink,
Button,
EmptyContent,
Paperclip,
Download,
TrashCan,
Folder,
CloudDownloadOutline,
},
props: {
calendarObjectInstance: {
type: Object,
required: true,
},
isReadOnly: {
type: Boolean,
default: true,
},
},
data(){
return {
uploading: false,
}
},
computed: {
attachments(){
return this.calendarObjectInstance.attachments
}
},
methods: {
addAttachmentBySharedData(calendarObjectInstance, sharedData){
this.$store.commit('addAttachmentBySharedData', {
calendarObjectInstance: calendarObjectInstance,
sharedData: sharedData,
})
},
addAttachmentByLocalFile(calendarObjectInstance, file){
console.log('input change')
this.$store.commit('addAttachmentByLocalFile', {
calendarObjectInstance: calendarObjectInstance,
file: file,
})
},
deleteAttachmentFromEvent(attachment) {
console.log('deleteAttachmentFromEvent',attachment)
this.$store.commit('deleteAttachment', {
calendarObjectInstance: this.calendarObjectInstance,
attachment: attachment,
})
},
async openFilesModal(){
const picker = getFilePickerBuilder(t('mail', 'Choose a file to add as attachment')).setMultiSelect(false).build()
try{
const path = await picker.pick(t('mail', 'Choose a file to share as a link'))
const sharedData = await shareFile(path, getRequestToken())
this.addAttachmentBySharedData(this.calendarObjectInstance, sharedData)
}
catch(error){
}
},
clickOnUploadButton(){
this.$refs.localAttachments.click()
},
async onLocalAttachmentSelected(e) {
//const toUpload = sumBy(prop('size'), Object.values(e.target.files))
const attachments = await uploadLocalAttachment(e, this.$store.getters.getCurrentUserPrincipal.dav, this.attachments).then(
attachments => {
attachments.forEach(async attachment => {
console.log(attachment)
this.addAttachmentByLocalFile(this.calendarObjectInstance, attachment)
//const sharedData = await shareFile(attachment.path, getRequestToken())
})
}
)
e.target.value = ''
}
},
}
</script>

<style lang="scss" scoped>
.actions-absolute {
position: sticky;
left: 0;
bottom: 55px;
right: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
}
#attachments .empty-content {
margin-top: 1rem;
text-align: center;
}
.button-group {
display: flex;
align-content: center;
justify-content: center;
button:first-child {
margin-right: 6px;
}
}
</style>
61 changes: 61 additions & 0 deletions src/models/attachment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* @copyright Copyright (c) 2022 Mikhail Sazanov
*
* @author Mikhail Sazanov <m@sazanof.ru>
*
* @license AGPL-3.0-or-later
*
* 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/>.
*
*/

/**
* Creates a complete attachment object based on given props
*
* @param {object} props The attachment properties already provided
* @return {object}
*/
const getDefaultAttachmentObject = (props = {}) => Object.assign({}, {
// The calendar-js attachment property
attachmentProperty: null,
// The file name of the attachment
fileName: null,
// The attachment mime type
formatType: null,
// The uri of the attachment
uri: null,
// The value from calendar object
value: null,
}, props)

/**
* Maps a calendar-js attachment property to our attachment object
*
* @param {attachmentProperty} attachmentProperty The calendar-js attachmentProperty to turn into a attachment object
* @return {object}
*/
const mapAttachmentPropertyToAttchmentObject = (attachmentProperty) => {
return getDefaultAttachmentObject({
attachmentProperty,
fileName: typeof attachmentProperty._parameters.get('FILENAME') !== 'undefined' ? attachmentProperty._parameters.get('FILENAME').value : null,
formatType: attachmentProperty.formatType,
uri: attachmentProperty.uri,
value: attachmentProperty.value,
})
}

export {
getDefaultAttachmentObject,
mapAttachmentPropertyToAttchmentObject,
}
11 changes: 11 additions & 0 deletions src/models/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { DurationValue } from '@nextcloud/calendar-js'
import { getHexForColorName } from '../utils/color.js'
import { mapAlarmComponentToAlarmObject } from './alarm.js'
import { mapAttendeePropertyToAttendeeObject } from './attendee.js'
import { mapAttachmentPropertyToAttchmentObject } from './attachment.js'
import {
getDefaultRecurrenceRuleObject,
mapRecurrenceRuleValueToRecurrenceRuleObject,
Expand Down Expand Up @@ -85,6 +86,8 @@ const getDefaultEventObject = (props = {}) => Object.assign({}, {
customColor: null,
// Categories
categories: [],
// Attachments of this event
attachments: [],
}, props)

/**
Expand Down Expand Up @@ -154,6 +157,14 @@ const mapEventComponentToEventObject = (eventComponent) => {
eventObject.attendees.push(mapAttendeePropertyToAttendeeObject(attendee))
}

/**
* Extract attachments
*/

for (const attachment of eventComponent.getPropertyIterator('ATTACH')) {
eventObject.attachments.push(mapAttachmentPropertyToAttchmentObject(attachment))
}

/**
* Extract recurrence-rule
*/
Expand Down
Loading

0 comments on commit 72e07ca

Please sign in to comment.