BigBrainParking/app/modules/bbp-notify/index.ts
Erik 463facbe5a
All checks were successful
build-apk / build (push) Successful in 40m22s
v0.3.0: local free check-in with actionable countdown notification (Phase C)
For a zone labeled free_2h/3h/4h, "Check in (free · Xh)" starts a local, API-free
countdown to the free limit — no ParkSmarter call. The bbp-notify module gains a
showCheckin() that posts the ticking chronometer with "End" / "Pay" buttons, a
BbpActionReceiver (declared in the module's new AndroidManifest.xml) that records
the tap in SharedPreferences and, for Pay, relaunches the app; consumePendingAction()
is drained on foreground by useCheckinSync() — End clears the check-in, Pay hands
off to StartSession for the stored zone. A pre-expiry alert reuses the existing
reminder lead-minutes. Starting a paid session supersedes an active check-in.

Also: MeterDetail shows the free/pay-immediate label and (admins) labeling chips.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 18:23:58 +00:00

64 lines
2 KiB
TypeScript

import { Platform } from 'react-native';
import { requireOptionalNativeModule } from 'expo-modules-core';
interface BbpNotifyNative {
/**
* Post/replace an ongoing notification with a native chronometer counting down
* to endTimeMillis. Returns a short diagnostic string (e.g. "posted enabled=true").
*/
showCountdown(title: string, body: string, endTimeMillis: number): Promise<string>;
/** Same ticking countdown, plus "End" / "Pay" action buttons. */
showCheckin(
title: string,
body: string,
endTimeMillis: number,
endLabel: string,
payLabel: string,
): Promise<string>;
/** Remove the countdown notification. */
clear(): Promise<void>;
/** Read + clear the action a notification button set: 'end' | 'pay' | ''. */
consumePendingAction(): Promise<string>;
}
export type PendingAction = 'end' | 'pay' | null;
// Android-only, and only present in a build that includes the native module
// (returns null in Expo Go / other platforms — callers degrade gracefully).
const native =
Platform.OS === 'android'
? (requireOptionalNativeModule('BbpNotify') as BbpNotifyNative | null)
: null;
/** True when the native ticking-countdown module is available. */
export const hasNativeCountdown = native != null;
export async function showCountdown(
title: string,
body: string,
endTimeMillis: number,
): Promise<string> {
if (!native) return 'no-native-module';
return native.showCountdown(title, body, endTimeMillis);
}
export async function showCheckin(
title: string,
body: string,
endTimeMillis: number,
endLabel: string,
payLabel: string,
): Promise<string> {
if (!native) return 'no-native-module';
return native.showCheckin(title, body, endTimeMillis, endLabel, payLabel);
}
export async function clearCountdown(): Promise<void> {
await native?.clear();
}
/** Read + clear a notification-button action taken while the app was away. */
export async function consumePendingAction(): Promise<PendingAction> {
const a = await native?.consumePendingAction();
return a === 'end' || a === 'pay' ? a : null;
}