ultisuite-client/lib/native/contacts.ts
R3D347HR4Y d6d18f911b
Some checks failed
E2E / Playwright e2e (push) Has been cancelled
Lots of stuff and mobile app
2026-06-17 00:13:28 +02:00

53 lines
1.8 KiB
TypeScript

/**
* Device contacts import (mobile). Reads the OS address book via the
* `ulti-core` contacts plugin (Android ContactsContract / iOS Contacts) and
* maps to the shared `ContactImportInput` shape used by the import dialog.
*/
import { invoke } from "@/lib/native/bridge"
import { isTauriRuntime } from "@/lib/platform"
import type { ContactImportInput } from "@/lib/contacts/import-parsers"
type DeviceContact = {
display_name?: string | null
first_name?: string | null
last_name?: string | null
emails: string[]
phones: string[]
organization?: string | null
}
function splitDisplayName(name: string): { firstName: string; lastName: string } {
const parts = name.trim().split(/\s+/)
if (parts.length <= 1) return { firstName: parts[0] ?? "", lastName: "" }
return { firstName: parts[0], lastName: parts.slice(1).join(" ") }
}
function toImportInput(c: DeviceContact): ContactImportInput {
let firstName = c.first_name ?? ""
let lastName = c.last_name ?? ""
if (!firstName && !lastName && c.display_name) {
const parts = splitDisplayName(c.display_name)
firstName = parts.firstName
lastName = parts.lastName
}
return {
firstName,
lastName,
company: c.organization ?? undefined,
emails: c.emails.map((value) => ({ value, label: "personal" })),
phones: c.phones.map((value) => ({ value, label: "mobile" })),
}
}
/** True when device contacts import is available (native runtime). */
export function deviceContactsAvailable(): boolean {
return isTauriRuntime()
}
/** Fetch device contacts. Throws a coded error if unsupported/denied. */
export async function fetchDeviceContacts(): Promise<ContactImportInput[]> {
const list = await invoke<DeviceContact[]>("plugin:ulti-core|contacts_fetch")
if (!list) throw new Error("contacts_unavailable")
return list.map(toImportInput)
}