mirror of https://github.com/nextcloud/contacts
278 lines
7.0 KiB
Vue
278 lines
7.0 KiB
Vue
<!--
|
|
- @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
|
-
|
|
- @author John Molakvoæ <skjnldsv@protonmail.com>
|
|
-
|
|
- @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/>.
|
|
-
|
|
-->
|
|
|
|
<template>
|
|
<!-- If not in the rfcProps then we don't want to display it -->
|
|
<component v-if="propModel && propType !== 'unknown'" :is="componentInstance" :select-type.sync="selectType"
|
|
:prop-model="propModel" :value.sync="value" :is-first-property="isFirstProperty"
|
|
:property="property" :is-last-property="isLastProperty" :class="{'property--last': isLastProperty}"
|
|
:contact="contact" :prop-name="propName" :prop-type="propType"
|
|
:options="sortedModelOptions"
|
|
@delete="deleteProp" />
|
|
</template>
|
|
|
|
<script>
|
|
import { Property } from 'ical.js'
|
|
import rfcProps from '../../models/rfcProps.js'
|
|
import Contact from '../../models/contact'
|
|
|
|
import PropertyText from '../Properties/PropertyText'
|
|
import PropertyMultipleText from '../Properties/PropertyMultipleText'
|
|
import PropertyDateTime from '../Properties/PropertyDateTime'
|
|
import propertyGroups from '../Properties/PropertyGroups'
|
|
import PropertySelect from '../Properties/PropertySelect'
|
|
|
|
export default {
|
|
name: 'ContactDetailsProperty',
|
|
|
|
props: {
|
|
property: {
|
|
type: Property,
|
|
default: true
|
|
},
|
|
sortedProperties: {
|
|
type: Array,
|
|
default() {
|
|
return []
|
|
}
|
|
},
|
|
index: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
contact: {
|
|
type: Contact,
|
|
default: null
|
|
}
|
|
},
|
|
|
|
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
|
|
} else if (this.propType && ['date-and-or-time', 'date-time', 'time', 'date'].indexOf(this.propType) > -1) {
|
|
return PropertyDateTime
|
|
} else if (this.propType && this.propType === 'select') {
|
|
return PropertySelect
|
|
} else if (this.propType && this.propType !== 'unknown') {
|
|
return PropertyText
|
|
}
|
|
return PropertyText
|
|
},
|
|
|
|
// rfc properties list
|
|
properties() {
|
|
return rfcProps.properties
|
|
},
|
|
fieldOrder() {
|
|
return rfcProps.fieldOrder
|
|
},
|
|
|
|
// is this the first property of its kind
|
|
isFirstProperty() {
|
|
if (this.index > 0) {
|
|
return this.sortedProperties[this.index - 1].name !== this.propName
|
|
}
|
|
return true
|
|
},
|
|
// is this the last property of its kind
|
|
isLastProperty() {
|
|
// array starts at 0, length starts at 1
|
|
if (this.index < this.sortedProperties.length - 1) {
|
|
return this.sortedProperties[this.index + 1].name !== this.propName
|
|
}
|
|
return true
|
|
},
|
|
|
|
/**
|
|
* Return the type of the prop e.g. FN
|
|
*
|
|
* @returns {String}
|
|
*/
|
|
propName() {
|
|
return this.property.name
|
|
},
|
|
/**
|
|
* Return the type or property
|
|
*
|
|
* @see src/models/rfcProps
|
|
* @returns {String}
|
|
*/
|
|
propType() {
|
|
// if we have a force type set, use it!
|
|
if (this.propModel && this.propModel.force) {
|
|
return this.propModel.force
|
|
}
|
|
return this.property.getDefaultType()
|
|
},
|
|
|
|
/**
|
|
* RFC template matching this property
|
|
*
|
|
* @see src/models/rfcProps
|
|
* @returns {Object}
|
|
*/
|
|
propModel() {
|
|
return this.properties[this.propName]
|
|
},
|
|
|
|
/**
|
|
* Remove duplicate name amongst options
|
|
* but make sure to include the selected one
|
|
* in the final list
|
|
*
|
|
* @returns {Array<Object>}
|
|
*/
|
|
sortedModelOptions() {
|
|
if (this.propModel.options) {
|
|
return this.propModel.options.reduce((list, option) => {
|
|
if (!list.find(search => search.name === option.name)) {
|
|
list.push(option)
|
|
}
|
|
return list
|
|
}, this.selectType ? [this.selectType] : [])
|
|
}
|
|
return []
|
|
},
|
|
|
|
/**
|
|
* Returns the closest match to the selected type
|
|
* or return the default selected as a new object if
|
|
* none exists
|
|
*
|
|
* @returns Object|undefined
|
|
*/
|
|
selectType: {
|
|
get() {
|
|
if (this.propModel && this.propModel.options && this.type) {
|
|
|
|
let selectedType = this.type
|
|
// vcard 3.0 save pref alongside TYPE
|
|
.filter(type => type !== 'pref')
|
|
// we only use uppercase strings
|
|
.map(str => str.toUpperCase())
|
|
|
|
// Compare array and score them by how many matches they have to the selected type
|
|
// sorting directly is cleaner but slower
|
|
// https://jsperf.com/array-map-and-intersection-perf
|
|
let matchingTypes = this.propModel.options.map(type => {
|
|
return {
|
|
type,
|
|
// "WORK,HOME" => ['WORK', 'HOME']
|
|
score: type.id.split(',').filter(value => selectedType.indexOf(value) !== -1).length
|
|
}
|
|
})
|
|
|
|
// Sort by score, filtering out the null score and selecting the first match
|
|
let matchingType = matchingTypes
|
|
.sort((a, b) => b.score - a.score)
|
|
.filter(type => type.score > 0)[0]
|
|
|
|
if (matchingType) {
|
|
return matchingType.type
|
|
}
|
|
}
|
|
if (this.type) {
|
|
// vcard 3.0 save pref alongside TYPE
|
|
let selectedType = this.type.filter(type => type !== 'pref').join(',')
|
|
return {
|
|
id: selectedType,
|
|
name: selectedType
|
|
}
|
|
}
|
|
},
|
|
set(data) {
|
|
// ical.js take types as arrays
|
|
this.type = data.id.split(',')
|
|
this.$emit('updatedcontact')
|
|
}
|
|
|
|
},
|
|
|
|
// property value(s)
|
|
value: {
|
|
get() {
|
|
if (this.property.isMultiValue) {
|
|
// differences between values types :x;x;x;x;x and x,x,x,x,x
|
|
return this.property.isStructuredValue
|
|
? this.property.getValues()[0]
|
|
: this.property.getValues()
|
|
}
|
|
return this.property.getFirstValue()
|
|
},
|
|
set(data) {
|
|
if (this.property.isMultiValue) {
|
|
// differences between values types :x;x;x;x;x and x,x,x,x,x
|
|
this.property.isStructuredValue
|
|
? this.property.setValues([data])
|
|
: this.property.setValues(data)
|
|
} else {
|
|
this.property.setValue(data)
|
|
}
|
|
this.$emit('updatedcontact')
|
|
}
|
|
},
|
|
|
|
// property meta type
|
|
type: {
|
|
get() {
|
|
let type = this.property.getParameter('type')
|
|
// ensure we have an array
|
|
if (type) {
|
|
return Array.isArray(type) ? type : [type]
|
|
}
|
|
},
|
|
set(data) {
|
|
this.property.setParameter('type', data)
|
|
}
|
|
},
|
|
|
|
// property meta pref
|
|
pref: {
|
|
get() {
|
|
return this.property.getParameter('pref')
|
|
},
|
|
set(data) {
|
|
this.property.setParameter('pref', data)
|
|
}
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
/**
|
|
* Delete this property
|
|
*/
|
|
deleteProp() {
|
|
this.contact.vCard.removeProperty(this.property)
|
|
}
|
|
}
|
|
|
|
}
|
|
</script>
|