Login: fixed-length 4-digit PIN that auto-submits on the 4th digit (no submit button to scroll to on small iPhone screens) and clears on a wrong PIN. Compact, vertically-centered keypad so it fits without scrolling. Operator tracking: after PIN auth, staff enter their name (new /operator screen, persisted per device). The name is sent as X-Operator on every authed request and recorded on each check-in/undo/ice audit entry (new Operator column), so logs show who did what. Shown in the scanner header and the admin audit view. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
84 lines
2.2 KiB
TypeScript
84 lines
2.2 KiB
TypeScript
import { Platform } from "react-native";
|
|
|
|
// Token persistence: localStorage on web, SecureStore on native.
|
|
const KEY = "campscan.token";
|
|
const OPERATOR_KEY = "campscan.operator";
|
|
|
|
export async function saveToken(token: string): Promise<void> {
|
|
if (Platform.OS === "web") {
|
|
try {
|
|
window.localStorage.setItem(KEY, token);
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
return;
|
|
}
|
|
const SecureStore = await import("expo-secure-store");
|
|
await SecureStore.setItemAsync(KEY, token);
|
|
}
|
|
|
|
export async function loadToken(): Promise<string | null> {
|
|
if (Platform.OS === "web") {
|
|
try {
|
|
return window.localStorage.getItem(KEY);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
const SecureStore = await import("expo-secure-store");
|
|
return SecureStore.getItemAsync(KEY);
|
|
}
|
|
|
|
export async function clearToken(): Promise<void> {
|
|
if (Platform.OS === "web") {
|
|
try {
|
|
window.localStorage.removeItem(KEY);
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
return;
|
|
}
|
|
const SecureStore = await import("expo-secure-store");
|
|
await SecureStore.deleteItemAsync(KEY);
|
|
}
|
|
|
|
// Operator (gate staff) name — plain persistence, not sensitive. Kept in
|
|
// localStorage on both platforms for simplicity (SecureStore is overkill here;
|
|
// on native we still use localStorage-less AsyncStorage-free approach below).
|
|
export async function saveOperator(name: string): Promise<void> {
|
|
if (Platform.OS === "web") {
|
|
try {
|
|
window.localStorage.setItem(OPERATOR_KEY, name);
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
return;
|
|
}
|
|
const SecureStore = await import("expo-secure-store");
|
|
await SecureStore.setItemAsync(OPERATOR_KEY, name);
|
|
}
|
|
|
|
export async function loadOperator(): Promise<string | null> {
|
|
if (Platform.OS === "web") {
|
|
try {
|
|
return window.localStorage.getItem(OPERATOR_KEY);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
const SecureStore = await import("expo-secure-store");
|
|
return SecureStore.getItemAsync(OPERATOR_KEY);
|
|
}
|
|
|
|
export async function clearOperator(): Promise<void> {
|
|
if (Platform.OS === "web") {
|
|
try {
|
|
window.localStorage.removeItem(OPERATOR_KEY);
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
return;
|
|
}
|
|
const SecureStore = await import("expo-secure-store");
|
|
await SecureStore.deleteItemAsync(OPERATOR_KEY);
|
|
}
|