summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2019-03-29 10:03:03 +0100
committerJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2019-03-29 10:03:03 +0100
commit80ea98fdab10d08b3d31d3b00129cb49512b24fe (patch)
treeebf69fcfd18efe52e4d1b4b302915f7114b596e3 /src
parent0444744e0b4432406bf32dcb7ab7492dfe97cd70 (diff)
Allow to pick avatar from files + use modal
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/components/ContactDetails.vue5
-rw-r--r--src/components/ContactDetails/ContactDetailsAvatar.vue151
-rw-r--r--src/main.js6
3 files changed, 138 insertions, 24 deletions
diff --git a/src/components/ContactDetails.vue b/src/components/ContactDetails.vue
index 75014703..ac1ff0b1 100644
--- a/src/components/ContactDetails.vue
+++ b/src/components/ContactDetails.vue
@@ -134,8 +134,6 @@ import PQueue from 'p-queue'
import qr from 'qr-image'
import { stringify } from 'ical.js'
-import { Modal } from 'nextcloud-vue'
-
import rfcProps from 'Models/rfcProps'
import validate from 'Services/validate'
@@ -157,8 +155,7 @@ export default {
PropertyGroups,
PropertyRev,
AddNewProp,
- ContactAvatar,
- Modal
+ ContactAvatar
},
props: {
diff --git a/src/components/ContactDetails/ContactDetailsAvatar.vue b/src/components/ContactDetails/ContactDetailsAvatar.vue
index 54636f17..7557cec2 100644
--- a/src/components/ContactDetails/ContactDetailsAvatar.vue
+++ b/src/components/ContactDetails/ContactDetailsAvatar.vue
@@ -22,25 +22,39 @@
-->
<template>
- <div :class="{'maximised':maximizeAvatar }" class="contact-header-avatar">
+ <div class="contact-header-avatar">
<div class="contact-header-avatar__wrapper">
- <div class="contact-header-avatar__background" @click="toggleSize" />
+ <div class="contact-header-avatar__background" @click="toggleModal" />
<div v-if="contact.photo" :style="{ 'backgroundImage': `url(${photo})` }"
class="contact-header-avatar__photo"
- @click="toggleSize" />
- <div class="contact-header-avatar__options">
- <input id="contact-avatar-upload" type="file" class="hidden"
- accept="image/*" @change="processFile">
- <label v-if="!contact.addressbook.readOnly" v-tooltip.auto="t('contacts', 'Upload a new picture')"
- for="contact-avatar-upload" class="icon-upload-force-white" @click="processFile" />
- <div v-if="maximizeAvatar && !contact.addressbook.readOnly" class="icon-delete-force-white" @click="removePhoto" />
- <a v-if="maximizeAvatar" :href="contact.url + '?photo'" class="icon-download-force-white" />
+ @click="toggleModal" />
+
+ <div v-click-outside="closeMenu" class="contact-header-avatar__options">
+ <a v-tooltip.bottom="t('contacts', 'Add a new picture')" href="#" class="contact-avatar-options"
+ :class="loading ? 'icon-loading-small' : 'icon-picture-force-white'"
+ @click.prevent="toggleMenu" />
+ <input id="contact-avatar-upload" ref="uploadInput" type="file"
+ class="hidden" accept="image/*" @change="processFile">
+ </div>
+
+ <modal v-if="maximizeAvatar" class="contact-header-modal__photo" :actions="modalActions"
+ @close="toggleModal">
+ <img :src="photo" class="contact-header-modal__photo">
+ </modal>
+
+ <!-- out of the avatar__options because of the overflow hidden -->
+ <div :class="{ 'open': opened }" class="contact-avatar-options__popovermenu popovermenu">
+ <popover-menu :menu="actions" />
</div>
</div>
</div>
</template>
<script>
+import { pickFileOrDirectory } from 'nextcloud-server/dist/files'
+import { generateRemoteUrl } from 'nextcloud-server/dist/router'
+
+const axios = () => import('axios')
export default {
name: 'ContactAvatar',
@@ -51,9 +65,13 @@ export default {
required: true
}
},
+
data() {
return {
- maximizeAvatar: false
+ maximizeAvatar: false,
+ opened: false,
+ loading: false,
+ root: generateRemoteUrl(`dav/files/${OC.getCurrentUser().uid}`)
}
},
computed: {
@@ -65,6 +83,35 @@ export default {
return `data:image;base64,${this.contact.photo.split(',').pop()}`
}
return this.contact.photo
+ },
+ actions() {
+ return [
+ {
+ icon: 'icon-upload',
+ text: t('contacts', 'Upload a new picture'),
+ action: this.selectFileInput
+ },
+ {
+ icon: 'icon-picture',
+ text: t('contacts', 'Choose from files'),
+ action: this.selectFilePicker
+ }
+ ]
+ },
+ modalActions() {
+ return [...this.actions, ...[
+ {
+ icon: 'icon-delete',
+ text: t('contacts', 'Delete picture'),
+ action: this.removePhoto
+ },
+ {
+ icon: 'icon-download',
+ text: t('contacts', 'Download picture'),
+ href: this.contact.url + '?photo',
+ target: '_blank'
+ }
+ ]]
}
},
methods: {
@@ -74,32 +121,47 @@ export default {
* @param {Object} event the event object containing the image
*/
processFile(event) {
- if (event.target.files) {
+ if (event.target.files && !this.loading) {
+ this.closeMenu()
+
let file = event.target.files[0]
if (file && file.size && file.size <= 1 * 1024 * 1024) {
let reader = new FileReader()
let self = this
- // check if photo property exists to decide whether to add/update it
- reader.onload = function(e) {
- self.contact.photo
- ? self.contact.photo = reader.result
- : self.contact.vCard.addPropertyWithValue('photo', reader.result)
- self.$store.dispatch('updateContact', self.contact)
+ reader.onload = function(e) {
+ self.setPhoto(reader.result)
}
+
reader.readAsDataURL(file)
} else {
OC.Notification.showTemporary(t('contacts', 'Image is too big (max 1MB).'))
// reset input
event.target.value = ''
+ this.loading = false
}
}
},
/**
+ * Update the contact photo
+ *
+ * @param {String} value the photo as base64
+ */
+ setPhoto(value) {
+ // check if photo property exists to decide whether to add/update it
+ this.contact.photo
+ ? this.contact.photo = value
+ : this.contact.vCard.addPropertyWithValue('photo', value)
+
+ this.$store.dispatch('updateContact', this.contact)
+ this.loading = false
+ },
+
+ /**
* Toggle the full image preview
*/
- toggleSize() {
+ toggleModal() {
// maximise or minimise avatar photo
this.maximizeAvatar = !this.maximizeAvatar
},
@@ -111,6 +173,57 @@ export default {
this.contact.vCard.removeProperty('photo')
this.maximizeAvatar = !this.maximizeAvatar
this.$store.dispatch('updateContact', this.contact)
+ },
+
+ /**
+ * Picker handlers
+ */
+ selectFileInput() {
+ if (!this.loading) {
+ this.$refs.uploadInput.click()
+ }
+ },
+ async selectFilePicker() {
+ if (!this.loading) {
+ const file = await pickFileOrDirectory(
+ t('contacts', 'Pick an avatar'),
+ false,
+ [
+ 'image/png',
+ 'image/jpeg',
+ 'image/gif',
+ 'image/x-xbitmap',
+ 'image/bmp',
+ 'image/svg+xml'
+ ]
+ )
+ if (file) {
+ this.loading = true
+ try {
+ const { get } = await axios()
+ const response = await get(`${this.root}${file}`, {
+ responseType: 'arraybuffer'
+ })
+ const data = `data:${response.headers['content-type']};base64,${Buffer.from(response.data, 'binary').toString('base64')}`
+ this.setPhoto(data)
+ } catch (error) {
+ OC.Notification.showTemporary(t('contacts', 'Error while processing the picture.'))
+ console.error(error)
+ this.loading = false
+ }
+ }
+ }
+ },
+
+ /**
+ * Menu handling
+ */
+ toggleMenu() {
+ // only open if not loading
+ this.opened = !this.opened ? !this.opened && !this.loading : false
+ },
+ closeMenu() {
+ this.opened = false
}
}
diff --git a/src/main.js b/src/main.js
index bc6e37a3..6b74fd5e 100644
--- a/src/main.js
+++ b/src/main.js
@@ -27,7 +27,7 @@ import { sync } from 'vuex-router-sync'
import { generateFilePath } from 'nextcloud-server/dist/router'
/** GLOBAL COMPONENTS AND DIRECTIVE */
-import { Action, DatetimePicker, Multiselect, PopoverMenu } from 'nextcloud-vue'
+import { Action, DatetimePicker, Multiselect, PopoverMenu, Modal } from 'nextcloud-vue'
import ClickOutside from 'vue-click-outside'
import { VTooltip } from 'v-tooltip'
import VueClipboard from 'vue-clipboard2'
@@ -43,12 +43,16 @@ __webpack_nonce__ = btoa(OC.requestToken)
// eslint-disable-next-line
__webpack_public_path__ = generateFilePath('contacts', '', 'js/')
+// Register global components
Vue.component('Action', Action)
Vue.component('DatetimePicker', DatetimePicker)
+Vue.component('Modal', Modal)
Vue.component('Multiselect', Multiselect)
Vue.component('PopoverMenu', PopoverMenu)
+
Vue.directive('ClickOutside', ClickOutside)
Vue.directive('Tooltip', VTooltip)
+
Vue.use(VueClipboard)
sync(store, router)