65 lines
1.8 KiB
TypeScript
65 lines
1.8 KiB
TypeScript
/**
|
|
* Expo push notification provider.
|
|
*
|
|
* Uses the Expo push notification service REST API.
|
|
* No SDK dependency — just HTTP requests.
|
|
*/
|
|
|
|
import type { PushNotification, PushProvider, PushResult } from '../types.js';
|
|
|
|
const EXPO_PUSH_URL = 'https://exp.host/--/api/v2/push/send';
|
|
|
|
export class ExpoPushProvider implements PushProvider {
|
|
isConfigured(): boolean {
|
|
return true; // Expo push is open — no API key required for basic use
|
|
}
|
|
|
|
async send(notification: PushNotification): Promise<PushResult> {
|
|
const results = await this.sendBatch([notification]);
|
|
return results[0]!;
|
|
}
|
|
|
|
async sendBatch(notifications: PushNotification[]): Promise<PushResult[]> {
|
|
const messages = notifications.map(n => ({
|
|
to: n.deviceToken,
|
|
title: n.title,
|
|
body: n.body,
|
|
data: n.data,
|
|
badge: n.badge,
|
|
sound: n.sound ?? 'default',
|
|
channelId: n.channelId,
|
|
}));
|
|
|
|
try {
|
|
const response = await fetch(EXPO_PUSH_URL, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(messages),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const text = await response.text();
|
|
return notifications.map(() => ({
|
|
success: false,
|
|
error: `Expo push error ${response.status}: ${text}`,
|
|
}));
|
|
}
|
|
|
|
const data = (await response.json()) as {
|
|
data: Array<{ status: string; id?: string; message?: string }>;
|
|
};
|
|
|
|
return data.data.map(ticket => ({
|
|
success: ticket.status === 'ok',
|
|
messageId: ticket.id,
|
|
error: ticket.status !== 'ok' ? ticket.message : undefined,
|
|
}));
|
|
} catch (err) {
|
|
return notifications.map(() => ({
|
|
success: false,
|
|
error: err instanceof Error ? err.message : 'Unknown error',
|
|
}));
|
|
}
|
|
}
|
|
}
|