Compare commits
2
Commits
dbd6fd1bbf
...
014536d5ec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
014536d5ec | ||
|
|
d6a3d4651b |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -94,6 +94,7 @@ export default function CameraScreen({
|
|||||||
const usbCameraRef = useRef<UsbCameraRef>(null);
|
const usbCameraRef = useRef<UsbCameraRef>(null);
|
||||||
const expoCameraRef = useRef<any>(null);
|
const expoCameraRef = useRef<any>(null);
|
||||||
const burstTimerRef = useRef<any>(null);
|
const burstTimerRef = useRef<any>(null);
|
||||||
|
const countdownTimerRef = useRef<any>(null);
|
||||||
const isCancelledRef = useRef<boolean>(false);
|
const isCancelledRef = useRef<boolean>(false);
|
||||||
|
|
||||||
// Reanimated countdown pulse animation
|
// Reanimated countdown pulse animation
|
||||||
@@ -159,11 +160,12 @@ export default function CameraScreen({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
|
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
|
||||||
|
if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Set safe picture size to avoid OOM
|
// Set safe picture size to avoid OOM
|
||||||
const handleCameraReady = async () => {
|
const handleCameraReady = useCallback(async () => {
|
||||||
if (expoCameraRef.current) {
|
if (expoCameraRef.current) {
|
||||||
try {
|
try {
|
||||||
const sizes = await expoCameraRef.current.getAvailablePictureSizes();
|
const sizes = await expoCameraRef.current.getAvailablePictureSizes();
|
||||||
@@ -188,18 +190,24 @@ export default function CameraScreen({
|
|||||||
setPictureSize(optimal.size);
|
setPictureSize(optimal.size);
|
||||||
} else if (parsedSizes.length > 0) {
|
} else if (parsedSizes.length > 0) {
|
||||||
setPictureSize(parsedSizes[parsedSizes.length - 1].size);
|
setPictureSize(parsedSizes[parsedSizes.length - 1].size);
|
||||||
|
} else {
|
||||||
|
setPictureSize('fallback');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setPictureSize('fallback');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Failed to fetch picture sizes:', e);
|
console.warn('Failed to fetch picture sizes:', e);
|
||||||
|
setPictureSize('fallback');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
// Cancel capture process
|
// Cancel capture process
|
||||||
const handleLocalCancel = async () => {
|
const handleLocalCancel = async () => {
|
||||||
isCancelledRef.current = true;
|
isCancelledRef.current = true;
|
||||||
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
|
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
|
||||||
|
if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current);
|
||||||
|
|
||||||
// Clean up any temp files from a partially completed burst
|
// Clean up any temp files from a partially completed burst
|
||||||
for (const uri of burstUris) {
|
for (const uri of burstUris) {
|
||||||
@@ -229,14 +237,20 @@ export default function CameraScreen({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delay start until safe picture size is negotiated (or fallback is set)
|
||||||
|
// Only applies to built-in camera, USB camera ignores pictureSize
|
||||||
|
if (pictureSize === undefined && !isUsbConnected) {
|
||||||
|
console.log('Waiting for pictureSize negotiation...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
startCountdown();
|
startCountdown();
|
||||||
|
|
||||||
return () => {};
|
return () => {};
|
||||||
}, [countdownDuration, isIdle]);
|
}, [countdownDuration, isIdle, pictureSize, isUsbConnected]);
|
||||||
|
|
||||||
const startCountdown = () => {
|
const startCountdown = () => {
|
||||||
isCancelledRef.current = false;
|
isCancelledRef.current = false;
|
||||||
let timer: any;
|
|
||||||
let count = countdownDuration;
|
let count = countdownDuration;
|
||||||
|
|
||||||
setCountdown(count);
|
setCountdown(count);
|
||||||
@@ -248,20 +262,17 @@ export default function CameraScreen({
|
|||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
count -= 1;
|
count -= 1;
|
||||||
setCountdown(count);
|
setCountdown(count);
|
||||||
timer = setTimeout(runTimer, 1000);
|
countdownTimerRef.current = setTimeout(runTimer, 1000);
|
||||||
} else if (count === 1) {
|
} else if (count === 1) {
|
||||||
setCountdown('smile');
|
setCountdown('smile');
|
||||||
setIsCapturing(true);
|
setIsCapturing(true);
|
||||||
timer = setTimeout(() => {
|
countdownTimerRef.current = setTimeout(() => {
|
||||||
capture();
|
capture();
|
||||||
}, 800);
|
}, 800);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
timer = setTimeout(runTimer, 1000);
|
countdownTimerRef.current = setTimeout(runTimer, 1000);
|
||||||
|
|
||||||
// Store timer for cleanup
|
|
||||||
burstTimerRef.current = timer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 4. Capture photo function
|
// 4. Capture photo function
|
||||||
@@ -552,7 +563,7 @@ export default function CameraScreen({
|
|||||||
ref={expoCameraRef}
|
ref={expoCameraRef}
|
||||||
style={styles.cameraPreview}
|
style={styles.cameraPreview}
|
||||||
facing="front"
|
facing="front"
|
||||||
pictureSize={pictureSize}
|
pictureSize={pictureSize === 'fallback' ? undefined : pictureSize}
|
||||||
onCameraReady={handleCameraReady}
|
onCameraReady={handleCameraReady}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import {
|
|||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
Text,
|
||||||
View,
|
View,
|
||||||
|
Image,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import ViewShot from 'react-native-view-shot';
|
import ViewShot from 'react-native-view-shot';
|
||||||
import { Image } from 'expo-image';
|
|
||||||
import { LinearGradient } from 'expo-linear-gradient';
|
import { LinearGradient } from 'expo-linear-gradient';
|
||||||
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
|
||||||
@@ -72,6 +72,14 @@ export default function PreviewScreen({
|
|||||||
|
|
||||||
// Sticker state
|
// Sticker state
|
||||||
const [stickers, setStickers] = useState<StickerData[]>([]);
|
const [stickers, setStickers] = useState<StickerData[]>([]);
|
||||||
|
const [selectedImageIndex, setSelectedImageIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const mountedRef = useRef(true);
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
mountedRef.current = false;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
const [selectedStickerId, setSelectedStickerId] = useState<string | null>(null);
|
const [selectedStickerId, setSelectedStickerId] = useState<string | null>(null);
|
||||||
const [showStickerTray, setShowStickerTray] = useState<boolean>(false);
|
const [showStickerTray, setShowStickerTray] = useState<boolean>(false);
|
||||||
|
|
||||||
@@ -214,6 +222,7 @@ export default function PreviewScreen({
|
|||||||
try {
|
try {
|
||||||
setStatusMessage('Bild wird generiert...');
|
setStatusMessage('Bild wird generiert...');
|
||||||
const printUri = await captureComposite(currentPhoto);
|
const printUri = await captureComposite(currentPhoto);
|
||||||
|
if (!mountedRef.current) return;
|
||||||
|
|
||||||
setStatusMessage('Druckauftrag wird an Drucker gesendet...');
|
setStatusMessage('Druckauftrag wird an Drucker gesendet...');
|
||||||
await printImageLocal(printUri, {
|
await printImageLocal(printUri, {
|
||||||
@@ -223,13 +232,16 @@ export default function PreviewScreen({
|
|||||||
|
|
||||||
setStatusMessage('Wird in Galerie gespeichert...');
|
setStatusMessage('Wird in Galerie gespeichert...');
|
||||||
await saveToGallery(printUri);
|
await saveToGallery(printUri);
|
||||||
|
if (!mountedRef.current) return;
|
||||||
|
|
||||||
setStatusMessage('Druck erfolgreich! Viel Spaß! 🎉');
|
setStatusMessage('Druck erfolgreich! Viel Spaß! 🎉');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
if (!mountedRef.current) return;
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
onReset();
|
onReset();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
if (!mountedRef.current) return;
|
||||||
console.error('Printing failed:', error);
|
console.error('Printing failed:', error);
|
||||||
alert('Fehler: ' + error.message);
|
alert('Fehler: ' + error.message);
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
@@ -243,16 +255,20 @@ export default function PreviewScreen({
|
|||||||
setStatusMessage('Bild wird generiert...');
|
setStatusMessage('Bild wird generiert...');
|
||||||
try {
|
try {
|
||||||
const saveUri = await captureComposite(currentPhoto);
|
const saveUri = await captureComposite(currentPhoto);
|
||||||
|
if (!mountedRef.current) return;
|
||||||
|
|
||||||
setStatusMessage('Wird in Galerie gespeichert...');
|
setStatusMessage('Wird in Galerie gespeichert...');
|
||||||
await saveToGallery(saveUri);
|
await saveToGallery(saveUri);
|
||||||
|
if (!mountedRef.current) return;
|
||||||
|
|
||||||
setStatusMessage('Erfolgreich gespeichert!');
|
setStatusMessage('Erfolgreich gespeichert!');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
if (!mountedRef.current) return;
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
onReset();
|
onReset();
|
||||||
}, 1500);
|
}, 1500);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
if (!mountedRef.current) return;
|
||||||
console.error('Saving failed:', error);
|
console.error('Saving failed:', error);
|
||||||
alert('Fehler: ' + error.message);
|
alert('Fehler: ' + error.message);
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
@@ -272,13 +288,17 @@ export default function PreviewScreen({
|
|||||||
setStatusMessage('Wird gespeichert...');
|
setStatusMessage('Wird gespeichert...');
|
||||||
try {
|
try {
|
||||||
const capturedUri = await captureComposite(state.currentPhoto);
|
const capturedUri = await captureComposite(state.currentPhoto);
|
||||||
|
if (!mountedRef.current) return;
|
||||||
await saveToGallery(capturedUri);
|
await saveToGallery(capturedUri);
|
||||||
|
if (!mountedRef.current) return;
|
||||||
setStatusMessage('Erfolgreich gespeichert!');
|
setStatusMessage('Erfolgreich gespeichert!');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
if (!mountedRef.current) return;
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
state.onReset();
|
state.onReset();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (!mountedRef.current) return;
|
||||||
console.error('Auto-saving on exit failed:', error);
|
console.error('Auto-saving on exit failed:', error);
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
state.onReset();
|
state.onReset();
|
||||||
@@ -307,9 +327,12 @@ export default function PreviewScreen({
|
|||||||
setStatusMessage('Wird vor dem Wiederholen gespeichert...');
|
setStatusMessage('Wird vor dem Wiederholen gespeichert...');
|
||||||
try {
|
try {
|
||||||
const capturedUri = await captureComposite(currentPhoto);
|
const capturedUri = await captureComposite(currentPhoto);
|
||||||
|
if (!mountedRef.current) return;
|
||||||
await saveToGallery(capturedUri);
|
await saveToGallery(capturedUri);
|
||||||
} catch (error) {
|
if (!mountedRef.current) return;
|
||||||
console.error('Auto-saving before retake failed:', error);
|
} catch (e) {
|
||||||
|
if (!mountedRef.current) return;
|
||||||
|
console.error('Failed to auto-save before retake:', e);
|
||||||
}
|
}
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
}
|
}
|
||||||
@@ -326,11 +349,12 @@ export default function PreviewScreen({
|
|||||||
{photoUris.slice(-3).map((uri, idx) => (
|
{photoUris.slice(-3).map((uri, idx) => (
|
||||||
<Image
|
<Image
|
||||||
key={idx}
|
key={idx}
|
||||||
source={uri}
|
source={{ uri }}
|
||||||
style={styles.stripImage}
|
style={styles.stripImage}
|
||||||
contentFit="cover"
|
resizeMode="cover"
|
||||||
|
resizeMethod="resize"
|
||||||
onLoad={() => logger.log(`Image loaded successfully: ${uri}`)}
|
onLoad={() => logger.log(`Image loaded successfully: ${uri}`)}
|
||||||
onError={(e) => logger.error(`Image load failed: ${uri}`, e)}
|
onError={(e) => logger.error(`Image load failed: ${uri}`, e.nativeEvent?.error || e)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
@@ -347,11 +371,12 @@ export default function PreviewScreen({
|
|||||||
{photoUris.slice(-4).map((uri, idx) => (
|
{photoUris.slice(-4).map((uri, idx) => (
|
||||||
<Image
|
<Image
|
||||||
key={idx}
|
key={idx}
|
||||||
source={uri}
|
source={{ uri }}
|
||||||
style={styles.gridImage}
|
style={styles.gridImage}
|
||||||
contentFit="cover"
|
resizeMode="cover"
|
||||||
|
resizeMethod="resize"
|
||||||
onLoad={() => logger.log(`Grid Image loaded successfully: ${uri}`)}
|
onLoad={() => logger.log(`Grid Image loaded successfully: ${uri}`)}
|
||||||
onError={(e) => logger.error(`Grid Image load failed: ${uri}`, e)}
|
onError={(e) => logger.error(`Grid Image load failed: ${uri}`, e.nativeEvent?.error || e)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
@@ -367,11 +392,12 @@ export default function PreviewScreen({
|
|||||||
{photoUris.slice(-2).map((uri, idx) => (
|
{photoUris.slice(-2).map((uri, idx) => (
|
||||||
<Image
|
<Image
|
||||||
key={idx}
|
key={idx}
|
||||||
source={uri}
|
source={{ uri }}
|
||||||
style={styles.duoImage}
|
style={styles.duoImage}
|
||||||
contentFit="cover"
|
resizeMode="cover"
|
||||||
|
resizeMethod="resize"
|
||||||
onLoad={() => logger.log(`Duo Image loaded successfully: ${uri}`)}
|
onLoad={() => logger.log(`Duo Image loaded successfully: ${uri}`)}
|
||||||
onError={(e) => logger.error(`Duo Image load failed: ${uri}`, e)}
|
onError={(e) => logger.error(`Duo Image load failed: ${uri}`, e.nativeEvent?.error || e)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
@@ -384,11 +410,12 @@ export default function PreviewScreen({
|
|||||||
return (
|
return (
|
||||||
<View style={styles.singleCanvas}>
|
<View style={styles.singleCanvas}>
|
||||||
<Image
|
<Image
|
||||||
source={currentPhoto}
|
source={{ uri: currentPhoto }}
|
||||||
style={styles.singleImage}
|
style={styles.singleImage}
|
||||||
contentFit="cover"
|
resizeMode="cover"
|
||||||
|
resizeMethod="resize"
|
||||||
onLoad={() => logger.log(`Single Image loaded successfully: ${currentPhoto}`)}
|
onLoad={() => logger.log(`Single Image loaded successfully: ${currentPhoto}`)}
|
||||||
onError={(e) => logger.error(`Single Image load failed: ${currentPhoto}`, e)}
|
onError={(e) => logger.error(`Single Image load failed: ${currentPhoto}`, e.nativeEvent?.error || e)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user