feat: add photo selection reel for collages and app version reset

This commit is contained in:
2026-06-11 15:21:47 +02:00
parent a382df71b5
commit d9b2c9cc52
2 changed files with 127 additions and 22 deletions
+116 -22
View File
@@ -1,13 +1,5 @@
import React, { useState, useRef, useCallback, useEffect } from 'react';
import {
StyleSheet,
Text,
View,
Image,
TouchableOpacity,
ActivityIndicator,
BackHandler,
} from 'react-native';
import { ScrollView, StyleSheet, Text, View, Image, TouchableOpacity, ActivityIndicator, BackHandler } from 'react-native';
import ViewShot from 'react-native-view-shot';
import { LinearGradient } from 'expo-linear-gradient';
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
@@ -68,6 +60,42 @@ export default function PreviewScreen({
const [isCapturing, setIsCapturing] = useState<boolean>(false);
const [statusMessage, setStatusMessage] = useState<string>('');
const getRequiredPhotoCount = (l: CollageLayout) => {
if (l === 'single') return 1;
if (l === 'duo') return 2;
if (l === 'strip') return 3;
if (l === 'grid') return 4;
return 1;
};
const [activeUris, setActiveUris] = useState<string[]>([]);
useEffect(() => {
const req = getRequiredPhotoCount(layout);
setActiveUris(prev => {
if (prev.length === req) return prev;
if (prev.length > req) return prev.slice(prev.length - req);
const available = photoUris.filter(u => !prev.includes(u));
const needed = req - prev.length;
const toAdd = available.slice(-needed);
return [...prev, ...toAdd];
});
}, [layout, photoUris]);
const togglePhoto = (uri: string) => {
logger.log(`Action: Toggled photo ${uri}`);
setActiveUris(prev => {
const req = getRequiredPhotoCount(layout);
if (prev.includes(uri)) {
return prev; // Already selected, do nothing
}
if (prev.length >= req) {
return [...prev.slice(1), uri]; // FIFO: remove oldest, add new
}
return [...prev, uri];
});
};
// Sticker state
@@ -387,7 +415,7 @@ export default function PreviewScreen({
return (
<View style={[styles.collageCanvas, styles.stripCanvas]}>
<View style={styles.stripContent}>
{photoUris.slice(-3).map((uri, idx) => (
{activeUris.map((uri, idx) => (
<Image
key={idx}
source={{ uri }}
@@ -409,7 +437,7 @@ export default function PreviewScreen({
return (
<View style={[styles.collageCanvas, styles.gridCanvas]}>
<View style={styles.gridContainer}>
{photoUris.slice(-4).map((uri, idx) => (
{activeUris.map((uri, idx) => (
<Image
key={idx}
source={{ uri }}
@@ -430,7 +458,7 @@ export default function PreviewScreen({
return (
<View style={[styles.collageCanvas, styles.duoCanvas]}>
<View style={styles.duoContainer}>
{photoUris.slice(-2).map((uri, idx) => (
{activeUris.map((uri, idx) => (
<Image
key={idx}
source={{ uri }}
@@ -449,15 +477,17 @@ export default function PreviewScreen({
);
default:
return (
<View style={styles.singleCanvas}>
<Image
source={{ uri: currentPhoto }}
style={styles.singleImage}
resizeMode="cover"
resizeMethod="resize"
onLoad={() => logger.log(`Single Image loaded successfully: ${currentPhoto}`)}
onError={(e) => logger.error(`Single Image load failed: ${currentPhoto}`, e.nativeEvent?.error || e)}
/>
<View style={[styles.collageCanvas, styles.singleCanvas]}>
{activeUris.length > 0 && (
<Image
source={{ uri: activeUris[activeUris.length - 1] }}
style={styles.singleImage}
resizeMode="cover"
resizeMethod="resize"
onLoad={() => logger.log(`Single Image loaded successfully: ${currentPhoto}`)}
onError={(e) => logger.error(`Single Image load failed: ${currentPhoto}`, e.nativeEvent?.error || e)}
/>
)}
</View>
);
}
@@ -570,8 +600,34 @@ export default function PreviewScreen({
</TouchableOpacity>
</View>
{/* Thumbnail Reel for Photo Selection */}
{photoUris.length > 1 && layout !== 'single' && (
<View style={styles.thumbnailReel}>
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.thumbnailReelContent}>
{photoUris.map((uri, idx) => {
const isActive = activeUris.includes(uri);
return (
<TouchableOpacity
key={idx}
style={[styles.thumbnailWrapper, isActive && styles.thumbnailActive]}
onPress={() => togglePhoto(uri)}
activeOpacity={0.7}
>
<Image source={{ uri }} style={styles.thumbnailImage} />
{isActive && (
<View style={styles.thumbnailCheck}>
<Text style={{ color: '#fff', fontSize: 10, fontWeight: 'bold' }}></Text>
</View>
)}
</TouchableOpacity>
);
})}
</ScrollView>
</View>
)}
<Text style={styles.photosCountText}>
Aufgenommene Fotos: {photoUris.length} / 4
Aufgenommene Fotos: {photoUris.length}
</Text>
{/* Editing Tools Row */}
@@ -752,6 +808,44 @@ const styles = StyleSheet.create({
fontSize: 12,
fontWeight: 'bold',
},
thumbnailReel: {
marginBottom: THEME.spacing.md,
height: 60,
},
thumbnailReelContent: {
alignItems: 'center',
gap: 10,
},
thumbnailWrapper: {
width: 45,
height: 45,
borderRadius: 4,
borderWidth: 2,
borderColor: 'transparent',
overflow: 'hidden',
position: 'relative',
opacity: 0.5,
},
thumbnailActive: {
borderColor: THEME.colors.primary,
opacity: 1,
},
thumbnailImage: {
width: '100%',
height: '100%',
resizeMode: 'cover',
},
thumbnailCheck: {
position: 'absolute',
bottom: 2,
right: 2,
backgroundColor: THEME.colors.primary,
borderRadius: 10,
width: 14,
height: 14,
justifyContent: 'center',
alignItems: 'center',
},
photosCountText: {
fontSize: 14,
color: THEME.colors.textMuted,