summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2018-08-28 18:27:58 +0200
committersuntala <suntala@hotmail.com>2018-08-28 20:54:16 +0200
commitec6ecc7863098198133379e240613e0253276ab5 (patch)
tree0856727b0293821bc39e126833324e6b3c37c329
parent8850e9a3aeb637210cd4461e12b0dadc55d48857 (diff)
Import progress work by @suntala
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
-rw-r--r--css/contacts.scss1
-rw-r--r--css/importScreen.scss37
-rw-r--r--src/components/ImportScreen.vue42
-rw-r--r--src/components/Settings/SettingsImportContacts.vue62
-rw-r--r--src/components/SettingsSection.vue27
-rw-r--r--src/services/parseVcf.js14
-rw-r--r--src/store/addressbooks.js9
-rw-r--r--src/store/contacts.js1
-rw-r--r--src/store/importState.js41
-rw-r--r--src/store/index.js4
-rw-r--r--src/views/Contacts.vue3
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'