Skip to content

Commit

Permalink
Merge pull request #732 from nextcloud/properties-fixes
Browse files Browse the repository at this point in the history
Properties fixes
  • Loading branch information
skjnldsv authored Nov 19, 2018
2 parents b85852d + 85c3b1a commit 4bd0f4b
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 59 deletions.
43 changes: 40 additions & 3 deletions src/components/ContactDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@
<property-select :prop-model="addressbookModel" :value.sync="addressbook" :is-first-property="true"
:is-last-property="true" :property="{}" class="property--addressbooks property--last" />

<!-- Groups always visible -->
<property-groups :prop-model="groupsModel" :value.sync="groups" :contact="contact"
:is-read-only="isReadOnly" class="property--groups property--last" />

<!-- new property select -->
<add-new-prop v-if="!isReadOnly" :contact="contact" />
</section>
Expand Down Expand Up @@ -175,13 +179,13 @@ export default {
/**
* Warning messages
*
* @returns {Object|Boolean}
* @returns {Object|boolean}
*/
warning() {
if (!this.contact.dav) {
return {
icon: 'icon-error-white header-icon--pulse',
msg: t('contacts', 'This contact is not yet synced. Edit it to trigger a change.')
msg: t('contacts', 'This contact is not yet synced. Edit it to save it to the server.')
}
} else if (this.isReadOnly) {
return {
Expand All @@ -195,7 +199,7 @@ export default {
/**
* Conflict message
*
* @returns {String|Boolean}
* @returns {string|boolean}
*/
conflict() {
if (this.contact.conflict) {
Expand Down Expand Up @@ -283,6 +287,39 @@ export default {
}
},

/**
* Fake model to use the propertyGroups component
*
* @returns {Object}
*/
groupsModel() {
return {
readableName: t('contacts', 'Groups'),
icon: 'icon-contacts'
}
},

/**
* Usable groups object linked to the local contact
*
* @param {string[]} data An array of groups
* @returns {Array}
*/
groups: {
get: function() {
return this.contact.groups
},
set: function(data) {
let property = this.contact.vCard.getFirstProperty('categories')
if (!property) {
// Ical.js store comma separated by an Array of array of string
property = this.contact.vCard.addPropertyWithValue('categories', [data])
}
property.setValues(data)
this.updateContact()
}
},

/**
* Store getters filtered and mapped to usable object
*
Expand Down
37 changes: 31 additions & 6 deletions src/components/ContactDetails/ContactDetailsAddNewProp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,42 @@ export default {
},

computed: {
/**
* List of properties that the contact already have
*
* @returns {string[]}
*/
usedProperties() {
return this.contact.jCal[1].map(prop => prop[0])
},

/**
* List of every properties you are allowed to add
* on this contact
*
* @returns {Object[]}
*/
availableProperties() {
return Object.keys(rfcProps.properties).map(key => {
return {
id: key,
name: rfcProps.properties[key].readableName
}
}).sort((a, b) => a.name.localeCompare(b.name))
return Object.keys(rfcProps.properties)
// only allow to add multiple properties OR props that are not yet in the contact
.filter(prop => prop.multiple || this.usedProperties.indexOf(prop) === -1)
// usable array of objects
.map(key => {
return {
id: key,
name: rfcProps.properties[key].readableName
}
}).sort((a, b) => a.name.localeCompare(b.name))
}
},

methods: {
/**
* Add a new prop to the contact
*
* @param {Object} data destructuring object
* @param {string} data.id the id of the property. e.g fn
*/
addProp({ id }) {
let defaultData = rfcProps.properties[id].defaultValue
let property = this.contact.vCard.addPropertyWithValue(id, defaultData ? defaultData.value : '')
Expand Down
13 changes: 13 additions & 0 deletions src/components/ContactDetails/ContactDetailsAvatar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ export default {
}
},
methods: {
/**
* Handler to store a new photo on the current contact
*
* @param {Object} event the event object containing the image
*/
processFile(event) {
if (event.target.files) {
let file = event.target.files[0]
Expand All @@ -73,10 +78,18 @@ export default {
reader.readAsDataURL(file)
}
},

/**
* Toggle the full image preview
*/
toggleSize() {
// maximise or minimise avatar photo
this.maximizeAvatar = !this.maximizeAvatar
},

/**
* Remove the contact's picture
*/
removePhoto() {
this.contact.vCard.removeProperty('photo')
this.maximizeAvatar = !this.maximizeAvatar
Expand Down
12 changes: 3 additions & 9 deletions src/components/ContactDetails/ContactDetailsProperty.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import Contact from 'Models/contact'
import PropertyText from 'Components/Properties/PropertyText'
import PropertyMultipleText from 'Components/Properties/PropertyMultipleText'
import PropertyDateTime from 'Components/Properties/PropertyDateTime'
import propertyGroups from 'Components/Properties/PropertyGroups'
import PropertySelect from 'Components/Properties/PropertySelect'

export default {
Expand Down Expand Up @@ -68,11 +67,6 @@ export default {
computed: {
// dynamically load component based on property type
componentInstance() {
// groups
if (this.propName === 'categories') {
return propertyGroups
}

// dynamic matching
if (this.property.isMultiValue && this.propType === 'text') {
return PropertyMultipleText
Expand Down Expand Up @@ -119,7 +113,7 @@ export default {
/**
* Return the type of the prop e.g. FN
*
* @returns {String}
* @returns {string}
*/
propName() {
return this.property.name
Expand All @@ -128,7 +122,7 @@ export default {
* Return the type or property
*
* @see src/models/rfcProps
* @returns {String}
* @returns {string}
*/
propType() {
// if we have a force type set, use it!
Expand All @@ -153,7 +147,7 @@ export default {
* but make sure to include the selected one
* in the final list
*
* @returns {Array<Object>}
* @returns {Object[]}
*/
sortedModelOptions() {
if (this.propModel.options) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/ContactsList/ContactsListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default {
/**
* Is this matching the current search ?
*
* @returns {Boolean}
* @returns {boolean}
*/
matchSearch() {
if (this.searchQuery !== '') {
Expand All @@ -69,7 +69,7 @@ export default {
/**
* avatar color based on server toRgb method and the displayName
* @returns {String} the color in css format
* @returns {string} the color in css format
*/
colorAvatar() {
try {
Expand Down
26 changes: 15 additions & 11 deletions src/components/Properties/PropertyGroups.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@
<template>
<div v-if="propModel" class="grid-span-2 property">

<!-- NO title if first element for groups -->

<div class="property__row">
<div class="property__label">{{ propModel.readableName }}</div>

<!-- multiselect taggable groups with a limit to 3 groups shown -->
<multiselect v-model="localValue" :options="groups" :placeholder="t('contacts', 'Add contact in group')"
:limit="3" :multiple="true" :taggable="true"
:close-on-select="false" :readonly="isReadOnly" tag-placeholder="create"
class="property__value" @input="updateValue" @tag="validateGroup"
@select="addContactToGroup" @remove="removeContactToGroup">
:multiple="true" :taggable="true" :close-on-select="false"
:readonly="isReadOnly" :tag-width="60"
tag-placeholder="create" class="property__value"
@input="updateValue" @tag="validateGroup" @select="addContactToGroup"
@remove="removeContactToGroup">

<!-- show how many groups are hidden and add tooltip -->
<span v-tooltip.auto="formatGroupsTitle" slot="limit" class="multiselect__limit">+{{ localValue.length - 3 }}</span>
Expand Down Expand Up @@ -98,27 +101,28 @@ export default {
/**
* Debounce and send update event to parent
*/
updateValue: debounce(function(e) {
updateValue: debounce(function() {
// https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
this.$emit('update:value', this.localValue)
}, 500),

/**
* Dispatch contact addition to group
*
* @param {String} groupName the group name
* @param {string} groupName the group name
*/
addContactToGroup(groupName) {
this.$store.dispatch('addContactToGroup', {
async addContactToGroup(groupName) {
await this.$store.dispatch('addContactToGroup', {
contact: this.contact,
groupName
})
this.updateValue()
},

/**
* Dispatch contact removal from group
*
* @param {String} groupName the group name
* @param {string} groupName the group name
*/
removeContactToGroup(groupName) {
this.$store.dispatch('removeContactToGroup', {
Expand All @@ -130,8 +134,8 @@ export default {
/**
* Validate groupname and dispatch creation
*
* @param {String} groupName the group name
* @returns {Boolean}
* @param {string} groupName the group name
* @returns {boolean}
*/
validateGroup(groupName) {
// Only allow characters without vcard special chars
Expand Down
4 changes: 2 additions & 2 deletions src/components/Settings/SettingsAddressbookShare.vue
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export default {
* @param {string} data.user the userId
* @param {string} data.displayName the displayName
* @param {string} data.uri the sharing principalScheme uri
* @param {Boolean} data.isGroup is this a group ?
* @param {boolean} data.isGroup is this a group ?
*/
shareAddressbook({ user, displayName, uri, isGroup }) {
let addressbook = this.addressbook
Expand All @@ -101,7 +101,7 @@ export default {
/**
* Use the cdav client call to find matches to the query from the existing Users & Groups
*
* @param {String} query
* @param {string} query
*/
findSharee: debounce(async function(query) {
this.isLoading = true
Expand Down
3 changes: 2 additions & 1 deletion src/models/contact.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export default class Contact {
let groupsProp = this.vCard.getFirstProperty('categories')
if (groupsProp) {
return groupsProp.getValues()
.filter(group => group !== '')
}
return []
}
Expand Down Expand Up @@ -332,7 +333,7 @@ export default class Contact {
*
* @readonly
* @memberof Contact
* @returns {String[]}
* @returns {string[]}
*/
get searchData() {
return this.jCal[1].map(x => x[0] + ':' + x[3])
Expand Down
3 changes: 0 additions & 3 deletions src/models/rfcProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,6 @@ const properties = {
{ id: 'OTHER', name: t('contacts', 'Other') }
]
},
categories: {
readableName: t('contacts', 'Groups')
},
bday: {
readableName: t('contacts', 'Birthday'),
icon: 'icon-calendar-dark',
Expand Down
Loading

0 comments on commit 4bd0f4b

Please sign in to comment.