diff options
author | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2020-10-12 14:23:53 +0200 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2020-10-20 09:02:22 +0200 |
commit | fefe82209461c5a78b239935d64590a1c83b3e9c (patch) | |
tree | 0361c2b2db9fc17a025d5d08d7d6da5e6bdd8a1a /src | |
parent | 5c2eb89b0fe280eaf819b9690cf15146a81d8e0a (diff) |
Fix avatar display on read-only contacts and use global Avatar component
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/components/ContactDetails/ContactDetailsAvatar.vue | 42 | ||||
-rw-r--r-- | src/components/ContactsList/ContactsListItem.vue | 59 | ||||
-rw-r--r-- | src/models/contact.js | 24 |
3 files changed, 60 insertions, 65 deletions
diff --git a/src/components/ContactDetails/ContactDetailsAvatar.vue b/src/components/ContactDetails/ContactDetailsAvatar.vue index cd5a71b1..aa8af0e8 100644 --- a/src/components/ContactDetails/ContactDetailsAvatar.vue +++ b/src/components/ContactDetails/ContactDetailsAvatar.vue @@ -37,12 +37,20 @@ :style="{ 'backgroundImage': `url(${contact.photoUrl})` }" class="contact-header-avatar__photo" @click="toggleModal" /> + <Avatar v-else + :disable-tooltip="true" + :display-name="contact.displayName" + :is-no-user="true" + :size="75" + class="contact-header-avatar__photo" /> <!-- attention, this menu exists twice in this file --> <Actions - default-icon="icon-picture-force-white" + v-if="!isReadOnly || contact.photo" + :force-menu="true" :open.sync="opened" - class="contact-header-avatar__menu"> + class="contact-header-avatar__menu" + default-icon="icon-picture-force-white"> <template v-if="!isReadOnly"> <ActionButton icon="icon-upload" @@ -90,20 +98,18 @@ @close="toggleModal"> <!-- attention, this menu exists twice in this file --> <template #actions> - <ActionButton - v-if="!isReadOnly" - icon="icon-upload" - @click="selectFileInput"> - {{ t('contacts', 'Upload a new picture') }} - </ActionButton> - <ActionButton - v-if="!isReadOnly" - icon="icon-folder" - @click="selectFilePicker"> - {{ t('contacts', 'Choose from files') }} - </ActionButton> <template v-if="!isReadOnly"> <ActionButton + icon="icon-upload" + @click="selectFileInput"> + {{ t('contacts', 'Upload a new picture') }} + </ActionButton> + <ActionButton + icon="icon-folder" + @click="selectFilePicker"> + {{ t('contacts', 'Choose from files') }} + </ActionButton> + <ActionButton v-for="network in supportedSocial" :key="network" :icon="'icon-' + network.toLowerCase()" @@ -111,6 +117,7 @@ {{ t('contacts', 'Get from ' + network) }} </ActionButton> </template> + <!-- FIXME: the link seems to have a bigger font size than the button caption --> <ActionLink v-if="contact.photo" @@ -126,6 +133,7 @@ {{ t('contacts', 'Delete picture') }} </ActionButton> </template> + <img ref="img" :src="contact.photoUrl" class="contact-header-modal__photo"> @@ -134,6 +142,7 @@ </template> <script> +import Avatar from '@nextcloud/vue/dist/Components/Avatar' import Modal from '@nextcloud/vue/dist/Components/Modal' import Actions from '@nextcloud/vue/dist/Components/Actions' import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' @@ -153,10 +162,11 @@ export default { name: 'ContactDetailsAvatar', components: { - Modal, - Actions, ActionButton, ActionLink, + Actions, + Avatar, + Modal, }, props: { diff --git a/src/components/ContactsList/ContactsListItem.vue b/src/components/ContactsList/ContactsListItem.vue index fef0a9ca..14efc25b 100644 --- a/src/components/ContactsList/ContactsListItem.vue +++ b/src/components/ContactsList/ContactsListItem.vue @@ -11,13 +11,14 @@ class="app-content-list-item-checkbox checkbox" @keypress.enter.space.prevent.stop="toggleSelect"> <label :for="contact.key" @click.prevent.stop="toggleSelect" @keypress.enter.space.prevent.stop="toggleSelect" /> --> - <div :style="avatarStyle" class="app-content-list-item-icon"> - <!-- try to fetch the avatar only if the contact exists on the server --> - <div v-if="hasPhoto" :style="{ 'backgroundImage': avatarUrl }" class="app-content-list-item-icon__avatar" /> - <template v-else> - {{ contact.displayName | firstLetter }} - </template> - </div> + <Avatar + :disable-menu="true" + :disable-tooltip="true" + :display-name="contact.displayName" + :is-no-user="true" + :size="40" + :url="avatarUrl" + class="app-content-list-item-icon" /> <!-- contact data --> <div class="app-content-list-item-line-one"> @@ -30,13 +31,15 @@ </template> <script> +import Avatar from '@nextcloud/vue/dist/Components/Avatar' + export default { name: 'ContactsListItem', - filters: { - firstLetter(value) { - return value.charAt(0) - }, + + components: { + Avatar, }, + props: { index: { type: Number, @@ -47,6 +50,7 @@ export default { required: true, }, }, + computed: { selectedGroup() { return this.$route.params.selectedGroup @@ -60,37 +64,14 @@ export default { return window.btoa(this.contact.key).slice(0, -2) }, - hasPhoto() { - return this.contact.dav && (this.contact.dav.hasphoto || this.contact.photo) - }, - - avatarStyle() { - if (this.hasPhoto) { - return { - backgroundColor: '#fff', - // The contact photo gets cropped in a circular shape, which might look odd with transparent photos. - // This box shadow ensures that there's always a very faint edge hinting at the circle. - boxShadow: '0 0 5px rgba(0, 0, 0, 0.05) inset', - } - } - - try { - const color = this.contact.uid.toRgb() - return { - backgroundColor: `rgb(${color.r}, ${color.g}, ${color.b})`, - } - } catch (e) { - return { - backgroundColor: 'grey', - } - } - }, - avatarUrl() { if (this.contact.photo) { - return `url(${this.contact.photoUrl})` + return `${this.contact.photoUrl}` + } + if (this.contact.url) { + return `${this.contact.url}?photo` } - return `url(${this.contact.url}?photo)` + return undefined }, }, methods: { diff --git a/src/models/contact.js b/src/models/contact.js index 70802626..3b3ace90 100644 --- a/src/models/contact.js +++ b/src/models/contact.js @@ -22,6 +22,7 @@ import { v4 as uuid } from 'uuid' import ICAL from 'ical.js' +import b64toBlob from 'b64-to-blob' import store from '../store' import updateDesignSet from '../services/updateDesignSet' @@ -225,6 +226,7 @@ export default class Contact { /** * Return the photo usable url + * We cannot fetch external url because of csp policies * * @readonly * @memberof Contact @@ -232,22 +234,24 @@ export default class Contact { get photoUrl() { const photo = this.vCard.getFirstProperty('photo') const encoding = photo.getFirstParameter('encoding') - const type = photo.getFirstParameter('type') + let photoType = photo.getFirstParameter('type') + let photoB64 = this.photo const isBinary = photo.type === 'binary' || encoding === 'b' - if (photo && !this.photo.startsWith('data') && isBinary) { - // split on coma in case of any leftover base64 data and retrieve last part - // usually we come to this part when the base64 image type is unknown - return `data:image/${type};base64,${this.photo.split(',').pop()}` + if (photo && photoB64.startsWith('data') && !isBinary) { + // get the last part = base64 + photoB64 = photoB64.split(',').pop() + // 'data:image/png' => 'png' + photoType = photoB64.split(';')[0].split('/') } - // could be just an url of the already encoded `data:image...` + try { - // eslint-disable-next-line no-new - new URL(this.photo) - return this.photo + // Create blob from url + const blob = b64toBlob(photoB64, `image/${photoType}`) + return URL.createObjectURL(blob) } catch { - console.error('Invalid photo for the following contact. Ignoring...', this.contact) + console.error('Invalid photo for the following contact. Ignoring...', this.contact, { photoB64, photoType }) return false } } |