diff --git a/src/AppRoot.js b/src/AppRoot.js
index 90456ba..b81c9bf 100644
--- a/src/AppRoot.js
+++ b/src/AppRoot.js
@@ -35,6 +35,8 @@ const emptyItemForm = () => ({
placement: 'suitcase',
lentTo: '',
imageUri: '',
+ imageQuality: 'balanced',
+ imageAllowCrop: false,
});
const emptyCheckupNoForm = () => ({
@@ -234,7 +236,7 @@ export default function AppRoot() {
setDatePicker((prev) => ({ ...prev, visible: false }));
}
- async function pickImage(onPicked) {
+ async function pickImage(onPicked, options = {}) {
const perm = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (!perm.granted) {
showAlert('Permission needed', 'Allow gallery access to select images.');
@@ -243,8 +245,8 @@ export default function AppRoot() {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
- allowsEditing: false,
- quality: 0.85,
+ allowsEditing: !!options.allowCrop,
+ quality: typeof options.quality === 'number' ? options.quality : 0.85,
});
if (!result.canceled && result.assets?.[0]?.uri) {
@@ -252,7 +254,7 @@ export default function AppRoot() {
}
}
- async function takeImage(onPicked) {
+ async function takeImage(onPicked, options = {}) {
const perm = await ImagePicker.requestCameraPermissionsAsync();
if (!perm.granted) {
showAlert('Permission needed', 'Allow camera access to take photos.');
@@ -260,8 +262,8 @@ export default function AppRoot() {
}
const result = await ImagePicker.launchCameraAsync({
- allowsEditing: false,
- quality: 0.85,
+ allowsEditing: !!options.allowCrop,
+ quality: typeof options.quality === 'number' ? options.quality : 0.85,
});
if (!result.canceled && result.assets?.[0]?.uri) {
@@ -434,6 +436,8 @@ export default function AppRoot() {
placement: item.placement || 'suitcase',
lentTo: item.lentTo || '',
imageUri: item.imageUri || '',
+ imageQuality: item.imageQuality || 'balanced',
+ imageAllowCrop: !!item.imageAllowCrop,
});
setItemModalVisible(true);
}
@@ -463,6 +467,8 @@ export default function AppRoot() {
placement: itemForm.placement,
lentTo: itemForm.status === 'lent-to' ? itemForm.lentTo.trim() : '',
imageUri: itemForm.imageUri,
+ imageQuality: itemForm.imageQuality,
+ imageAllowCrop: itemForm.imageAllowCrop,
createdAt: existingCreatedAt,
updatedAt: now,
};
@@ -606,6 +612,28 @@ export default function AppRoot() {
setCheckupNoForm(emptyCheckupNoForm());
}
+ function goBackInCheckup() {
+ setCheckupFlowIndex((prev) => Math.max(0, prev - 1));
+ setCheckupFlowMode('question');
+ setCheckupNoForm(emptyCheckupNoForm());
+ }
+
+ function skipCurrentCheckupItem() {
+ if (!checkupCurrentEntry) return;
+ setCheckupSession((prev) =>
+ prev.map((x) =>
+ x.itemId === checkupCurrentEntry.itemId
+ ? {
+ ...x,
+ confirmed: false,
+ result: 'pending',
+ }
+ : x
+ )
+ );
+ goNextInCheckup();
+ }
+
function answerCurrentCheckupYes() {
const entry = checkupCurrentEntry;
if (!entry) return;
@@ -855,8 +883,8 @@ export default function AppRoot() {
itemForm={itemForm}
setItemModalVisible={setItemModalVisible}
updateItemForm={updateItemForm}
- pickItemImage={() => pickImage((uri) => updateItemForm('imageUri', uri))}
- takeItemImage={() => takeImage((uri) => updateItemForm('imageUri', uri))}
+ pickItemImage={(options) => pickImage((uri) => updateItemForm('imageUri', uri), options)}
+ takeItemImage={(options) => takeImage((uri) => updateItemForm('imageUri', uri), options)}
saveItemFromModal={saveItemFromModal}
/>
@@ -872,6 +900,8 @@ export default function AppRoot() {
onYes={answerCurrentCheckupYes}
onNo={openCurrentCheckupNo}
onSaveNo={saveCurrentCheckupNo}
+ onSkip={skipCurrentCheckupItem}
+ onBack={goBackInCheckup}
onFinish={finishCheckupFlow}
/>
diff --git a/src/modals/CheckupFlowModal.js b/src/modals/CheckupFlowModal.js
index 7254551..9770043 100644
--- a/src/modals/CheckupFlowModal.js
+++ b/src/modals/CheckupFlowModal.js
@@ -17,6 +17,8 @@ export default function CheckupFlowModal({
onYes,
onNo,
onSaveNo,
+ onSkip,
+ onBack,
onFinish,
}) {
const finished = !entry;
@@ -37,9 +39,14 @@ export default function CheckupFlowModal({
Done. Save this snapshot?
All {total} items were checked.
-
- Save Check-Up Snapshot
-
+
+
+ Back
+
+
+ Save Snapshot
+
+
) : (
Item {stepIndex + 1} / {total}
+
+
+
{entry.name}
{entry.category || 'uncategorized'}
@@ -60,6 +70,14 @@ export default function CheckupFlowModal({
{mode === 'question' ? (
+
+
+ Back
+
+
+ Skip
+
+
Yes, correct
@@ -106,9 +124,14 @@ export default function CheckupFlowModal({
-
- Save update + next
-
+
+
+ Back
+
+
+ Save + Next
+
+
)}
diff --git a/src/modals/ItemModal.js b/src/modals/ItemModal.js
index d1efbb0..eed1e83 100644
--- a/src/modals/ItemModal.js
+++ b/src/modals/ItemModal.js
@@ -5,6 +5,12 @@ import ChipGroup from '../components/ChipGroup';
import Field from '../components/Field';
import { styles } from '../styles';
+function qualityValue(level) {
+ if (level === 'high') return 0.95;
+ if (level === 'low') return 0.45;
+ return 0.75;
+}
+
export default function ItemModal({
visible,
itemForm,
@@ -14,6 +20,11 @@ export default function ItemModal({
takeItemImage,
saveItemFromModal,
}) {
+ const mediaOptions = {
+ quality: qualityValue(itemForm.imageQuality),
+ allowCrop: !!itemForm.imageAllowCrop,
+ };
+
return (
@@ -82,11 +93,28 @@ export default function ItemModal({
) : null}
+
+
+ {['low', 'balanced', 'high'].map((level) => {
+ const active = itemForm.imageQuality === level;
+ return (
+ updateItemForm('imageQuality', level)}>
+ {level}
+
+ );
+ })}
+
+
+
+ updateItemForm('imageAllowCrop', !itemForm.imageAllowCrop)}>
+ {itemForm.imageAllowCrop ? '☑' : '☐'} Enable optional crop before save
+
+
-
+ takeItemImage(mediaOptions)}>
Take photo
-
+ pickItemImage(mediaOptions)}>
{itemForm.imageUri ? 'From gallery (change)' : 'From gallery'}
diff --git a/src/styles.js b/src/styles.js
index a4f4e42..10ea627 100644
--- a/src/styles.js
+++ b/src/styles.js
@@ -404,6 +404,19 @@ export const styles = StyleSheet.create({
marginTop: 14,
gap: 10,
},
+ checkupProgressTrack: {
+ width: '100%',
+ height: 8,
+ borderRadius: 999,
+ backgroundColor: '#1e293b',
+ overflow: 'hidden',
+ marginTop: 4,
+ marginBottom: 8,
+ },
+ checkupProgressFill: {
+ height: '100%',
+ backgroundColor: '#2563eb',
+ },
answerYesWide: {
backgroundColor: '#163223',
borderWidth: 1,
@@ -494,7 +507,7 @@ export const styles = StyleSheet.create({
tabBarWrap: {
position: 'absolute',
- bottom: 0,
+ bottom: 6,
left: 0,
right: 0,
paddingHorizontal: 10,