ultisuite-client/lib/api/contact-book-cache.ts
R3D347HR4Y 07d57f13a8
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
Add Contact Avatar Features and Improve UI Components
- Introduced new ContactAvatar and ContactAvatarPicker components for enhanced avatar management in contact views.
- Updated ContactDetailView and ContactFormView to utilize the new avatar components, improving user experience when adding or editing contacts.
- Enhanced ContactHoverCard and ContactRow components to display avatars, providing a more visually appealing interface.
- Added loading and error states in ContactsListView for better user feedback during data fetching.
- Implemented a new ContactsLoadState component to handle loading and error scenarios in the contacts list.
- Updated package.json to include @formkit/auto-animate for improved UI animations.
2026-06-06 20:26:51 +02:00

81 lines
2.4 KiB
TypeScript

import type { QueryClient } from '@tanstack/react-query'
import type { ApiContact } from '@/lib/api/types'
import { contactApiPath } from '@/lib/contacts/contact-api-path'
import type { FullContact } from '@/lib/contacts/types'
function contactMatchesPath(api: ApiContact, apiPath: string): boolean {
const normalizedPath = apiPath.replace(/^\/+/, '')
const contactPath = api.path?.replace(/^\/+/, '')
return (
contactPath === normalizedPath ||
api.uid === normalizedPath ||
contactPath?.endsWith(`/${normalizedPath}`) === true ||
contactPath?.endsWith(`/${normalizedPath}.vcf`) === true
)
}
/** Merge selection snapshot with latest React Query cache (etag, path). */
export function resolveContactForUpdate(
queryClient: QueryClient,
contact: FullContact,
): FullContact {
const api = findApiContactInCaches(queryClient, contact)
if (!api) return contact
const etag = contact.etag ?? api.etag
const path = contact.path ?? api.path
if (etag === contact.etag && path === contact.path) return contact
return { ...contact, etag, path }
}
export function findApiContactInCaches(
queryClient: QueryClient,
contact: Pick<FullContact, 'id' | 'path'>,
): ApiContact | undefined {
const apiPath = contactApiPath(contact)
for (const [, list] of queryClient.getQueriesData<ApiContact[]>({
queryKey: ['contacts'],
})) {
if (!list) continue
const hit = list.find(
(c) => c.uid === contact.id || contactMatchesPath(c, apiPath),
)
if (hit) return hit
}
return undefined
}
export function appendContactToBookCache(
queryClient: QueryClient,
bookId: string,
contact: ApiContact,
) {
if (!contact.uid && !contact.path) return
queryClient.setQueryData<ApiContact[]>(['contacts', bookId], (old) => {
const list = old ?? []
const uid = contact.uid
if (uid && list.some((item) => item.uid === uid)) return list
return [...list, contact]
})
}
export function replaceContactInBookCaches(
queryClient: QueryClient,
apiPath: string,
patch: Partial<ApiContact>,
) {
for (const [key, list] of queryClient.getQueriesData<ApiContact[]>({
queryKey: ['contacts'],
})) {
if (!list) continue
let changed = false
const next = list.map((item) => {
if (!contactMatchesPath(item, apiPath)) return item
changed = true
return { ...item, ...patch }
})
if (changed) {
queryClient.setQueryData(key, next)
}
}
}