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' ,
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 …')" >
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
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'
6379import ContactsListItem from ' ./ContactsList/ContactsListItem.vue'
6480import VirtualList from ' vue-virtual-scroll-list'
6581import IconSelect from ' vue-material-design-icons/CloseThick.vue'
6682import 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
6887import 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