diff options
author | John Molakvoæ <skjnldsv@users.noreply.github.com> | 2018-09-27 18:41:11 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-27 18:41:11 +0200 |
commit | 6d848dcd3bf513076bb2f5cadb138898422d4b30 (patch) | |
tree | 1d1a510c683afba5fa18bee50ddedc2d12bd4977 /src | |
parent | 309dbc5efd6d87eb89a1b6c99f7b957bcc3e4513 (diff) | |
parent | 42953d1ed21cdff26c3f144a1013a1244066a2bf (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.vue | 33 | ||||
-rw-r--r-- | src/components/Settings/SettingsImportContacts.vue | 2 | ||||
-rw-r--r-- | src/services/parseVcf.js | 6 | ||||
-rw-r--r-- | src/store/addressbooks.js | 47 | ||||
-rw-r--r-- | src/store/groups.js | 73 | ||||
-rw-r--r-- | src/store/importState.js | 15 | ||||
-rw-r--r-- | src/views/Contacts.vue | 2 |
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) }) }, |