Skip to content

Commit 025472c

Browse files
committed
feat(ContactsList): add contact merging action
Signed-off-by: Grigory Vodyanov <scratchx@gmx.com> # Conflicts: # src/components/ContactsList.vue
1 parent d1215ad commit 025472c

2 files changed

Lines changed: 722 additions & 4 deletions

File tree

src/components/ContactsList.vue

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
-->
55

66
<template>
7-
<AppContentList class="content-list">
7+
<AppContentList class="content-list" data-testid="contacts-list">
88
<NcDialog :open="showDeleteConfirmationDialog"
99
:name="n(
1010
'contacts',
@@ -19,12 +19,19 @@
1919
<NcNoteCard v-if="readOnlyMultiSelectedCount"
2020
type="info"
2121
:text="n('contacts',
22-
'Please note that {number} contact is read only and won\'t be deleted',
23-
'Please note that {number} contacts are read only and won\'t be deleted',
22+
'Please note that {number} contact is read only and will not be deleted',
23+
'Please note that {number} contacts are read only and will not be deleted',
2424
readOnlyMultiSelectedCount,
2525
{ number: readOnlyMultiSelectedCount })" />
2626
</NcDialog>
2727

28+
<NcModal v-if="isMerging"
29+
:name="t('contacts', 'Merge contacts')"
30+
size="large"
31+
@close="isMerging = false">
32+
<Merging :contacts="multiSelectedContacts" @finished="finishContactMerging" />
33+
</NcModal>
34+
2835
<div class="contacts-list__header">
2936
<div class="search-contacts-field">
3037
<input v-model="query" type="text" :placeholder="t('contacts', 'Search contacts …')">
@@ -45,6 +52,15 @@
4552
@click.prevent="attemptDeleteAllMultiSelected">
4653
<IconDelete :size="16" />
4754
</NcButton>
55+
<NcButton v-if="!isMergingLoading"
56+
type="tertiary"
57+
:disabled="!areTwoEditable"
58+
:title="mergeActionTitle"
59+
:close-after-click="true"
60+
@click.prevent="initiateContactMerging">
61+
<IconSetMerge :size="20" />
62+
</NcButton>
63+
<NcLoadingIcon v-else :size="20" />
4864
</div>
4965
</transition>
5066

@@ -59,11 +75,14 @@
5975
</template>
6076

6177
<script>
62-
import { NcAppContentList as AppContentList, NcButton, NcDialog, NcNoteCard } from '@nextcloud/vue'
78+
import { NcAppContentList as AppContentList, NcButton, NcDialog, NcNoteCard, NcModal, NcLoadingIcon } from '@nextcloud/vue'
6379
import ContactsListItem from './ContactsList/ContactsListItem.vue'
6480
import VirtualList from 'vue-virtual-scroll-list'
6581
import IconSelect from 'vue-material-design-icons/CloseThick.vue'
6682
import IconDelete from 'vue-material-design-icons/DeleteOutline.vue'
83+
import IconSetMerge from 'vue-material-design-icons/SetMerge.vue'
84+
import Merging from './ContactsList/Merging.vue'
85+
6786
// eslint-disable-next-line import/no-unresolved
6887
import IconCancelRaw from '@mdi/svg/svg/cancel.svg?raw'
6988
// eslint-disable-next-line import/no-unresolved
@@ -79,7 +98,11 @@ export default {
7998
NcButton,
8099
IconSelect,
81100
IconDelete,
101+
IconSetMerge,
82102
NcDialog,
103+
NcModal,
104+
Merging,
105+
NcLoadingIcon,
83106
},
84107
85108
props: {
@@ -122,6 +145,8 @@ export default {
122145
},
123146
],
124147
lastToggledIndex: undefined,
148+
isMerging: false,
149+
isMergingLoading: false,
125150
}
126151
},
127152
@@ -160,11 +185,19 @@ export default {
160185
isAtLeastOneEditable() {
161186
return this.readOnlyMultiSelectedCount !== this.multiSelectedContacts.size
162187
},
188+
areTwoEditable() {
189+
return this.multiSelectedContacts.size - this.readOnlyMultiSelectedCount === 2
190+
},
163191
deleteActionTitle() {
164192
return this.isAtLeastOneEditable
165193
? n('contacts', 'Delete {number} contact', 'Delete {number} contacts', this.multiSelectedContacts.size, { number: this.multiSelectedContacts.size })
166194
: t('contacts', 'Please select at least one editable contact to delete')
167195
},
196+
mergeActionTitle() {
197+
return this.areTwoEditable
198+
? t('contacts', 'Merge contacts')
199+
: t('contacts', 'Please select two editable contacts to merge')
200+
},
168201
},
169202
170203
watch: {
@@ -303,6 +336,30 @@ export default {
303336
this.unselectAllMultiSelected()
304337
this.showDeleteConfirmationDialog = false
305338
},
339+
340+
async initiateContactMerging() {
341+
// For every contact in the multiSelectedContacts, we need to dispatch the load contact action
342+
this.isMergingLoading = true
343+
const contacts = Array.from(this.multiSelectedContacts.values())
344+
for (const contact of contacts) {
345+
await this.$store.dispatch('fetchFullContact', { contact })
346+
}
347+
348+
this.isMergingLoading = false
349+
this.isMerging = true
350+
},
351+
352+
async finishContactMerging(mergedContact) {
353+
// After merging, we need to update the contact in the store
354+
await this.$store.dispatch('fetchFullContact', { contact: mergedContact, forceReFetch: true })
355+
356+
this.unselectAllMultiSelected()
357+
this.isMerging = false
358+
359+
await this.$router.push({
360+
name: 'root',
361+
})
362+
},
306363
},
307364
}
308365
</script>

0 commit comments

Comments
 (0)