New asynchronous way to synchronize from addressbook.

This commit is contained in:
Antoine Nguyen
2025-12-12 16:47:22 +01:00
parent ec66d050da
commit c13f4082d0
7 changed files with 204 additions and 119 deletions
+159 -103
View File
@@ -1,97 +1,120 @@
<template>
<v-card class="mt-6">
<v-data-table :headers="headers" :items="contacts">
<template #top>
<v-toolbar color="white" flat>
<v-text-field
v-model="search"
prepend-inner-icon="mdi-magnify"
:placeholder="$gettext('Search')"
variant="outlined"
single-line
flat
hide-details
density="compact"
class="flex-grow-0 w-33 mr-4"
></v-text-field>
<v-btn color="primary" variant="outlined" @click="showForm = true">
{{ $gettext('Add') }}
</v-btn>
<template v-if="preferences.enable_carddav_sync">
<v-btn
icon="mdi-information-outline"
:title="$gettext('Display address book information')"
class="ml-2"
@click="showAddressBookInfo = true"
/>
<v-btn
v-if="!addressBook.last_sync"
icon="mdi-sync"
color="success"
:title="$gettext('Synchronize contacts with address book')"
@click="syncToAddressBook"
/>
</template>
<v-chip
v-if="$route.params.category"
class="my-2 mx-4"
label
size="small"
color="green"
prepend-icon="mdi-label"
@click="editCategory"
>
{{ $route.params.category }}
</v-chip>
<v-spacer></v-spacer>
</v-toolbar>
</template>
<template #[`item.display_name`]="{ item }">
<template v-if="item.display_name">
{{ item.display_name }}
</template>
<template v-else> {{ item.first_name }} {{ item.last_name }} </template>
</template>
<template #[`item.emails`]="{ item }">
{{ displayEmails(item.emails) }}
</template>
<template #[`item.phone_numbers`]="{ item }">
{{ displayPhoneNumbers(item.phone_numbers) }}
</template>
<template #[`item.actions`]="{ item }">
<div class="text-right">
<v-menu offset-y>
<template #activator="{ props }">
<v-btn icon="mdi-dots-horizontal" variant="text" v-bind="props">
<div>
<v-card class="mt-6">
<v-data-table :headers="headers" :items="contacts">
<template #top>
<v-toolbar color="white" flat>
<v-text-field
v-model="search"
prepend-inner-icon="mdi-magnify"
:placeholder="$gettext('Search')"
variant="outlined"
single-line
flat
hide-details
density="compact"
class="flex-grow-0 w-33 mr-4"
></v-text-field>
<v-btn color="primary" variant="outlined" @click="showForm = true">
{{ $gettext('Add') }}
</v-btn>
<template v-if="preferences.enable_carddav_sync">
<v-btn class="ml-2" variant="tonal" icon size="small">
<v-icon icon="mdi-cog" />
<v-menu activator="parent">
<v-list density="compact">
<v-list-item
v-if="!addressBook.last_sync"
color="success"
:title="
$gettext('Synchronize contacts with address book')
"
prepend-icon="mdi-sync"
@click="syncToAddressBook"
/>
<v-list-item
:title="$gettext('Synchronize')"
prepend-icon="mdi-sync"
:disabled="addressBook.syncing"
@click="syncFromAddressBook(true)"
/>
</v-list>
</v-menu>
</v-btn>
<v-btn
icon="mdi-information-outline"
:title="$gettext('Display address book information')"
@click="showAddressBookInfo = true"
/>
</template>
<MenuItems :items="contactActions" :obj="item" />
</v-menu>
</div>
</template>
</v-data-table>
</v-card>
<v-dialog v-model="showForm" persistent max-width="800px">
<ContactForm
:contact="selectedContact"
@close="closeForm"
@added="fetchContacts"
@updated="fetchContacts"
/>
</v-dialog>
<v-dialog v-model="showCategoriesForm" persistent max-width="800px">
<ContactCategoriesForm
:contact="selectedContact"
@updated="fetchContacts"
@close="closeCategoriesForm"
/>
</v-dialog>
<v-dialog v-model="showAddressBookInfo" persistent max-width="800px">
<AddressBookInfo
:address-book-url="addressBook.url"
@close="showAddressBookInfo = false"
/>
</v-dialog>
<v-chip
v-if="$route.params.category"
class="my-2 mx-4"
label
size="small"
color="green"
prepend-icon="mdi-label"
@click="editCategory"
>
{{ $route.params.category }}
</v-chip>
<v-spacer></v-spacer>
<v-progress-circular
v-if="addressBook.syncing"
color="primary"
indeterminate
></v-progress-circular>
</v-toolbar>
</template>
<template #[`item.display_name`]="{ item }">
<template v-if="item.display_name">
{{ item.display_name }}
</template>
<template v-else>
{{ item.first_name }} {{ item.last_name }}
</template>
</template>
<template #[`item.emails`]="{ item }">
{{ displayEmails(item.emails) }}
</template>
<template #[`item.phone_numbers`]="{ item }">
{{ displayPhoneNumbers(item.phone_numbers) }}
</template>
<template #[`item.actions`]="{ item }">
<div class="text-right">
<v-menu offset-y>
<template #activator="{ props }">
<v-btn icon="mdi-dots-horizontal" variant="text" v-bind="props">
</v-btn>
</template>
<MenuItems :items="contactActions" :obj="item" />
</v-menu>
</div>
</template>
</v-data-table>
</v-card>
<v-dialog v-model="showForm" persistent max-width="800px">
<ContactForm
:contact="selectedContact"
@close="closeForm"
@added="fetchContacts"
@updated="fetchContacts"
/>
</v-dialog>
<v-dialog v-model="showCategoriesForm" persistent max-width="800px">
<ContactCategoriesForm
:contact="selectedContact"
@updated="fetchContacts"
@close="closeCategoriesForm"
/>
</v-dialog>
<v-dialog v-model="showAddressBookInfo" persistent max-width="800px">
<AddressBookInfo
:address-book-url="addressBook.url"
@close="showAddressBookInfo = false"
/>
</v-dialog>
</div>
</template>
<script setup>
@@ -191,8 +214,18 @@ function deleteContact(contact) {
})
}
async function fetchAddressBook() {
const resp = await contactsApi.getDefaultAddressBook()
addressBook.value = resp.data
}
async function fetchPreferences() {
const resp = await parametersApi.getUserApplication('contacts')
preferences.value = resp.data.params
}
function fetchContacts() {
contactsApi.getContacts(route.params).then((resp) => {
return contactsApi.getContacts(route.params).then((resp) => {
contacts.value = resp.data
})
}
@@ -203,20 +236,43 @@ function syncToAddressBook() {
})
}
contactsApi.getDefaultAddressBook().then((resp) => {
addressBook.value = resp.data
})
parametersApi.getUserApplication('contacts').then((resp) => {
preferences.value = resp.data.params
})
fetchContacts()
async function monitorAddressBook() {
await fetchAddressBook()
await fetchContacts()
if (!addressBook.value.syncing) {
clearInterval(intervalId)
intervalId = null
startAutomaticSync()
}
}
async function syncFromAddressBook(withNotification) {
if (intervalId) {
// Stop automatic sync for now.
clearInterval(intervalId)
}
await contactsApi.synchronizeFromAddressBook()
if (withNotification) {
displayNotification({ msg: $gettext('Synchronization started') })
}
addressBook.value.syncing = true
intervalId = setInterval(monitorAddressBook, 5000)
}
function startAutomaticSync() {
intervalId = setInterval(
syncFromAddressBook,
preferences.value.sync_frequency * 1000
)
}
await fetchAddressBook()
await fetchPreferences()
await fetchContacts()
watch(preferences, (value) => {
if (value) {
intervalId = setInterval(
contactsApi.synchronizeFromAddressBook,
value.sync_frequency * 1000
)
startAutomaticSync()
}
})
+13 -11
View File
@@ -1,16 +1,18 @@
<template>
<div class="text-h5 ml-4">
{{ $gettext('Contacts') }}
<div>
<div class="text-h5 ml-4">
{{ $gettext('Contacts') }}
</div>
<ContactList @edit-category="openCategoryForm" />
<v-dialog v-model="showCategoryForm" persistent max-width="800px">
<CategoryForm
:category="contactsStore.currentCategory"
@close="closeForm"
@added="fetchCategories"
@updated="fetchCategories"
/>
</v-dialog>
</div>
<ContactList @edit-category="openCategoryForm" />
<v-dialog v-model="showCategoryForm" persistent max-width="800px">
<CategoryForm
:category="contactsStore.currentCategory"
@close="closeForm"
@added="fetchCategories"
@updated="fetchCategories"
/>
</v-dialog>
</template>
<script setup>