From 2e4526135427e9ab274b7ca7dad40d2e855a11fe Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 18 Apr 2026 14:19:48 +0200 Subject: [PATCH] feat: add camera capture for trip and item images --- app.json | 11 ++++++++++- src/AppRoot.js | 19 +++++++++++++++++++ src/modals/ItemModal.js | 12 +++++++++--- src/styles.js | 5 +++++ src/tabs/TripsTab.js | 12 +++++++++--- 5 files changed, 52 insertions(+), 7 deletions(-) diff --git a/app.json b/app.json index 37f0a5c..eb8cebb 100644 --- a/app.json +++ b/app.json @@ -34,6 +34,15 @@ "eas": { "projectId": "1275f90e-33c6-4af1-942e-ca29a309f8c8" } - } + }, + "plugins": [ + [ + "expo-image-picker", + { + "photosPermission": "Allow Luggage List to access your photos for trip and item images.", + "cameraPermission": "Allow Luggage List to use your camera to take trip and item photos." + } + ] + ] } } diff --git a/src/AppRoot.js b/src/AppRoot.js index ab2fa93..29e5c5a 100644 --- a/src/AppRoot.js +++ b/src/AppRoot.js @@ -169,6 +169,23 @@ export default function AppRoot() { } } + async function takeImage(onPicked) { + const perm = await ImagePicker.requestCameraPermissionsAsync(); + if (!perm.granted) { + Alert.alert('Permission needed', 'Allow camera access to take photos.'); + return; + } + + const result = await ImagePicker.launchCameraAsync({ + allowsEditing: false, + quality: 0.85, + }); + + if (!result.canceled && result.assets?.[0]?.uri) { + onPicked(result.assets[0].uri); + } + } + function createTrip() { if (!tripForm.name.trim()) { Alert.alert('Missing name', 'Trip name is required.'); @@ -526,6 +543,7 @@ export default function AppRoot() { tripForm={tripForm} updateTripForm={updateTripForm} pickTripImage={() => pickImage((uri) => updateTripForm('imageUri', uri))} + takeTripImage={() => takeImage((uri) => updateTripForm('imageUri', uri))} templateTrip={templateTrip} createTrip={createTrip} trips={data.trips} @@ -587,6 +605,7 @@ export default function AppRoot() { setItemModalVisible={setItemModalVisible} updateItemForm={updateItemForm} pickItemImage={() => pickImage((uri) => updateItemForm('imageUri', uri))} + takeItemImage={() => takeImage((uri) => updateItemForm('imageUri', uri))} saveItemFromModal={saveItemFromModal} /> diff --git a/src/modals/ItemModal.js b/src/modals/ItemModal.js index 18020b9..d1efbb0 100644 --- a/src/modals/ItemModal.js +++ b/src/modals/ItemModal.js @@ -11,6 +11,7 @@ export default function ItemModal({ setItemModalVisible, updateItemForm, pickItemImage, + takeItemImage, saveItemFromModal, }) { return ( @@ -81,9 +82,14 @@ export default function ItemModal({ ) : null} - - {itemForm.imageUri ? 'Change image' : 'Add image'} - + + + Take photo + + + {itemForm.imageUri ? 'From gallery (change)' : 'From gallery'} + + {!!itemForm.imageUri && } diff --git a/src/styles.js b/src/styles.js index 78b5c1e..f87c42a 100644 --- a/src/styles.js +++ b/src/styles.js @@ -211,6 +211,11 @@ export const styles = StyleSheet.create({ color: '#dbeafe', fontWeight: '700', }, + actionRow: { + flexDirection: 'row', + gap: 8, + marginTop: 4, + }, inlineToggle: { marginTop: 2, diff --git a/src/tabs/TripsTab.js b/src/tabs/TripsTab.js index e509b49..542f6d1 100644 --- a/src/tabs/TripsTab.js +++ b/src/tabs/TripsTab.js @@ -17,6 +17,7 @@ export default function TripsTab({ tripForm, updateTripForm, pickTripImage, + takeTripImage, templateTrip, createTrip, trips, @@ -58,9 +59,14 @@ export default function TripsTab({ openDatePicker('startDate')} /> openDatePicker('endDate')} /> - - {tripForm.imageUri ? 'Change trip image' : 'Add trip image'} - + + + Take photo + + + {tripForm.imageUri ? 'From gallery (change)' : 'From gallery'} + + {tripForm.imageUri ? : null}