diff --git a/App.tsx b/App.tsx
index 8b08269..2ffb0e4 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,5 +1,7 @@
import { StatusBar } from 'expo-status-bar';
import React, { useEffect } from 'react';
+import { Platform } from 'react-native';
+import * as NavigationBar from 'expo-navigation-bar';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { SessionProvider } from './src/auth/session';
import { getDb } from './src/db/database';
@@ -9,13 +11,20 @@ export default function App() {
// Abre y migra la BD local al arrancar.
useEffect(() => {
void getDb();
+
+ // Configuración global de la barra de navegación en Android (Edge-to-Edge)
+ if (Platform.OS === 'android') {
+ void NavigationBar.setPositionAsync('absolute');
+ void NavigationBar.setBackgroundColorAsync('#ffffff00'); // Transparente
+ void NavigationBar.setButtonStyleAsync('dark'); // Iconos oscuros por defecto
+ }
}, []);
return (
-
+
);
diff --git a/package-lock.json b/package-lock.json
index 0c5af0f..00ec35b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,7 @@
"expo-file-system": "~56.0.8",
"expo-image-picker": "~56.0.18",
"expo-location": "~56.0.18",
+ "expo-navigation-bar": "^57.0.2",
"expo-screen-orientation": "^57.0.1",
"expo-secure-store": "~56.0.4",
"expo-sqlite": "~56.0.5",
@@ -5911,6 +5912,20 @@
"react-native": "*"
}
},
+ "node_modules/expo-navigation-bar": {
+ "version": "57.0.2",
+ "resolved": "https://registry.npmjs.org/expo-navigation-bar/-/expo-navigation-bar-57.0.2.tgz",
+ "integrity": "sha512-Ufe49dsTZjarA7gMMwwwnNJWpXo+oTyl5Ud0g3lsGVrj2HXjyw9cb9098N8+CoWpjfIa5PghuRYYXrcCrGQIHg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.2"
+ },
+ "peerDependencies": {
+ "expo": "*",
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/expo-screen-orientation": {
"version": "57.0.1",
"resolved": "https://registry.npmjs.org/expo-screen-orientation/-/expo-screen-orientation-57.0.1.tgz",
diff --git a/package.json b/package.json
index 524a458..d36e215 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"expo-file-system": "~56.0.8",
"expo-image-picker": "~56.0.18",
"expo-location": "~56.0.18",
+ "expo-navigation-bar": "^57.0.2",
"expo-screen-orientation": "^57.0.1",
"expo-secure-store": "~56.0.4",
"expo-sqlite": "~56.0.5",
diff --git a/src/screens/CameraScreen.tsx b/src/screens/CameraScreen.tsx
index c754426..3c62795 100644
--- a/src/screens/CameraScreen.tsx
+++ b/src/screens/CameraScreen.tsx
@@ -1,6 +1,7 @@
import { CameraView, useCameraPermissions } from 'expo-camera';
import * as Location from 'expo-location';
import * as ScreenOrientation from 'expo-screen-orientation';
+import * as NavigationBar from 'expo-navigation-bar';
import { useFocusEffect } from '@react-navigation/native';
import React, { useCallback, useRef, useState } from 'react';
import {
@@ -10,6 +11,8 @@ import {
View,
ActivityIndicator,
useWindowDimensions,
+ Platform,
+ StatusBar,
} from 'react-native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { RootStackParamList } from '../navigation/types';
@@ -36,6 +39,13 @@ export function CameraScreen({ route, navigation }: Props) {
useCallback(() => {
let isMounted = true;
+ // Ocultar barras del sistema (Modo Inmersivo)
+ if (Platform.OS === 'android') {
+ void NavigationBar.setVisibilityAsync('hidden');
+ void NavigationBar.setBehaviorAsync('inset-touch');
+ }
+ StatusBar.setHidden(true);
+
const load = async () => {
const [cfg, projectId] = await Promise.all([
loadFooterConfig(),
@@ -95,6 +105,11 @@ export function CameraScreen({ route, navigation }: Props) {
return () => {
isMounted = false;
ScreenOrientation.removeOrientationChangeListener(sub);
+ // Restaurar barras del sistema al salir
+ if (Platform.OS === 'android') {
+ void NavigationBar.setVisibilityAsync('visible');
+ }
+ StatusBar.setHidden(false);
};
}, [])
);
diff --git a/src/screens/InspectionFormScreen.tsx b/src/screens/InspectionFormScreen.tsx
index 4778988..28c7bc5 100644
--- a/src/screens/InspectionFormScreen.tsx
+++ b/src/screens/InspectionFormScreen.tsx
@@ -14,16 +14,9 @@ import { getTemplates } from '../db/repositories';
import { createInspection } from '../sync/mutations';
import { getDb } from '../db/database';
import { newUuid, nextTempId } from '../sync/uuid';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { RootStackParamList } from '../navigation/types';
import { MediaStrip } from '../ui/MediaStrip';
-import {
- Card,
- ChipSelect,
- COLORS,
- Field,
- PrimaryButton,
- SectionTitle,
-} from '../ui/components';
type Props = NativeStackScreenProps;
@@ -78,6 +71,7 @@ function groupFields(fields: NormField[]): { group: string; items: NormField[] }
const RESULTS = ['pass', 'fail', 'na'] as const;
export function InspectionFormScreen({ route, navigation }: Props) {
+ const insets = useSafeAreaInsets();
const { featureId, featureName, templateId: suggestedId } = route.params;
// Paso 1: elegir plantilla (las asignadas al proyecto llegan en el bundle).
@@ -204,7 +198,7 @@ export function InspectionFormScreen({ route, navigation }: Props) {
// ── Paso 1: selector de plantilla ──
if (!chosen) {
return (
-
+
{featureName}
Elige una plantilla
@@ -241,7 +235,7 @@ export function InspectionFormScreen({ route, navigation }: Props) {
// ── Paso 2: formulario ──
return (
-
+
{featureName}
{template?.name ?? 'Inspección libre'}
diff --git a/src/screens/ProjectDetailScreen.tsx b/src/screens/ProjectDetailScreen.tsx
index 6589e4d..1edea90 100644
--- a/src/screens/ProjectDetailScreen.tsx
+++ b/src/screens/ProjectDetailScreen.tsx
@@ -7,6 +7,7 @@ import { getOutboxCounts, OutboxCounts } from '../db/outbox';
import { isOnline } from '../net/connectivity';
import { runSync } from '../sync/engine';
import { useAutoSync } from '../sync/useAutoSync';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { RootStackParamList } from '../navigation/types';
import { COLORS, PrimaryButton } from '../ui/components';
import { FeaturesSection } from './sections/FeaturesSection';
@@ -19,6 +20,7 @@ const TABS = ['Features', 'Incidencias'] as const;
type Tab = (typeof TABS)[number];
export function ProjectDetailScreen({ route, navigation }: Props) {
+ const insets = useSafeAreaInsets();
const { projectId } = route.params;
const [tab, setTab] = useState('Features');
const [counts, setCounts] = useState(EMPTY);
@@ -107,7 +109,7 @@ export function ProjectDetailScreen({ route, navigation }: Props) {
{tab === 'Incidencias' && }
-
+
void onSync()}
diff --git a/src/screens/ProjectsScreen.tsx b/src/screens/ProjectsScreen.tsx
index 5b334b2..c4449a3 100644
--- a/src/screens/ProjectsScreen.tsx
+++ b/src/screens/ProjectsScreen.tsx
@@ -18,6 +18,8 @@ import { runSync } from '../sync/engine';
import { RootStackParamList } from '../navigation/types';
import { COLORS } from '../ui/components';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
+
type Props = NativeStackScreenProps;
/** Tolerante con la forma de la respuesta: `{projects}` (actual), `{data}` o array. */
@@ -28,6 +30,7 @@ function normalize(res: unknown): Project[] {
}
export function ProjectsScreen({ navigation }: Props) {
+ const insets = useSafeAreaInsets();
const { user, signOut } = useSession();
const [projects, setProjects] = useState([]);
const [loading, setLoading] = useState(false);
@@ -105,7 +108,7 @@ export function ProjectsScreen({ navigation }: Props) {
};
return (
-
+
{/* Menu flotante (se posiciona relativo al contenedor principal) */}
{showMenu && (
diff --git a/src/screens/detail/FeatureDetailContent.tsx b/src/screens/detail/FeatureDetailContent.tsx
index 7b3b4fe..0f999ba 100644
--- a/src/screens/detail/FeatureDetailContent.tsx
+++ b/src/screens/detail/FeatureDetailContent.tsx
@@ -5,6 +5,7 @@ import { useFocusEffect, useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import React, { useCallback, useEffect, useState } from 'react';
import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { RootStackParamList } from '../../navigation/types';
import {
Badge,
@@ -17,6 +18,7 @@ import {
type Nav = NativeStackNavigationProp;
export function FeatureDetailContent({ featureId }: { featureId: number }) {
+ const insets = useSafeAreaInsets();
const navigation = useNavigation