Finalize background photo processing and orientation fixes
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { CameraView, useCameraPermissions } from 'expo-camera';
|
||||
import * as Location from 'expo-location';
|
||||
import * as ScreenOrientation from 'expo-screen-orientation';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
@@ -30,66 +31,73 @@ export function CameraScreen({ route, navigation }: Props) {
|
||||
const [taking, setTaking] = useState(false);
|
||||
const [rotation, setRotation] = useState(0);
|
||||
|
||||
// Detectar orientación física para rotar el sello
|
||||
useEffect(() => {
|
||||
const updateOrientation = async () => {
|
||||
const info = await ScreenOrientation.getOrientationAsync();
|
||||
handleOrientation(info);
|
||||
};
|
||||
// Carga de datos cada vez que la pantalla gana el foco
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
let isMounted = true;
|
||||
|
||||
const handleOrientation = (o: ScreenOrientation.Orientation) => {
|
||||
// Nota: LANDSCAPE_LEFT es rotar el móvil a la derecha (home button a la derecha)
|
||||
// Queremos contrarrestar el giro para que el sello esté derecho.
|
||||
if (o === ScreenOrientation.Orientation.LANDSCAPE_LEFT) setRotation(90);
|
||||
else if (o === ScreenOrientation.Orientation.LANDSCAPE_RIGHT) setRotation(-90);
|
||||
else if (o === ScreenOrientation.Orientation.PORTRAIT_UPSIDE_DOWN) setRotation(180);
|
||||
else setRotation(0);
|
||||
};
|
||||
const load = async () => {
|
||||
const [cfg, projectId] = await Promise.all([
|
||||
loadFooterConfig(),
|
||||
getActiveProjectId(),
|
||||
]);
|
||||
if (!isMounted) return;
|
||||
setConfig(cfg);
|
||||
|
||||
void updateOrientation();
|
||||
const sub = ScreenOrientation.addOrientationChangeListener((evt) => {
|
||||
handleOrientation(evt.orientationInfo.orientation);
|
||||
});
|
||||
return () => ScreenOrientation.removeOrientationChangeListener(sub);
|
||||
}, []);
|
||||
let projectName: string | null = null;
|
||||
if (projectId != null) {
|
||||
const proj = await getProject(projectId);
|
||||
projectName = proj?.name ?? null;
|
||||
}
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
const [cfg, projectId] = await Promise.all([
|
||||
loadFooterConfig(),
|
||||
getActiveProjectId(),
|
||||
]);
|
||||
setConfig(cfg);
|
||||
const { granted } = await Location.requestForegroundPermissionsAsync();
|
||||
let coordsStr: string | null = null;
|
||||
if (granted) {
|
||||
const loc = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.Balanced });
|
||||
const lat = loc.coords.latitude;
|
||||
const lng = loc.coords.longitude;
|
||||
const la = lat >= 0 ? 'N' : 'S';
|
||||
const lo = lng >= 0 ? 'E' : 'O';
|
||||
coordsStr = `${Math.abs(lat).toFixed(5)}°${la}, ${Math.abs(lng).toFixed(5)}°${lo}`;
|
||||
}
|
||||
|
||||
let projectName: string | null = null;
|
||||
if (projectId != null) {
|
||||
const proj = await getProject(projectId);
|
||||
projectName = proj?.name ?? null;
|
||||
}
|
||||
if (isMounted) {
|
||||
setMeta({
|
||||
projectName,
|
||||
date: new Date().toLocaleString('es-ES', {
|
||||
year: 'numeric', month: '2-digit', day: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit',
|
||||
}),
|
||||
coordinates: coordsStr,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const { granted } = await Location.requestForegroundPermissionsAsync();
|
||||
let coordsStr: string | null = null;
|
||||
if (granted) {
|
||||
const loc = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.Balanced });
|
||||
const lat = loc.coords.latitude;
|
||||
const lng = loc.coords.longitude;
|
||||
const la = lat >= 0 ? 'N' : 'S';
|
||||
const lo = lng >= 0 ? 'E' : 'O';
|
||||
coordsStr = `${Math.abs(lat).toFixed(5)}°${la}, ${Math.abs(lng).toFixed(5)}°${lo}`;
|
||||
}
|
||||
const syncOrientation = async () => {
|
||||
const info = await ScreenOrientation.getOrientationAsync();
|
||||
handleOrientation(info);
|
||||
};
|
||||
|
||||
setMeta({
|
||||
projectName,
|
||||
date: new Date().toLocaleString('es-ES', {
|
||||
year: 'numeric', month: '2-digit', day: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit',
|
||||
}),
|
||||
coordinates: coordsStr,
|
||||
});
|
||||
}, []);
|
||||
const handleOrientation = (o: ScreenOrientation.Orientation) => {
|
||||
if (o === ScreenOrientation.Orientation.LANDSCAPE_LEFT) setRotation(90);
|
||||
else if (o === ScreenOrientation.Orientation.LANDSCAPE_RIGHT) setRotation(-90);
|
||||
else if (o === ScreenOrientation.Orientation.PORTRAIT_UPSIDE_DOWN) setRotation(180);
|
||||
else setRotation(0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
void loadData();
|
||||
}, [loadData]);
|
||||
void load();
|
||||
void syncOrientation();
|
||||
|
||||
const sub = ScreenOrientation.addOrientationChangeListener((evt) => {
|
||||
handleOrientation(evt.orientationInfo.orientation);
|
||||
});
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
ScreenOrientation.removeOrientationChangeListener(sub);
|
||||
};
|
||||
}, [])
|
||||
);
|
||||
|
||||
if (!permission) {
|
||||
return <View style={styles.center}><ActivityIndicator size="large" /></View>;
|
||||
@@ -111,9 +119,9 @@ export function CameraScreen({ route, navigation }: Props) {
|
||||
setTaking(true);
|
||||
try {
|
||||
const photo = await cameraRef.current.takePictureAsync({
|
||||
quality: config?.quality ?? 0.85,
|
||||
quality: 0.9, // Máxima calidad para el raw
|
||||
base64: false,
|
||||
exif: false,
|
||||
exif: true,
|
||||
});
|
||||
if (photo) {
|
||||
onCapture(photo.uri, photo.width, photo.height);
|
||||
@@ -135,7 +143,6 @@ export function CameraScreen({ route, navigation }: Props) {
|
||||
autofocus="on"
|
||||
/>
|
||||
|
||||
{/* Capa de UI sobre la cámara */}
|
||||
<View style={styles.overlay}>
|
||||
{/* Header con botón cerrar */}
|
||||
<View style={styles.header}>
|
||||
@@ -144,7 +151,7 @@ export function CameraScreen({ route, navigation }: Props) {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Preview del sello (posicionado en su esquina con rotación) */}
|
||||
{/* Preview del sello: posicionado por FooterOverlay, no por este contenedor */}
|
||||
{config && meta && (
|
||||
<View style={styles.fullOverlay} pointerEvents="none">
|
||||
<FooterOverlay
|
||||
@@ -156,7 +163,7 @@ export function CameraScreen({ route, navigation }: Props) {
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Botón de disparo abajo al centro */}
|
||||
{/* Botón de disparo */}
|
||||
<View style={styles.footerControls}>
|
||||
<TouchableOpacity
|
||||
style={[styles.captureBtn, taking && styles.disabled]}
|
||||
@@ -176,14 +183,15 @@ const styles = StyleSheet.create({
|
||||
overlay: { flex: 1, justifyContent: 'space-between', zIndex: 10 },
|
||||
fullOverlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
// No centramos aquí, dejamos que FooterOverlay use sus offsets top/bottom/left/right
|
||||
// IMPORTANTE: Eliminado justifyContent: center.
|
||||
// Ahora FooterOverlay se anclará a las esquinas correctamente.
|
||||
},
|
||||
center: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#000' },
|
||||
message: { color: '#fff', textAlign: 'center', marginBottom: 20 },
|
||||
btn: { backgroundColor: COLORS.primary, padding: 12, borderRadius: 8 },
|
||||
btnText: { color: '#fff', fontWeight: 'bold' },
|
||||
header: { padding: 20, paddingTop: 50 },
|
||||
closeBtn: { width: 40, height: 40, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.3)', borderRadius: 20 },
|
||||
closeBtn: { width: 44, height: 44, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.4)', borderRadius: 22 },
|
||||
closeTxt: { color: '#fff', fontSize: 24 },
|
||||
footerControls: {
|
||||
paddingBottom: 40,
|
||||
@@ -191,18 +199,18 @@ const styles = StyleSheet.create({
|
||||
width: '100%',
|
||||
},
|
||||
captureBtn: {
|
||||
width: 76,
|
||||
height: 70, // Un poco ovalado para que se note
|
||||
borderRadius: 38,
|
||||
width: 74,
|
||||
height: 74,
|
||||
borderRadius: 37,
|
||||
borderWidth: 5,
|
||||
borderColor: '#fff',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
captureInner: {
|
||||
width: 54,
|
||||
height: 54,
|
||||
borderRadius: 27,
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: 28,
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
disabled: { opacity: 0.5 },
|
||||
|
||||
Reference in New Issue
Block a user