Skip to content

Commit f7cfaab

Browse files
committed
feat: sort addressbooks
Signed-off-by: Grigory Vodyanov <[email protected]>
1 parent 66f0ea1 commit f7cfaab

File tree

4 files changed

+100
-6
lines changed

4 files changed

+100
-6
lines changed

src/components/AppNavigation/RootNavigation.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ export default {
274274
return this.$store.getters.getCircles
275275
},
276276
contacts() {
277+
const contacts = this.$store.getters.getContacts
278+
if (contacts.undefined) {
279+
contacts[this.selectedContact] = contacts.undefined
280+
delete contacts.undefined
281+
}
282+
277283
return this.$store.getters.getContacts
278284
},
279285
groups() {
@@ -285,7 +291,7 @@ export default {
285291
286292
// list all the contacts that doesn't have a group
287293
ungroupedContacts() {
288-
return this.sortedContacts.filter(contact => this.contacts[contact.key].groups && this.contacts[contact.key].groups.length === 0)
294+
return this.sortedContacts.filter(contact => this.contacts[contact.key]?.groups && this.contacts[contact.key]?.groups?.length === 0)
289295
},
290296
291297
// check if any contact has manager, if not then is no need for organization chart menu

src/components/ContactDetails.vue

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,12 +400,12 @@ import PropertySelect from './Properties/PropertySelect.vue'
400400
import { generateUrl } from '@nextcloud/router'
401401
import { loadState } from '@nextcloud/initial-state'
402402
import isTalkEnabled from '../services/isTalkEnabled.js'
403-
import { reactive, toRaw } from 'vue'
403+
import { reactive, toRaw, defineComponent } from 'vue'
404404
import IsMobileMixin from '../mixins/IsMobileMixin.ts'
405405
406406
const { profileEnabled } = loadState('user_status', 'profileEnabled', false)
407407
408-
export default {
408+
export default defineComponent({
409409
name: 'ContactDetails',
410410
411411
components: {
@@ -1051,10 +1051,12 @@ export default {
10511051
})
10521052
await this.updateContact()
10531053
if (this.newAddressBook && this.newAddressBook !== this.contact.addressbook.id) {
1054+
this.updateAddressBookAccesses(this.newAddressBook)
10541055
this.moveContactToAddressbook(this.newAddressBook)
10551056
this.newAddressBook = null
10561057
}
10571058
this.editMode = false
1059+
await this.$store.commit('resortAddressbooks')
10581060
} catch (error) {
10591061
this.logger.error('error while saving contact', { error })
10601062
showError(t('contacts', 'Unable to update contact'))
@@ -1065,8 +1067,15 @@ export default {
10651067
})
10661068
}
10671069
},
1070+
1071+
updateAddressBookAccesses(newAddressBook) {
1072+
const accesses = JSON.parse(localStorage.getItem('addressbook-accesses') || '{}')
1073+
accesses[newAddressBook] = new Date()
1074+
1075+
localStorage.setItem('addressbook-accesses', JSON.stringify(accesses))
1076+
},
10681077
},
1069-
}
1078+
})
10701079
</script>
10711080
10721081
<style lang="scss" scoped>

src/components/ContactsList.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,16 @@ export default {
160160
161161
computed: {
162162
filteredList() {
163-
const contactsList = this.list
163+
let contactsList = this.list
164164
.filter(item => this.matchSearch(this.contacts[item.key]))
165165
.map(item => this.contacts[item.key])
166166
167+
contactsList = contactsList.filter(item => item !== undefined)
168+
167169
contactsList.forEach((contact, index) => {
168-
contact.isMultiSelected = this.multiSelectedContacts.has(index)
170+
if (contact !== undefined) {
171+
contact.isMultiSelected = this.multiSelectedContacts.has(index)
172+
}
169173
})
170174
171175
return contactsList

src/store/addressbooks.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,71 @@ export function mapDavShareeToSharee(sharee) {
7070
}
7171
}
7272

73+
/**
74+
* Sorts addressbooks by rules:
75+
* 1. First group: Default personal addressbook ("contacts")
76+
* 2. Second group: Recently used (based on lastUsedAddressBooks) go second, ordered from newest to oldest
77+
* 3. Third group: Writeable enabled contacts, sorted by the amount of contacts they have, from more to less
78+
* 4. Fourth group: Read-only enabled contacts, sorted by the amount of contacts
79+
* 5. Fifth group: Disabled contacts, sorted by the amount of contacts
80+
*
81+
* @param {Array} addressbooks
82+
* @return {Array}
83+
*/
84+
export function sortAddressbooks(addressbooks) {
85+
const lastUsed = lastUsedAddressBooks(addressbooks)
86+
87+
return addressbooks
88+
.slice()
89+
.sort((a, b) => {
90+
const getContactCount = (ab) => Object.keys(ab.contacts || {}).length
91+
92+
const getPriorityGroup = (ab) => {
93+
if (ab.id === 'contacts') return 0
94+
if (ab.enabled === false) return 4
95+
if (lastUsed.includes(ab.id)) return 1
96+
if (ab.readOnly) return 3
97+
return 2
98+
};
99+
100+
const groupA = getPriorityGroup(a)
101+
const groupB = getPriorityGroup(b)
102+
103+
// First, sort by priority group
104+
if (groupA !== groupB) {
105+
return groupA - groupB
106+
}
107+
108+
// Within the same group, apply specific sorting rules
109+
if (groupA === 0) {
110+
return 0
111+
}
112+
else if (groupA === 1) {
113+
// Sort by position in lastUsed array (newest first)
114+
return lastUsed.indexOf(a.id) - lastUsed.indexOf(b.id)
115+
}
116+
else { // Groups 2, 3, 4 - sort by contact count (descending)
117+
const countA = getContactCount(a)
118+
const countB = getContactCount(b)
119+
120+
if (countA !== countB) {
121+
return countB - countA
122+
}
123+
124+
// If contact counts are equal, sort alphabetically by ID as tiebreaker
125+
return a.id.localeCompare(b.id)
126+
}
127+
})
128+
}
129+
130+
function lastUsedAddressBooks() {
131+
const accesses = JSON.parse(localStorage.getItem('addressbook-accesses') || '{}')
132+
133+
return Object.entries(accesses)
134+
.sort(([, a], [, b]) => new Date(b) - new Date(a))
135+
.map(([id]) => id)
136+
}
137+
73138
const mutations = {
74139

75140
/**
@@ -218,6 +283,14 @@ const mutations = {
218283
sharee.writeable = !sharee.writeable
219284
},
220285

286+
/**
287+
* Needed to track indirect state changes for addressbook sorting
288+
*
289+
* @param state
290+
*/
291+
resortAddressbooks(state) {
292+
state.addressbooks = sortAddressbooks(state.addressbooks)
293+
},
221294
}
222295

223296
const getters = {
@@ -246,6 +319,8 @@ const actions = {
246319
context.commit('addAddressbook', addressbook)
247320
})
248321

322+
context.commit('resortAddressbooks')
323+
249324
return addressbooks
250325
},
251326

0 commit comments

Comments
 (0)