61 lines
2.3 KiB
TypeScript
61 lines
2.3 KiB
TypeScript
import type { CallEvent } from './swyx/protocol'
|
||
|
||
/** Ein gespeicherter eingehender Anruf aus GET /api/calls. */
|
||
export interface StoredCall {
|
||
id: string
|
||
receivedAt: string
|
||
line: number | null
|
||
peer: string | null
|
||
peerNumber: string | null
|
||
peerName: string | null
|
||
}
|
||
|
||
export async function fetchIncomingCalls(signal?: AbortSignal): Promise<StoredCall[]> {
|
||
const response = await fetch('/api/calls', { signal, cache: 'no-store' })
|
||
if (!response.ok) {
|
||
throw new Error(`Anrufhistorie nicht abrufbar (HTTP ${response.status}).`)
|
||
}
|
||
return (await response.json()) as StoredCall[]
|
||
}
|
||
|
||
/** Ältere Snapshots enthalten die Rufnummer teilweise nur in der Anzeigeform. */
|
||
export function callerNumber(call: StoredCall): string | null {
|
||
const number = call.peerNumber?.trim()
|
||
if (number) return number
|
||
const peer = call.peer?.trim() ?? ''
|
||
const candidate = peer.match(/\(([^()]+)\)$/)?.[1] ?? peer
|
||
return /^[+\d][\d\s()/.-]*$/.test(candidate) ? candidate : null
|
||
}
|
||
|
||
export function matchesCall(call: StoredCall, query: string): boolean {
|
||
const wanted = query.trim().toLocaleLowerCase('de')
|
||
if (!wanted) return true
|
||
const text = [callerNumber(call), call.peerName, call.peer].filter(Boolean).join(' ')
|
||
if (text.toLocaleLowerCase('de').includes(wanted)) return true
|
||
// Rufnummern auch ohne die Leerzeichen/Klammern ihrer Anzeigeform finden.
|
||
const digits = wanted.replace(/\D/g, '')
|
||
return /^[+\d\s()/.-]+$/.test(wanted) && digits.length > 0 &&
|
||
(callerNumber(call)?.replace(/\D/g, '').includes(digits) ?? false)
|
||
}
|
||
|
||
/**
|
||
* Meldet Anruf-Ereignisse an das Backend, das sie in der MongoDB ablegt.
|
||
*
|
||
* Die Ereignisse entstehen nur hier im Browser (aus dem Vergleich zweier
|
||
* SwyxTray-Snapshots); das Backend sieht die WebSocket-Verbindung nicht.
|
||
* Fire-and-forget: Ist das Backend nicht erreichbar, läuft die Anzeige
|
||
* unverändert weiter – die Speicherung ist Beiwerk, nicht Voraussetzung.
|
||
*/
|
||
export function reportCallEvents(events: CallEvent[]): void {
|
||
if (events.length === 0) return
|
||
void fetch('/api/calls', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(events),
|
||
// Auch beim Schließen des Tabs noch absetzen, z. B. das "ended" beim Auflegen.
|
||
keepalive: true,
|
||
}).catch((e: unknown) => {
|
||
console.warn('Anrufdaten nicht an das Backend gemeldet:', e)
|
||
})
|
||
}
|