Fix 5 hidden edge cases: IPP decode, settings race, PreviewScreen state sync, burst timeout leak, and logger spam
This commit is contained in:
@@ -148,11 +148,10 @@ export default function DraggableSticker({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
stickerWrapper: {
|
stickerContainer: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
zIndex: 100,
|
|
||||||
},
|
},
|
||||||
emojiText: {
|
emojiText: {
|
||||||
fontSize: 48,
|
fontSize: 48,
|
||||||
@@ -179,13 +178,12 @@ const styles = StyleSheet.create({
|
|||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: -10,
|
top: -10,
|
||||||
right: -10,
|
right: -10,
|
||||||
width: 22,
|
width: 24,
|
||||||
height: 22,
|
height: 24,
|
||||||
borderRadius: 11,
|
borderRadius: 12,
|
||||||
backgroundColor: THEME.colors.error,
|
backgroundColor: THEME.colors.error,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
zIndex: 200,
|
|
||||||
},
|
},
|
||||||
deleteBtnText: {
|
deleteBtnText: {
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ export default function CameraScreen({
|
|||||||
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 countdownTimerRef = useRef<any>(null);
|
||||||
|
const completionTimerRef = useRef<any>(null);
|
||||||
const isCancelledRef = useRef<boolean>(false);
|
const isCancelledRef = useRef<boolean>(false);
|
||||||
|
|
||||||
// Reanimated countdown pulse animation
|
// Reanimated countdown pulse animation
|
||||||
@@ -162,6 +163,7 @@ export default function CameraScreen({
|
|||||||
return () => {
|
return () => {
|
||||||
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
|
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
|
||||||
if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current);
|
if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current);
|
||||||
|
if (completionTimerRef.current) clearTimeout(completionTimerRef.current);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -207,6 +209,7 @@ export default function CameraScreen({
|
|||||||
isCancelledRef.current = true;
|
isCancelledRef.current = true;
|
||||||
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
|
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
|
||||||
if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current);
|
if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current);
|
||||||
|
if (completionTimerRef.current) clearTimeout(completionTimerRef.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) {
|
||||||
@@ -362,12 +365,12 @@ export default function CameraScreen({
|
|||||||
setBurstIndex(0);
|
setBurstIndex(0);
|
||||||
setBurstUris([]);
|
setBurstUris([]);
|
||||||
// Short delay prevents Android expo-camera crash on immediate unmount
|
// Short delay prevents Android expo-camera crash on immediate unmount
|
||||||
setTimeout(() => {
|
completionTimerRef.current = setTimeout(() => {
|
||||||
onBurstComplete(newUris);
|
onBurstComplete(newUris);
|
||||||
}, 400);
|
}, 400);
|
||||||
} else {
|
} else {
|
||||||
// Single shot mode
|
// Single shot mode
|
||||||
setTimeout(() => {
|
completionTimerRef.current = setTimeout(() => {
|
||||||
onPhotoCaptured(capturedUri);
|
onPhotoCaptured(capturedUri);
|
||||||
}, 400);
|
}, 400);
|
||||||
}
|
}
|
||||||
@@ -443,6 +446,22 @@ export default function CameraScreen({
|
|||||||
>
|
>
|
||||||
{welcomeText}
|
{welcomeText}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
{/* White Outline Layers */}
|
||||||
|
{[
|
||||||
|
{ t: -2, l: -2 }, { t: -2, l: 0 }, { t: -2, l: 2 },
|
||||||
|
{ t: 0, l: -2 }, { t: 0, l: 2 },
|
||||||
|
{ t: 2, l: -2 }, { t: 2, l: 0 }, { t: 2, l: 2 }
|
||||||
|
].map((pos, index) => (
|
||||||
|
<Text
|
||||||
|
key={`outline-${index}`}
|
||||||
|
style={[styles.headerTitleBase, { position: 'absolute', color: 'white', top: pos.t, left: pos.l }]}
|
||||||
|
adjustsFontSizeToFit
|
||||||
|
numberOfLines={1}
|
||||||
|
>
|
||||||
|
{welcomeText}
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
|
||||||
{/* Gradient Text Layer over the shadow layer */}
|
{/* Gradient Text Layer over the shadow layer */}
|
||||||
<MaskedView
|
<MaskedView
|
||||||
|
|||||||
@@ -66,9 +66,7 @@ export default function PreviewScreen({
|
|||||||
const [isProcessing, setIsProcessing] = useState<boolean>(false);
|
const [isProcessing, setIsProcessing] = useState<boolean>(false);
|
||||||
const [statusMessage, setStatusMessage] = useState<string>('');
|
const [statusMessage, setStatusMessage] = useState<string>('');
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
logger.log('PreviewScreen: render function executed');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sticker state
|
// Sticker state
|
||||||
const [stickers, setStickers] = useState<StickerData[]>([]);
|
const [stickers, setStickers] = useState<StickerData[]>([]);
|
||||||
@@ -224,11 +222,10 @@ export default function PreviewScreen({
|
|||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
if (isProcessing) return;
|
if (isProcessing) return;
|
||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
setStatusMessage('Dein Foto wird vorbereitet...');
|
|
||||||
try {
|
try {
|
||||||
setStatusMessage('Bild wird generiert...');
|
|
||||||
const printUri = await captureComposite(currentPhoto);
|
const printUri = await captureComposite(currentPhoto);
|
||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
|
setStatusMessage('Dein Foto wird vorbereitet...');
|
||||||
|
|
||||||
setStatusMessage('Druckauftrag wird an Drucker gesendet...');
|
setStatusMessage('Druckauftrag wird an Drucker gesendet...');
|
||||||
await printImageLocal(printUri, {
|
await printImageLocal(printUri, {
|
||||||
@@ -259,10 +256,10 @@ export default function PreviewScreen({
|
|||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
if (isProcessing) return;
|
if (isProcessing) return;
|
||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
setStatusMessage('Bild wird generiert...');
|
|
||||||
try {
|
try {
|
||||||
const saveUri = await captureComposite(currentPhoto);
|
const saveUri = await captureComposite(currentPhoto);
|
||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
|
setStatusMessage('Bild wird generiert...');
|
||||||
|
|
||||||
setStatusMessage('Wird in Galerie gespeichert...');
|
setStatusMessage('Wird in Galerie gespeichert...');
|
||||||
await saveToGallery(saveUri);
|
await saveToGallery(saveUri);
|
||||||
@@ -293,10 +290,10 @@ export default function PreviewScreen({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
setStatusMessage('Wird gespeichert...');
|
|
||||||
try {
|
try {
|
||||||
const capturedUri = await captureComposite(state.currentPhoto);
|
const capturedUri = await captureComposite(state.currentPhoto);
|
||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
|
setStatusMessage('Wird gespeichert...');
|
||||||
await saveToGallery(capturedUri);
|
await saveToGallery(capturedUri);
|
||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
setStatusMessage('Erfolgreich gespeichert!');
|
setStatusMessage('Erfolgreich gespeichert!');
|
||||||
@@ -333,10 +330,10 @@ export default function PreviewScreen({
|
|||||||
if (isProcessing) return;
|
if (isProcessing) return;
|
||||||
if (stickers.length > 0 || layout !== 'single' || activeFrame || dateOverlay !== 'off') {
|
if (stickers.length > 0 || layout !== 'single' || activeFrame || dateOverlay !== 'off') {
|
||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
setStatusMessage('Wird vor dem Wiederholen gespeichert...');
|
|
||||||
try {
|
try {
|
||||||
const capturedUri = await captureComposite(currentPhoto);
|
const capturedUri = await captureComposite(currentPhoto);
|
||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
|
setStatusMessage('Wird vor dem Wiederholen gespeichert...');
|
||||||
await saveToGallery(capturedUri);
|
await saveToGallery(capturedUri);
|
||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -659,6 +656,8 @@ const styles = StyleSheet.create({
|
|||||||
height: 320,
|
height: 320,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
overflow: 'hidden',
|
||||||
|
backgroundColor: THEME.colors.background,
|
||||||
},
|
},
|
||||||
controlPanel: {
|
controlPanel: {
|
||||||
flex: 0.8,
|
flex: 0.8,
|
||||||
|
|||||||
@@ -91,8 +91,9 @@ export async function printImageLocal(imageUri: string, options: PrintOptions):
|
|||||||
}
|
}
|
||||||
console.log('Silent print job accepted successfully!');
|
console.log('Silent print job accepted successfully!');
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// If decoding fails, we still assume success since the network request completed successfully
|
// If decoding fails, it's a print error. Let it trigger a retry.
|
||||||
console.warn('Failed to parse IPP response, but network request succeeded:', e.message);
|
console.warn('Failed to parse IPP response. The printer may be incompatible or returning malformed data:', e.message);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return; // Success, exit retry loop
|
return; // Success, exit retry loop
|
||||||
|
|||||||
@@ -46,11 +46,9 @@ const DEFAULT_SETTINGS: AppSettings = {
|
|||||||
*/
|
*/
|
||||||
export async function loadSettings(): Promise<AppSettings> {
|
export async function loadSettings(): Promise<AppSettings> {
|
||||||
try {
|
try {
|
||||||
if (settingsFile.exists) {
|
const content = await settingsFile.text();
|
||||||
const content = await settingsFile.text();
|
const parsed = JSON.parse(content);
|
||||||
const parsed = JSON.parse(content);
|
return { ...DEFAULT_SETTINGS, ...parsed };
|
||||||
return { ...DEFAULT_SETTINGS, ...parsed };
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to load settings, using defaults:', e);
|
console.error('Failed to load settings, using defaults:', e);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user