summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2018-09-24 23:46:01 +0200
committerJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2018-09-25 14:53:28 +0200
commitf3c1f13792d78e3617e3d3f726d1324f3ea576ee (patch)
tree29197f5d49c4cbc9c4d3bcf4d36fd25a2e09224f
parent19a56a6fba9ad82f9b0c34d92bb4cd256ae88a94 (diff)
Fixed select property
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
-rw-r--r--css/Properties/Properties.scss12
-rw-r--r--src/components/ContactDetails.vue51
-rw-r--r--src/components/ContactDetails/ContactDetailsProperty.vue10
-rw-r--r--src/components/Properties/PropertySelect.vue50
-rw-r--r--src/components/Settings/SettingsNewAddressbook.vue5
-rw-r--r--src/models/rfcProps.js7
-rw-r--r--src/store/addressbooks.js1
-rw-r--r--src/store/contacts.js63
-rw-r--r--src/views/Contacts.vue51
9 files changed, 165 insertions, 85 deletions
diff --git a/css/Properties/Properties.scss b/css/Properties/Properties.scss
index bf444758..d79fb758 100644
--- a/css/Properties/Properties.scss
+++ b/css/Properties/Properties.scss
@@ -100,6 +100,18 @@
}
}
+ &__label.multiselect {
+ .multiselect__tags {
+ border: none;
+ .multiselect__single {
+ @include icon-color('triangle-s', 'actions', $color-black, 1, true);
+ background-repeat: no-repeat;
+ background-position: center right 4px;
+ padding-right: 24px;
+ }
+ }
+ }
+
// property value within row, after label
&__value {
flex: 1 1;
diff --git a/src/components/ContactDetails.vue b/src/components/ContactDetails.vue
index f3c4b647..889dc973 100644
--- a/src/components/ContactDetails.vue
+++ b/src/components/ContactDetails.vue
@@ -95,7 +95,7 @@
<!-- addressbook change select - no last property because class is not applied here-->
<property-select :prop-model="addressbookModel" :value.sync="addressbook" :is-first-property="true"
- :is-last-property="false" :options="addressbooksOptions" class="property--addressbooks" />
+ :is-last-property="false" class="property--addressbooks" />
<!-- new property select -->
<add-new-prop :contact="contact" />
@@ -158,11 +158,7 @@ export default {
localContact: undefined,
loadingData: true,
loadingUpdate: false,
- openedMenu: false,
- addressbookModel: {
- readableName: t('contacts', 'Addressbook'),
- icon: 'icon-addressbook'
- }
+ openedMenu: false
}
},
@@ -212,16 +208,24 @@ export default {
})
},
+ /**
+ * Fake model to use the propertySelect component
+ */
+ addressbookModel() {
+ return {
+ readableName: t('contacts', 'Addressbook'),
+ icon: 'icon-addressbook',
+ options: this.addressbooksOptions
+ }
+ },
+
// usable addressbook object linked to the local contact
addressbook: {
get: function() {
- return {
- id: this.contact.addressbook.id,
- name: this.contact.addressbook.displayName
- }
+ return this.contact.addressbook.id
},
- set: function(addressbook) {
- this.moveContactToAddressbook(addressbook)
+ set: function(addressbookId) {
+ this.moveContactToAddressbook(addressbookId)
}
},
@@ -291,13 +295,16 @@ export default {
},
/**
- * Trigger an full fetch and
+ * Select a contac, and update the localContact
+ * Fetch updated data if necessary
*/
selectContact(uid) {
// local version of the contact
this.loadingData = true
let contact = this.$store.getters.getContact(uid)
- if (contact) {
+
+ // if contact exists AND if exists on server
+ if (contact && contact.dav) {
this.$store.dispatch('fetchFullContact', contact)
.then(() => {
// create empty contact and copy inner data
@@ -309,6 +316,14 @@ export default {
this.localContact = localContact
this.loadingData = false
})
+ } else if (contact) {
+ // create empty contact and copy inner data
+ // wait for an update to really push the contact on the server!
+ this.localContact = new Contact(
+ 'BEGIN:VCARD\nUID:' + contact.uid + '\nEND:VCARD',
+ contact.addressbook
+ )
+ this.loadingData = false
}
},
@@ -322,12 +337,10 @@ export default {
/**
* Move contact to the specified addressbook
*
- * @param {Object} addressbook the desired addressbook
+ * @param {string} addressbookId the desired addressbook ID
*/
- moveContactToAddressbook(addressbook) {
- addressbook = this.addressbooks.find(
- search => search.id === addressbook.id
- )
+ moveContactToAddressbook(addressbookId) {
+ let addressbook = this.addressbooks.find(search => search.id === addressbookId)
// TODO Make sure we do not overwrite contacts
if (addressbook) {
this.$store
diff --git a/src/components/ContactDetails/ContactDetailsProperty.vue b/src/components/ContactDetails/ContactDetailsProperty.vue
index 4b0428fb..3be03f70 100644
--- a/src/components/ContactDetails/ContactDetailsProperty.vue
+++ b/src/components/ContactDetails/ContactDetailsProperty.vue
@@ -37,7 +37,7 @@ import PropertyText from '../Properties/PropertyText'
import PropertyMultipleText from '../Properties/PropertyMultipleText'
import PropertyDateTime from '../Properties/PropertyDateTime'
import propertyGroups from '../Properties/PropertyGroups'
-// import PropertySelect from '../Properties/PropertyMultipleText'
+import PropertySelect from '../Properties/PropertySelect'
export default {
name: 'ContactDetailsProperty',
@@ -76,6 +76,8 @@ export default {
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
}
@@ -111,6 +113,10 @@ export default {
return this.property.name
},
propType() {
+ // if we have a force type set, use it!
+ if (this.propModel && this.propModel.force) {
+ return this.propModel.force
+ }
return this.property.getDefaultType()
},
@@ -141,6 +147,8 @@ export default {
}
})
+ console.log(matchingTypes)
+
// Sort by score, filtering out the null score and selecting the first match
let matchingType = matchingTypes
.sort((a, b) => b.score - a.score)
diff --git a/src/components/Properties/PropertySelect.vue b/src/components/Properties/PropertySelect.vue
index fb1aa827..3769967f 100644
--- a/src/components/Properties/PropertySelect.vue
+++ b/src/components/Properties/PropertySelect.vue
@@ -26,14 +26,9 @@
<property-title v-if="isFirstProperty && propModel.icon" :icon="propModel.icon" :readable-name="propModel.readableName" />
<div class="property__row">
- <!-- type selector -->
- <multiselect v-if="propModel.options" v-model="localType"
- :options="propModel.options" :searchable="false" :placeholder="t('contacts', 'Select type')"
- class="multiselect-vue property__label" track-by="id" label="name"
- @input="updateType" />
<!-- if we do not support any type on our model but one is set anyway -->
- <div v-else-if="selectType" class="property__label">{{ selectType.name }}</div>
+ <div v-if="selectType" class="property__label">{{ selectType.name }}</div>
<!-- no options, empty space -->
<div v-else class="property__label">{{ propModel.readableName }}</div>
@@ -41,9 +36,10 @@
<!-- delete the prop -->
<button :title="t('contacts', 'Delete')" class="property__delete icon-delete" @click="deleteProperty" />
- <multiselect v-model="localValue" :options="options" :placeholder="t('contacts', 'Select option')"
+ <multiselect v-model="matchedOptions" :options="propModel.options" :placeholder="t('contacts', 'Select option')"
class="multiselect-vue property__value" track-by="id" label="name"
- @input="updateValue" />
+ @input="updateValue">
+ </multiselect>
</div>
</div>
</template>
@@ -76,11 +72,6 @@ export default {
default: '',
required: true
},
- options: {
- type: Array,
- default: () => [],
- required: true
- },
isFirstProperty: {
type: Boolean,
default: true
@@ -93,8 +84,8 @@ export default {
data() {
return {
- localValue: this.value,
- localType: this.selectType
+ localValue: this.value
+ // localType: this.selectType
}
},
@@ -104,7 +95,20 @@ export default {
let isLast = this.isLastProperty ? 1 : 0
// length is one & add one space at the end
return hasTitle + 1 + isLast
+ },
+ matchedOptions: {
+ get() {
+ let selected = this.propModel.options.find(option => option.id === this.localValue)
+ return selected || {
+ id: this.localValue,
+ name: this.localValue
+ }
+ },
+ set(value) {
+ this.localValue = value.id
+ }
}
+
},
watch: {
@@ -115,10 +119,10 @@ export default {
*/
value: function() {
this.localValue = this.value
- },
- selectType: function() {
- this.localType = this.selectType
}
+ // selectType: function() {
+ // this.localType = this.selectType
+ // }
},
methods: {
@@ -136,12 +140,12 @@ export default {
updateValue: debounce(function(e) {
// https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
this.$emit('update:value', this.localValue)
- }, 500),
-
- updateType: debounce(function(e) {
- // https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
- this.$emit('update:selectType', this.localType)
}, 500)
+
+ // updateType: debounce(function(e) {
+ // // https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
+ // this.$emit('update:selectType', this.localType)
+ // }, 500)
}
}
diff --git a/src/components/Settings/SettingsNewAddressbook.vue b/src/components/Settings/SettingsNewAddressbook.vue
index 609fd1bd..1b1773ce 100644
--- a/src/components/Settings/SettingsNewAddressbook.vue
+++ b/src/components/Settings/SettingsNewAddressbook.vue
@@ -47,8 +47,9 @@ export default {
return {
loading: false,
displayName: '',
- // no slash!!
- addressBookRegex: '[^/\\]+'
+ // no slashes!
+ // eslint-disable-next-line
+ addressBookRegex: '[^/\\\\]+'
}
},
methods: {
diff --git a/src/models/rfcProps.js b/src/models/rfcProps.js
index cae39970..e2cdb40b 100644
--- a/src/models/rfcProps.js
+++ b/src/models/rfcProps.js
@@ -140,7 +140,7 @@ const properties = {
default: true,
defaultValue: {
value: '',
- type: ['HOME,VOICE']
+ type: ['HOME', 'VOICE']
},
options: [
{ id: 'HOME,VOICE', name: t('contacts', 'Home') },
@@ -183,6 +183,7 @@ const properties = {
},
relationship: {
readableName: t('contacts', 'Relationship'),
+ force: 'select',
info: t(
'contacts',
'Specify a relationship between you and the entity represented by this vCard.'
@@ -234,6 +235,10 @@ const properties = {
},
gender: {
readableName: t('contacts', 'Gender'),
+ defaultValue: {
+ // default to Female 🙋
+ value: ['F']
+ },
options: [
{ id: 'F', name: t('contacts', 'Female') },
{ id: 'M', name: t('contacts', 'Male') },
diff --git a/src/store/addressbooks.js b/src/store/addressbooks.js
index 68be9067..669e6585 100644
--- a/src/store/addressbooks.js
+++ b/src/store/addressbooks.js
@@ -244,7 +244,6 @@ const actions = {
return client.addressBookHomes[0].createAddressBookCollection(addressbook.displayName)
.then((response) => {
addressbook = mapDavCollectionToAddressbook(response)
- console.log(response, addressbook)
context.commit('addAddressbooks', addressbook)
})
.catch((error) => { throw error })
diff --git a/src/store/contacts.js b/src/store/contacts.js
index c1d7ecb9..7b577467 100644
--- a/src/store/contacts.js
+++ b/src/store/contacts.js
@@ -88,11 +88,21 @@ const mutations = {
for (var i = 0, len = state.sortedContacts.length; i < len; i++) {
var nameA = state.sortedContacts[i].value.toUpperCase() // ignore upper and lowercase
var nameB = sortedContact.value.toUpperCase() // ignore upper and lowercase
- if (nameA.localeCompare(nameB) > 0) {
+ if (nameA.localeCompare(nameB) >= 0) {
state.sortedContacts.splice(i, 0, sortedContact)
break
+ } else if (i + 1 === len) {
+ // we reached the end insert it now
+ state.sortedContacts.push(sortedContact)
}
}
+
+ // sortedContact is empty, just push it
+ if (state.sortedContacts.length === 0) {
+ state.sortedContacts.push(sortedContact)
+ }
+
+ // default contacts list
Vue.set(state.contacts, contact.key, contact)
} else {
@@ -116,7 +126,9 @@ const mutations = {
// has the sort key changed for this contact ?
let hasChanged = sortedContact.value !== contact[state.orderKey]
if (hasChanged) {
- // then we sort again
+ // then update the new data
+ sortedContact.value = contact[state.orderKey]
+ // and then we sort again
state.sortedContacts
.sort((a, b) => {
var nameA = a.value.toUpperCase() // ignore upper and lowercase
@@ -196,16 +208,16 @@ const actions = {
* @param {Object} context
* @param {Contact} contact the contact to delete
*/
- deleteContact(context, contact) {
- contact.dav.delete()
- .then((response) => {
- context.commit('deleteContact', contact)
- context.commit('deleteContactFromAddressbook', contact)
- })
- .catch((error) => {
- console.error(error)
- OC.Notification.showTemporary(t('contacts', 'An error occurred'))
- })
+ async deleteContact(context, contact) {
+ if (contact.dav) {
+ await contact.dav.delete()
+ .catch((error) => {
+ console.error(error)
+ OC.Notification.showTemporary(t('contacts', 'An error occurred'))
+ })
+ }
+ context.commit('deleteContact', contact)
+ context.commit('deleteContactFromAddressbook', contact)
},
/**
@@ -214,14 +226,9 @@ const actions = {
* @param {Object} context
* @param {Contact} contact the contact to delete
*/
- addContact(context, contact) {
- return contact.addressbook.dav.createVCard(ICAL.stringify(contact.vCard.jCal))
- .then((response) => {
- console.log(response)
- context.commit('addContact', contact)
- context.commit('addContactToAddressbook', contact)
- })
- .catch((error) => { throw error })
+ async addContact(context, contact) {
+ await context.commit('addContact', contact)
+ await context.commit('addContactToAddressbook', contact)
},
/**
@@ -230,8 +237,20 @@ const actions = {
* @param {Object} context
* @param {Contact} contact the contact to update
*/
- updateContact(context, contact) {
- contact.dav.data = ICAL.stringify(contact.vCard.jCal)
+ async updateContact(context, contact) {
+ let vData = ICAL.stringify(contact.vCard.jCal)
+
+ // if no dav key, contact does not exists on server
+ if (!contact.dav) {
+ // create contact
+ await contact.addressbook.dav.createVCard(vData)
+ .then((response) => {
+ contact.dav = response
+ })
+ .catch((error) => { throw error })
+ }
+
+ contact.dav.data = vData
return contact.dav.update()
.then((response) => context.commit('updateContact', contact))
.catch((error) => { throw error })
diff --git a/src/views/Contacts.vue b/src/views/Contacts.vue
index f6117b49..245711f6 100644
--- a/src/views/Contacts.vue
+++ b/src/views/Contacts.vue
@@ -201,15 +201,18 @@ export default {
client.connect({ enableCardDAV: true }).then(() => {
console.debug('Connected to dav!', client)
this.$store.dispatch('getAddressbooks')
- .then(() => {
- // wait for all addressbooks to have fetch their contacts
- Promise.all(this.addressbooks.map(addressbook => this.$store.dispatch('getContactsFromAddressBook', { addressbook })))
- .then(results => {
- this.loading = false
- this.selectFirstContactIfNone()
- })
- // no need for a catch, the action does not throw
- // and the error is handled there
+ .then((addressbooks) => {
+
+ // No addressbooks? Create a new one!
+ if (addressbooks.length === 0) {
+ this.$store.dispatch('appendAddressbook', { displayName: t('contacts', 'Contacts') })
+ .then(() => {
+ this.fetchContacts()
+ })
+ // else, let's get those contacts!
+ } else {
+ this.fetchContacts()
+ }
})
// check local storage for orderKey
if (localStorage.getItem('orderKey')) {
@@ -240,13 +243,15 @@ export default {
contact.vCard.addPropertyWithValue('categories', this.selectedGroup)
}
this.$store.dispatch('addContact', contact)
- this.$router.push({
- name: 'contact',
- params: {
- selectedGroup: this.selectedGroup,
- selectedContact: contact.key
- }
- })
+ .then(() => {
+ this.$router.push({
+ name: 'contact',
+ params: {
+ selectedGroup: this.selectedGroup,
+ selectedContact: contact.key
+ }
+ })
+ })
},
/**
@@ -261,6 +266,20 @@ export default {
},
/**
+ * Fetch the contacts of each addressbooks
+ */
+ fetchContacts() {
+ // wait for all addressbooks to have fetch their contacts
+ Promise.all(this.addressbooks.map(addressbook => this.$store.dispatch('getContactsFromAddressBook', { addressbook })))
+ .then(results => {
+ this.loading = false
+ this.selectFirstContactIfNone()
+ })
+ // no need for a catch, the action does not throw
+ // and the error is handled there
+ },
+
+ /**
* Select the first contact of the list
* if none are selected already
*/