CI / quality (push) Waiting to run
- Add src/core/ (config, errors, events, http, observability, storage, query) - Add src/Spreadsheet/ engine with native POI backend and Excel bridge - Add Envelope C spreadsheet flow and tender bidding envelope modules - Consolidate components into src/components/, remove legacy Component/ and Hooks/ - Add registration state/validation, auth session guard, profile edit modules - Add unit tests across axios, redux, bidding, registration, encryption, core - Add CI workflow, docs (architecture, audit, DSC API spec), keystore example - Remove tracked keystore binary; ignore scripts.zip Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
100 lines
2.5 KiB
TypeScript
100 lines
2.5 KiB
TypeScript
/**
|
|
* Centralized permission broker — screens must request permissions through
|
|
* these helpers, never react-native-permissions directly.
|
|
*/
|
|
import {
|
|
check,
|
|
request,
|
|
RESULTS,
|
|
PermissionStatus,
|
|
openSettings,
|
|
PERMISSIONS,
|
|
Permission,
|
|
checkNotifications,
|
|
requestNotifications,
|
|
} from 'react-native-permissions';
|
|
import { Platform } from 'react-native';
|
|
import { logger } from '../core/observability/logger';
|
|
|
|
// Normalize permission for platform
|
|
const normalizePermission = (perm: string): Permission => {
|
|
return Platform.select({
|
|
ios: (PERMISSIONS.IOS as Record<string, string>)[perm] || perm,
|
|
android: (PERMISSIONS.ANDROID as Record<string, string>)[perm] || perm,
|
|
default: perm,
|
|
}) as Permission;
|
|
};
|
|
|
|
// Generic permission requester
|
|
export const requestSinglePermission = async (
|
|
permission: string
|
|
): Promise<boolean> => {
|
|
const normalized = normalizePermission(permission);
|
|
|
|
const status: PermissionStatus = await check(normalized);
|
|
|
|
if (status === RESULTS.GRANTED) {
|
|
return true;
|
|
}
|
|
|
|
if (status === RESULTS.BLOCKED) {
|
|
logger.info('permissions', `${permission} is blocked — opening settings`);
|
|
openSettings();
|
|
return false;
|
|
}
|
|
|
|
if (status === RESULTS.DENIED || status === RESULTS.LIMITED) {
|
|
const result: PermissionStatus = await request(normalized);
|
|
return result === RESULTS.GRANTED;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
export const requestNotificationPermission = async (): Promise<boolean> => {
|
|
if (Platform.OS !== 'android') {
|
|
return true;
|
|
}
|
|
|
|
const { status } = await checkNotifications();
|
|
|
|
if (status === RESULTS.GRANTED) {
|
|
return true;
|
|
}
|
|
|
|
if (status === RESULTS.BLOCKED) {
|
|
logger.info('permissions', 'notifications blocked — opening settings');
|
|
openSettings();
|
|
return false;
|
|
}
|
|
|
|
if (status === RESULTS.DENIED) {
|
|
const { status: result } = await requestNotifications(['alert', 'sound']);
|
|
return result === RESULTS.GRANTED;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
export const isAnyPermissionGranted = async (permissions: Permission[]): Promise<boolean> => {
|
|
for (const permission of permissions) {
|
|
const result = await check(permission);
|
|
|
|
if (result === RESULTS.GRANTED) {
|
|
return true; // At least one permission is granted
|
|
}
|
|
}
|
|
|
|
return false; // No permissions granted
|
|
};
|
|
|
|
export async function checkPermission(permission: Permission) {
|
|
try {
|
|
const result = await check(permission);
|
|
return result === RESULTS.GRANTED;
|
|
} catch (error) {
|
|
logger.warn('permissions', 'permission check failed', { error: String(error) });
|
|
return false;
|
|
}
|
|
}
|