diff options
-rw-r--r-- | css/contacts.scss | 1 | ||||
-rw-r--r-- | css/importScreen.scss | 37 | ||||
-rw-r--r-- | src/components/ImportScreen.vue | 42 | ||||
-rw-r--r-- | src/components/Settings/SettingsImportContacts.vue | 62 | ||||
-rw-r--r-- | src/components/SettingsSection.vue | 27 | ||||
-rw-r--r-- | src/services/parseVcf.js | 14 | ||||
-rw-r--r-- | src/store/addressbooks.js | 9 | ||||
-rw-r--r-- | src/store/contacts.js | 1 | ||||
-rw-r--r-- | src/store/importState.js | 41 | ||||
-rw-r--r-- | src/store/index.js | 4 | ||||
-rw-r--r-- | src/views/Contacts.vue | 3 |
11 files changed, 218 insertions, 23 deletions
diff --git a/css/contacts.scss b/css/contacts.scss index 8c5f9ba6..503eaf1f 100644 --- a/css/contacts.scss +++ b/css/contacts.scss @@ -22,3 +22,4 @@ $grid-input-height-with-margin: #{$grid-height-unit - $grid-input-margin * 2}; @import 'ContactDetails'; @import './Properties/Properties'; @import './Properties/PropertyTitle'; +@import 'importScreen';
\ No newline at end of file diff --git a/css/importScreen.scss b/css/importScreen.scss new file mode 100644 index 00000000..c57dca90 --- /dev/null +++ b/css/importScreen.scss @@ -0,0 +1,37 @@ +/** + * @copyright Copyright (c) 2018 Team Popcorn <teampopcornberlin@gmail.com> + * + * author Team Popcorn <teampopcornberlin@gmail.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/>. + * + */ + +.import-screen { + &__header { + padding-top: 20px; + } + &__progress { + width: 50%; + position: absolute; + left: 25%; + } + &__tracker { + padding-top: 10px; + position: absolute; + left: 25%; + } +}
\ No newline at end of file diff --git a/src/components/ImportScreen.vue b/src/components/ImportScreen.vue new file mode 100644 index 00000000..e0f3f827 --- /dev/null +++ b/src/components/ImportScreen.vue @@ -0,0 +1,42 @@ +<!-- + * @copyright Copyright (c) 2018 Team Popcorn <teampopcornberlin@gmail.com> + * + * @author Team Popcorn <teampopcornberlin@gmail.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> + <div class="emptycontent import-screen"> + <p class="icon-upload" /> + <h3 class="import-screen__header">{{ t('contacts', 'Importing into') }} {{ 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> + </div> +</template> + +<script> + +export default { + name: 'ImportScreen', + computed: { + importState() { + return this.$store.getters.getImportState + } + } +} +</script> diff --git a/src/components/Settings/SettingsImportContacts.vue b/src/components/Settings/SettingsImportContacts.vue index c67fdb63..608f054d 100644 --- a/src/components/Settings/SettingsImportContacts.vue +++ b/src/components/Settings/SettingsImportContacts.vue @@ -19,17 +19,20 @@ - along with this program. If not, see <http://www.gnu.org/licenses/>. - --> +// ☞ 5bfb0d3f-5288-48ce-9dc1-94c2b08cf3ca <template> <div class="import-contact"> - <input id="contact-import" type="file" class="hidden-visually"> + <input id="contact-import" type="file" class="hidden-visually" + @change="processFile"> <label id="upload" for="contact-import" class="button multiselect-label icon-upload no-select"> {{ t('contacts', 'Import into') }} </label> <multiselect - v-model="value" + v-model="importDestination" :options="options" :placeholder="t('contacts', 'Contacts')" + label="displayName" class="multiselect-vue" /> </div> </template> @@ -47,26 +50,65 @@ export default { directives: { clickOutside }, - // props: ['addressbooks'], props: { - addressbooks: { - type: Array, - required: false, - default: undefined + importState: { + type: Object, + default: () => { + return { + total: 0, + accepted: 0, + denied: 0 + } + } } }, data() { return { - value: '' + importDestination: '' } }, computed: { + addressbooks() { + return this.$store.getters.getAddressbooks + }, options() { - return [t('contacts', 'Contacts')].concat(this.addressbooks.map(x => x.displayName)) + return this.addressbooks.map(addressbook => { + return { + id: addressbook.id, + displayName: addressbook.displayName + } + }) + }, + importState() { + return this.$store.getters.getImportState + }, + selectedAddressbook: { + get() { + if (this.importDestination) { + return this.addressbooks.find(addressbook => addressbook.id === this.importDestination.id) + } + // default is first address book of the list + return this.addressbooks[0] + }, + set(value) { + this.importDestination = value + } } }, methods: { - + processFile(event) { + let file = event.target.files[0] + let reader = new FileReader() + let selectedAddressbook = this.selectedAddressbook + this.$emit('clicked', { importing: true }) + let self = this + reader.onload = function(e) { + self.$store.dispatch('getContactsFromAddressBook', { vcf: reader.result, addressbook: selectedAddressbook, importState: this.importState }) + self.$emit('fileLoaded', false) + } + reader.readAsText(file) + } } } </script> +// ☞ f34b2e1a-1610-4a5f-bdbf-9a83325796fe diff --git a/src/components/SettingsSection.vue b/src/components/SettingsSection.vue index f5a8968a..14f9e150 100644 --- a/src/components/SettingsSection.vue +++ b/src/components/SettingsSection.vue @@ -26,8 +26,8 @@ <address-book v-for="addressbook in addressbooks" :key="addressbook.id" :addressbook="addressbook" /> </ul> <add-address-book :addressbooks="addressbooks" /> - - <import-contacts :addressbooks="addressbooks" class="settings-section" /> + <import-contacts :addressbooks="addressbooks" :import-state="importState" class="settings-section" + @clicked="onClickImport" @fileLoaded="onLoad" /> <sort-contacts class="settings-section" /> </div> </template> @@ -46,10 +46,33 @@ export default { importContacts, sortContacts }, + props: { + importState: { + type: Object, + default: () => { + return { + total: 0, + accepted: 0, + denied: 0 + } + } + } + }, computed: { // store getters addressbooks() { return this.$store.getters.getAddressbooks + }, + importState() { + return this.$store.getters.getImportState + } + }, + methods: { + onClickImport(event) { + this.$emit('clicked', event) + }, + onLoad(event) { + this.$emit('fileLoaded', false) } } } diff --git a/src/services/parseVcf.js b/src/services/parseVcf.js index 2a2039d5..86c02981 100644 --- a/src/services/parseVcf.js +++ b/src/services/parseVcf.js @@ -1,7 +1,7 @@ /** - * @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com> + * @copyright Copyright (c) 2018 Team Popcorn <teampopcornberlin@gmail.com> * - * @author John Molakvoæ <skjnldsv@protonmail.com> + * @author Team Popcorn <teampopcornberlin@gmail.com> * * @license GNU AGPL version 3 or any later version * @@ -22,19 +22,25 @@ import Contact from '../models/contact' -export default function parseVcf(data = '', addressbook) { +export default function parseVcf(data = '', addressbook, importState) { let regexp = /BEGIN:VCARD[\s\S]*?END:VCARD/mgi let vCards = data.match(regexp) + importState.total = vCards.length + if (!vCards) { console.debug('Error during the parsing of the following vcf file: ', data) return [] } return vCards.map(vCard => { try { - return new Contact(vCard, addressbook) + // console.log(vCards.indexOf(vCard)) + let contact = new Contact(vCard, addressbook) + importState.accepted++ + return contact } catch (e) { // Parse error! Do not stop here... + importState.denied++ // eslint-disable-next-line console.error(e) } diff --git a/src/store/addressbooks.js b/src/store/addressbooks.js index be152edf..de2cb427 100644 --- a/src/store/addressbooks.js +++ b/src/store/addressbooks.js @@ -21,7 +21,6 @@ */ /* eslint-disable-next-line import/no-webpack-loader-syntax */ -import vcfFile from '!raw-loader!./FakeName.vcf' import parseVcf from '../services/parseVcf' import Vue from 'vue' @@ -187,14 +186,14 @@ const actions = { }, /** - * Retrieve the contacts of the specified addressbook + * Retrieve the contacts for the specified address book * and commit the results * * @param {Object} context - * @param {Object} addressbook + * @param {Object} importDetails = { vcf, addressbook } */ - async getContactsFromAddressBook(context, addressbook) { - let contacts = parseVcf(vcfFile, addressbook) + getContactsFromAddressBook(context, { vcf, addressbook, importState }) { + let contacts = parseVcf(vcf, addressbook, importState) context.commit('appendContactsToAddressbook', { addressbook, contacts }) context.commit('appendContacts', contacts) context.commit('sortContacts') diff --git a/src/store/contacts.js b/src/store/contacts.js index 9554766a..1b42d049 100644 --- a/src/store/contacts.js +++ b/src/store/contacts.js @@ -178,7 +178,6 @@ const mutations = { setOrder(state, orderKey = 'displayName') { state.orderKey = orderKey } - } const getters = { diff --git a/src/store/importState.js b/src/store/importState.js new file mode 100644 index 00000000..e0f0b52e --- /dev/null +++ b/src/store/importState.js @@ -0,0 +1,41 @@ +/** + * @copyright Copyright (c) 2018 Team Popcorn <teampopcornberlin@gmail.com> + * + * @author Team Popcorn <teampopcornberlin@gmail.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/>. + * + */ + +const state = { + importState: { + total: 0, + accepted: 0, + denied: 0 + } +} + +const mutations = { +} + +const getters = { + getImportState: state => state.importState +} + +const actions = { +} + +export default { state, mutations, getters, actions } diff --git a/src/store/index.js b/src/store/index.js index 94ecf0f4..8bd7c2c0 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -25,6 +25,7 @@ import Vuex from 'vuex' import addressbooks from './addressbooks' import contacts from './contacts' import groups from './groups' +import importState from './importState' Vue.use(Vuex) @@ -34,7 +35,8 @@ export default new Vuex.Store({ modules: { addressbooks, contacts, - groups + groups, + importState }, mutations diff --git a/src/views/Contacts.vue b/src/views/Contacts.vue index a999a505..e66f1eca 100644 --- a/src/views/Contacts.vue +++ b/src/views/Contacts.vue @@ -32,6 +32,8 @@ <!-- main content --> <div id="app-content"> <div id="app-content-wrapper"> + <!-- loading --> + <import-screen /> <!-- contacts list --> <content-list :list="contactsList" :contacts="contacts" :loading="loading" /> <!-- main contacts details --> @@ -47,6 +49,7 @@ import appNavigation from '../components/core/appNavigation' import settingsSection from '../components/SettingsSection' import contentList from '../components/ContentList' import contactDetails from '../components/ContactDetails' +import importScreen from '../components/ImportScreen' import Contact from '../models/contact' import rfcProps from '../models/rfcProps.js' |