summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@users.noreply.github.com>2018-09-27 18:41:11 +0200
committerGitHub <noreply@github.com>2018-09-27 18:41:11 +0200
commit6d848dcd3bf513076bb2f5cadb138898422d4b30 (patch)
tree1d1a510c683afba5fa18bee50ddedc2d12bd4977 /src
parent309dbc5efd6d87eb89a1b6c99f7b957bcc3e4513 (diff)
parent42953d1ed21cdff26c3f144a1013a1244066a2bf (diff)
Merge pull request #648 from nextcloud/vue-import-dav-fix
Fix import and push vCards to the server
Diffstat (limited to 'src')
-rw-r--r--src/components/ImportScreen.vue33
-rw-r--r--src/components/Settings/SettingsImportContacts.vue2
-rw-r--r--src/services/parseVcf.js6
-rw-r--r--src/store/addressbooks.js47
-rw-r--r--src/store/groups.js73
-rw-r--r--src/store/importState.js15
-rw-r--r--src/views/Contacts.vue2
7 files changed, 121 insertions, 57 deletions
diff --git a/src/components/ImportScreen.vue b/src/components/ImportScreen.vue
index bb903442..b58182fa 100644
--- a/src/components/ImportScreen.vue
+++ b/src/components/ImportScreen.vue
@@ -23,19 +23,46 @@
<template>
<div class="emptycontent import-screen">
<p class="icon-upload" />
- <h3 class="import-screen__header">{{ t('contacts', 'Importing into') }} {{ importState.addressbook }}</h3>
- <progress :max="importState.total" :value="importState.accepted" class="import-screen__progress" />
- <p class="import-screen__tracker">{{ Math.floor(importState.accepted/(importState.total + 1)) * 100 }} %</p>
+ <h3 class="import-screen__header">{{ t('contacts', 'Importing {total} contacts into', { total }) }} {{ addressbook }}</h3>
+ <progress :max="total" :value="progress" class="import-screen__progress" />
+ <p class="import-screen__tracker">
+ <span>{{ percentage }} %</span>
+ <span v-tooltip.auto="t('contacts', 'Open your browser console for more details')">{{ denied }} {{ t('contacts', 'failed') }}</span>
+ </p>
</div>
</template>
<script>
+import Vue from 'vue'
+import VTooltip from 'v-tooltip'
+
+Vue.use(VTooltip)
export default {
name: 'ImportScreen',
computed: {
importState() {
return this.$store.getters.getImportState
+ },
+ addressbook() {
+ return this.importState.addressbook
+ },
+ total() {
+ return this.importState.total
+ },
+ accepted() {
+ return this.importState.accepted
+ },
+ denied() {
+ return this.importState.denied
+ },
+ progress() {
+ return this.accepted + this.denied
+ },
+ percentage() {
+ return this.total <= 0
+ ? 0
+ : Math.floor(this.progress / this.total * 100)
}
}
}
diff --git a/src/components/Settings/SettingsImportContacts.vue b/src/components/Settings/SettingsImportContacts.vue
index 881308ae..d7a17044 100644
--- a/src/components/Settings/SettingsImportContacts.vue
+++ b/src/components/Settings/SettingsImportContacts.vue
@@ -98,6 +98,8 @@ export default {
let self = this
reader.onload = function(e) {
self.$store.dispatch('importContactsIntoAddressbook', { vcf: reader.result, addressbook: selectedAddressbook })
+ // reset input
+ event.target.value = ''
}
reader.readAsText(file)
}
diff --git a/src/services/parseVcf.js b/src/services/parseVcf.js
index 65d5f95c..11b1fc49 100644
--- a/src/services/parseVcf.js
+++ b/src/services/parseVcf.js
@@ -26,14 +26,13 @@ import Store from '../store/index'
export default function parseVcf(data = '', addressbook) {
let regexp = /BEGIN:VCARD[\s\S]*?END:VCARD/mgi
let vCards = data.match(regexp)
- let importState = Store.getters.getImportState
if (!vCards) {
console.debug('Error during the parsing of the following vcf file: ', data)
return []
}
- importState.total = vCards.length
+ Store.dispatch('setTotal', vCards.length)
// Not using map because we want to only push valid contacts
// map force to return at least undefined
@@ -41,11 +40,10 @@ export default function parseVcf(data = '', addressbook) {
try {
// console.log(vCards.indexOf(vCard))
let contact = new Contact(vCard, addressbook)
- importState.accepted++
contacts.push(contact)
} catch (e) {
// Parse error! Do not stop here...
- importState.denied++
+ Store.dispatch('incrementDenied')
console.error(e)
}
return contacts
diff --git a/src/store/addressbooks.js b/src/store/addressbooks.js
index 74a47bef..d6ca8a1d 100644
--- a/src/store/addressbooks.js
+++ b/src/store/addressbooks.js
@@ -22,9 +22,11 @@
*/
import Vue from 'vue'
+import ICAL from 'ical.js'
import parseVcf from '../services/parseVcf'
import client from '../services/cdav'
import Contact from '../models/contact'
+import pLimit from 'p-limit'
const addressbookModel = {
id: '',
@@ -298,7 +300,7 @@ const actions = {
* @returns {Promise}
*/
async getContactsFromAddressBook(context, { addressbook }) {
- return addressbook.dav.findAllAndFilterBySimpleProperties(['EMAIL', 'UID', 'CATEGORIES', 'FN', 'ORG'])
+ return addressbook.dav.findAllAndFilterBySimpleProperties(['EMAIL', 'UID', 'CATEGORIES', 'FN', 'ORG', 'N'])
.then((response) => {
// We don't want to lose the url information
// so we need to parse one by one
@@ -309,7 +311,7 @@ const actions = {
})
context.commit('appendContactsToAddressbook', { addressbook, contacts })
context.commit('appendContacts', contacts)
- context.commit('appendGroupsFromContacts', contacts)
+ context.commit('extractGroupsFromContacts', contacts)
context.commit('sortContacts')
return contacts
})
@@ -327,15 +329,42 @@ const actions = {
* @param {Object} context the store mutations
* @param {Object} importDetails = { vcf, addressbook }
*/
- importContactsIntoAddressbook(context, { vcf, addressbook }) {
- let contacts = parseVcf(vcf, addressbook)
+ async importContactsIntoAddressbook(context, { vcf, addressbook }) {
+ const contacts = parseVcf(vcf, addressbook)
context.commit('changeStage', 'importing')
- contacts.forEach(contact => {
- context.commit('addContact', contact)
- context.commit('addContactToAddressbook', contact)
- context.commit('appendGroupsFromContacts', [contact])
+
+ // max simultaneous requests
+ const limit = pLimit(3)
+ const requests = []
+
+ // create the array of requests to send
+ contacts.map(async contact => {
+ // Get vcard string
+ try {
+ let vData = ICAL.stringify(contact.vCard.jCal)
+ // push contact to server and use limit
+ requests.push(limit(() => contact.addressbook.dav.createVCard(vData)
+ .then((response) => {
+ // success, update store
+ context.commit('addContact', contact)
+ context.commit('addContactToAddressbook', contact)
+ context.commit('extractGroupsFromContacts', [contact])
+ context.commit('incrementAccepted')
+ })
+ .catch((error) => {
+ // error
+ context.commit('incrementDenied')
+ console.error(error)
+ })
+ ))
+ } catch (e) {
+ context.commit('incrementDenied')
+ }
+ })
+
+ Promise.all(requests).then(() => {
+ context.commit('changeStage', 'default')
})
- context.commit('changeStage', 'default')
},
/**
diff --git a/src/store/groups.js b/src/store/groups.js
index abd4599f..dd7a2393 100644
--- a/src/store/groups.js
+++ b/src/store/groups.js
@@ -27,40 +27,29 @@ const state = {
const mutations = {
/**
* Extract all the groups from the provided contacts
+ * and add the contacts to their respective groups
*
* @param {Object} state the store data
* @param {Contact[]} contacts the contacts to add
- * TODO: create single contact mutation
*/
- appendGroupsFromContacts(state, contacts) {
- // init groups list
- let groups = Object.values(contacts)
- // iterate on every contacts
- .reduce((groups, contact) => {
- // transform group names into Object
- contact.groups.map(groupName => {
- // overriding existing groups: remove duplicates
- groups[groupName] = {
- name: groupName,
- contacts: []
+ extractGroupsFromContacts(state, contacts) {
+ // iterate contacts
+ contacts.forEach(contact => {
+ if (contact.groups) {
+ contact.groups.forEach(groupName => {
+ let group = state.groups.find(search => search.name === groupName)
+ // nothing? create a new one
+ if (!group) {
+ state.groups.push({
+ name: groupName,
+ contacts: []
+ })
+ group = state.groups.find(search => search.name === groupName)
}
+ group.contacts.push(contact.key)
})
- return groups
- }, {})
-
- // store in state
- state.groups = Object.values(groups)
-
- // append keys to groups
- Object.values(contacts)
- .forEach(contact => {
- if (contact.groups) {
- contact.groups.forEach(groupName => {
- let group = state.groups.find(search => search.name === groupName)
- group.contacts.push(contact.key)
- })
- }
- })
+ }
+ })
},
/**
@@ -68,20 +57,22 @@ const mutations = {
*
* @param {Object} state the store data
* @param {Object} data destructuring object
- * @param {String} data.groupName the name of the group
+ * @param {Array<string>} data.groupNames the names of the group
* @param {Contact} data.contact the contact
*/
- addContactToGroup(state, { groupName, contact }) {
- let group = state.groups.find(search => search.name === groupName)
- // nothing? create a new one
- if (!group) {
- state.groups.push({
- name: groupName,
- contacts: []
- })
- group = state.groups.find(search => search.name === groupName)
- }
- group.contacts.push(contact.key)
+ addContactToGroups(state, { groupNames, contact }) {
+ groupNames.forEach(groupName => {
+ let group = state.groups.find(search => search.name === groupName)
+ // nothing? create a new one
+ if (!group) {
+ state.groups.push({
+ name: groupName,
+ contacts: []
+ })
+ group = state.groups.find(search => search.name === groupName)
+ }
+ group.contacts.push(contact.key)
+ })
},
/**
@@ -116,7 +107,7 @@ const actions = {
* @param {Contact} data.contact the contact
*/
addContactToGroup(context, { groupName, contact }) {
- context.commit('addContactToGroup', { groupName, contact })
+ context.commit('addContactToGroups', { groupNames: [groupName], contact })
},
/**
diff --git a/src/store/importState.js b/src/store/importState.js
index 024b0ac0..b5adafc7 100644
--- a/src/store/importState.js
+++ b/src/store/importState.js
@@ -77,6 +77,17 @@ const mutations = {
*/
changeStage(state, stage) {
state.importState.stage = stage
+ },
+
+ /**
+ * Reset to the default state
+ *
+ * @param {Object} state the store data
+ */
+ resetState(state) {
+ state.importState.total = 0
+ state.importState.accepted = 0
+ state.importState.denied = 0
}
}
@@ -125,12 +136,16 @@ const actions = {
/**
* Change stage to the indicated one
+ * and reset if the parsing starts
*
* @param {Object} context the store mutations
* @param {String} stage the name of the stage ('default', 'importing', 'parsing')
*/
changeStage(context, stage) {
context.commit('changeStage', stage)
+ if (stage === 'parsing') {
+ context.commit('resetState')
+ }
}
}
diff --git a/src/views/Contacts.vue b/src/views/Contacts.vue
index e9c236f2..fe947279 100644
--- a/src/views/Contacts.vue
+++ b/src/views/Contacts.vue
@@ -146,6 +146,8 @@ export default {
counter: group.contacts.length
}
}
+ }).sort(function(a, b) {
+ return parseInt(b.utils.counter) - parseInt(a.utils.counter)
})
},