vojo/src/app/plugins/polling.ts

66 lines
2.7 KiB
TypeScript

// Bridge to the native PollingPlugin (see
// android/app/src/main/java/chat/vojo/app/PollingPlugin.java).
//
// Drives the WorkManager-based /notifications polling fallback used on
// networks where FCM (mtalk.google.com:5228) is blocked. JS owns the
// credential + room-name cache lifecycle; native owns the periodic fetch
// and notification rendering.
//
// Web has no analogue: the Service Worker already wakes for push without
// needing a periodic poll, and browsers don't expose a 15-minute periodic
// background API anyway. The web fallback is a no-op.
import { registerPlugin } from '@capacitor/core';
import { isAndroidPlatform } from '../utils/capacitor';
export type RoomMetadataMap = Record<string, string | { name: string; isDirect: boolean }>;
interface PollingPluginIface {
saveSession(opts: { accessToken: string; homeserverUrl: string; userId?: string }): Promise<void>;
clearSession(): Promise<void>;
// Tolerant of both legacy (`roomId: "Display"`) and new structured shape
// (`roomId: { name, isDirect }`) — the Java side decides the message
// channel (DM vs group) based on the structured value when present.
saveRoomNames(opts: { names: RoomMetadataMap }): Promise<void>;
schedule(opts: { intervalMinutes: number }): Promise<void>;
cancel(): Promise<void>;
dismissRoom(opts: { roomId: string }): Promise<void>;
}
const noopPlugin: PollingPluginIface = {
saveSession: async () => undefined,
clearSession: async () => undefined,
saveRoomNames: async () => undefined,
schedule: async () => undefined,
cancel: async () => undefined,
dismissRoom: async () => undefined,
};
const plugin = registerPlugin<PollingPluginIface>('Polling', {
web: noopPlugin,
});
const guard = async <T>(fn: () => Promise<T>, fallback: T): Promise<T> => {
if (!isAndroidPlatform()) return fallback;
try {
return await fn();
} catch (err) {
// Old APK installed before the plugin shipped, or transient bridge
// error. Swallow — polling is a best-effort backup channel, not a
// hard dependency on the foreground push lifecycle.
// eslint-disable-next-line no-console
console.warn('[polling] native call failed:', err);
return fallback;
}
};
export const polling = {
saveSession: (opts: { accessToken: string; homeserverUrl: string; userId?: string }) =>
guard(() => plugin.saveSession(opts), undefined),
clearSession: () => guard(() => plugin.clearSession(), undefined),
saveRoomNames: (names: RoomMetadataMap) =>
guard(() => plugin.saveRoomNames({ names }), undefined),
schedule: (intervalMinutes = 15) => guard(() => plugin.schedule({ intervalMinutes }), undefined),
cancel: () => guard(() => plugin.cancel(), undefined),
dismissRoom: (roomId: string) => guard(() => plugin.dismissRoom({ roomId }), undefined),
};