diff options
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/ContactDetails/ContactDetailsAvatar.vue | 7 | ||||
-rw-r--r-- | src/components/Properties/PropertyDateTime.vue | 2 | ||||
-rw-r--r-- | src/components/Properties/PropertyMultipleText.vue | 4 | ||||
-rw-r--r-- | src/components/Settings/SettingsImportContacts.vue | 194 | ||||
-rw-r--r-- | src/components/SettingsSection.vue | 2 |
5 files changed, 185 insertions, 24 deletions
diff --git a/src/components/ContactDetails/ContactDetailsAvatar.vue b/src/components/ContactDetails/ContactDetailsAvatar.vue index 703a3d4f..76433d41 100644 --- a/src/components/ContactDetails/ContactDetailsAvatar.vue +++ b/src/components/ContactDetails/ContactDetailsAvatar.vue @@ -88,8 +88,9 @@ import debounce from 'debounce' import { ActionLink, ActionButton } from '@nextcloud/vue' -import { getFilePickerBuilder } from 'nextcloud-dialogs' -import { generateRemoteUrl } from 'nextcloud-router' +import { getFilePickerBuilder } from '@nextcloud/dialogs' +import { generateRemoteUrl } from '@nextcloud/router' +import { getCurrentUser } from '@nextcloud/auth' const axios = () => import('axios') @@ -113,7 +114,7 @@ export default { maximizeAvatar: false, opened: false, loading: false, - root: generateRemoteUrl(`dav/files/${OC.getCurrentUser().uid}`), + root: generateRemoteUrl(`dav/files/${getCurrentUser().uid}`), width: 0, height: 0, } diff --git a/src/components/Properties/PropertyDateTime.vue b/src/components/Properties/PropertyDateTime.vue index 03144509..cd0af107 100644 --- a/src/components/Properties/PropertyDateTime.vue +++ b/src/components/Properties/PropertyDateTime.vue @@ -85,7 +85,7 @@ import debounce from 'debounce' import moment from 'moment' import { DatetimePicker } from '@nextcloud/vue' -import { getLocale } from 'nextcloud-l10n' +import { getLocale } from '@nextcloud/l10n' import { VCardTime } from 'ical.js' import PropertyMixin from '../../mixins/PropertyMixin' diff --git a/src/components/Properties/PropertyMultipleText.vue b/src/components/Properties/PropertyMultipleText.vue index cd4107e9..896ff661 100644 --- a/src/components/Properties/PropertyMultipleText.vue +++ b/src/components/Properties/PropertyMultipleText.vue @@ -63,8 +63,8 @@ @input="updateValue"> <!-- props actions --> - <PropertyActions class="property__actions--floating" - v-if="!isReadOnly" + <PropertyActions v-if="!isReadOnly" + class="property__actions--floating" :actions="actions" :property-component="this" @delete="deleteProperty" /> diff --git a/src/components/Settings/SettingsImportContacts.vue b/src/components/Settings/SettingsImportContacts.vue index d7583a3a..f0b6f04c 100644 --- a/src/components/Settings/SettingsImportContacts.vue +++ b/src/components/Settings/SettingsImportContacts.vue @@ -23,21 +23,56 @@ <template> <div class="import-contact"> <template v-if="!isNoAddressbookAvailable"> - <input id="contact-import" - :disabled="isImporting" - type="file" - class="hidden-visually" - @change="processFile"> - <label id="upload" for="contact-import" class="button import-contact__multiselect-label icon-upload"> - {{ isImporting ? t('contacts', 'Importing into') : t('contacts', 'Import into') }} - </label> - <multiselect - v-model="selectedAddressbook" - :options="options" - :disabled="isSingleAddressbook || isImporting" - :placeholder="t('contacts', 'Contacts')" - label="displayName" - class="import-contact__multiselect" /> + <button class="import-contact__button-main" @click="toggleModal"> + <span class="icon-upload" /> + {{ t('contacts', 'Import contacts') }} + </button> + <Modal v-if="isOpened" + ref="modal" + class="import-contact__modal" + :title="t('contacts', 'Import contacts')" + @close="toggleModal"> + <section class="import-contact__modal-addressbook"> + <h3>{{ t('contacts', 'Import contacts') }}</h3> + <multiselect + v-if="!isSingleAddressbook" + id="select-addressbook" + v-model="selectedAddressbook" + :allow-empty="false" + :options="options" + :disabled="isSingleAddressbook || isImporting" + :placeholder="t('contacts', 'Contacts')" + label="displayName" + class="import-contact__multiselect"> + <template slot="singleLabel" slot-scope="{ option }"> + {{ t('contacts', `Import into the '{addressbookName}' addressbook`, { addressbookName: option.displayName }) }} + </template> + </multiselect> + </section> + <section class="import-contact__modal-pick"> + <input id="contact-import" + ref="contact-import-input" + :disabled="loading || isImporting" + type="file" + class="hidden-visually" + @change="processFile"> + <button + :disabled="loading" + class="button import-contact__button import-contact__button--local" + @click="clickImportInput"> + <span class="import-contact__button-icon icon-upload" /> + {{ t('contacts', 'Select local file') }} + </button> + <button + :class="{'icon-loading': loading}" + :disabled="loading" + class="button primary import-contact__button import-contact__button--files" + @click="openPicker"> + <span class="import-contact__button-icon icon-folder-white" /> + {{ t('contacts', 'Import from files') }} + </button> + </section> + </Modal> </template> <button v-else id="upload" @@ -49,12 +84,31 @@ </template> <script> +import { encodePath } from '@nextcloud/paths' +import { getCurrentUser } from '@nextcloud/auth' +import { generateRemoteUrl } from '@nextcloud/router' +import { getFilePickerBuilder } from '@nextcloud/dialogs' +import axios from 'axios' + +const CancelToken = axios.CancelToken + +const picker = getFilePickerBuilder(t('contacts', 'Choose a vcard file to import')) + .setMultiSelect(false) + .setModal(true) + .setType(1) + .allowDirectories(false) + .setMimeTypeFilter('text/vcard') + .build() + export default { name: 'SettingsImportContacts', data() { return { + cancelRequest: () => {}, importDestination: false, + isOpened: false, + loading: false, } }, @@ -110,20 +164,126 @@ export default { }, }, methods: { + /** + * Process input type file change + * + * @param {Event} event the input change event + */ processFile(event) { + this.loading = true + this.$store.dispatch('changeStage', 'parsing') + const file = event.target.files[0] const reader = new FileReader() const selectedAddressbook = this.selectedAddressbook - this.$store.dispatch('changeStage', 'parsing') - this.$store.dispatch('setAddressbook', selectedAddressbook.displayName) + const self = this reader.onload = function(e) { + self.isOpened = false self.$store.dispatch('importContactsIntoAddressbook', { vcf: reader.result, addressbook: selectedAddressbook }) + // reset input event.target.value = '' + self.resetState() } reader.readAsText(file) }, + + toggleModal() { + this.isOpened = !this.isOpened + // cancel any ongoing request if closed + if (!this.isOpened) { + this.cancelRequest() + } + }, + + clickImportInput() { + this.$refs['contact-import-input'].click() + }, + + /** + * Open nextcloud file picker + */ + async openPicker() { + try { + this.loading = true + // unlikely, but let's cancel any previous request + this.cancelRequest() + + // prepare cancel token for axios request + const source = CancelToken.source() + this.cancelRequest = source.cancel + + // pick and retrieve file + const path = await picker.pick() + const file = await axios.get(generateRemoteUrl(`dav/files/${getCurrentUser().uid}`) + encodePath(path), { + cancelToken: source.token, + }) + + this.$store.dispatch('changeStage', 'parsing') + this.$store.dispatch('setAddressbook', this.selectedAddressbook.displayName) + + if (file.data) { + await this.$store.dispatch('importContactsIntoAddressbook', { vcf: file.data, addressbook: this.selectedAddressbook }) + } + } catch (error) { + console.error('Something wrong happened while picking a file', error) + } finally { + this.resetState() + } + }, + + /** + * Reset default component state + */ + resetState() { + this.cancelRequest = () => {} + this.importDestination = false + this.isOpened = false + this.loading = false + }, }, } </script> + +<style lang="scss" scoped> +.import-contact { + &__modal { + section { + padding: 22px; + // only one padding bewteen sections + &:not(:last-child) { + padding-bottom: 0; + } + } + &-pick { + display: flex; + align-items: center; + flex-wrap: wrap; + justify-content: space-evenly; + } + } + &__button { + display: flex; + align-items: center; + flex: 0 1 150px; + width: 150px; + // spread evenly + margin: 10px; + padding: 10px; + &-icon { + width: 32px; + height: 32px; + margin-right: 5px; + } + &-main { + width: 100%; + } + &--cancel:not(:focus):not(:hover) { + border-color: transparent; + background-color: transparent; + } + } +} + +</style> diff --git a/src/components/SettingsSection.vue b/src/components/SettingsSection.vue index 7053356b..e0757716 100644 --- a/src/components/SettingsSection.vue +++ b/src/components/SettingsSection.vue @@ -26,11 +26,11 @@ <SettingsAddressbook v-for="addressbook in addressbooks" :key="addressbook.id" :addressbook="addressbook" /> </ul> <SettingsNewAddressbook :addressbooks="addressbooks" /> + <SettingsSortContacts class="settings-section" /> <SettingsImportContacts :addressbooks="addressbooks" class="settings-section" @clicked="onClickImport" @fileLoaded="onLoad" /> - <SettingsSortContacts class="settings-section" /> </div> </template> |