Handle error states and accessibility improvements

This commit is contained in:
2026-10-16 15:09:20 +02:00
parent fbd57d664f
commit 9b80580f9c
9 changed files with 139 additions and 42 deletions
+55 -26
View File
@@ -33,32 +33,31 @@ export default function App() {
const [manualPrices, setManualPrices] = useState({});
const [recentProducts, setRecentProducts] = useState([]);
const [isHydrated, setIsHydrated] = useState(false);
const [storageError, setStorageError] = useState('');
useEffect(() => {
let isMounted = true;
async function hydrate() {
const [
storedPreferences,
storedShoppingList,
storedProductCache,
storedManualPrices,
storedRecentProducts
] = await Promise.all([
storageService.loadPreferences(defaultPreferences),
storageService.loadShoppingList(initialShoppingList),
storageService.loadProductCache(),
storageService.loadManualPrices(),
storageService.loadRecentProducts()
]);
try {
const [storedPreferences, storedShoppingList, storedProductCache, storedManualPrices, storedRecentProducts] = await Promise.all([
storageService.loadPreferences(defaultPreferences),
storageService.loadShoppingList(initialShoppingList),
storageService.loadProductCache(),
storageService.loadManualPrices(),
storageService.loadRecentProducts()
]);
if (isMounted) {
setPreferences(storedPreferences);
setShoppingList(storedShoppingList);
setProductCache(storedProductCache);
setManualPrices(storedManualPrices);
setRecentProducts(storedRecentProducts);
setIsHydrated(true);
if (isMounted) {
setPreferences({ ...defaultPreferences, ...storedPreferences });
setShoppingList({ ...initialShoppingList, ...storedShoppingList, items: storedShoppingList?.items || [] });
setProductCache(storedProductCache || {});
setManualPrices(storedManualPrices || {});
setRecentProducts(Array.isArray(storedRecentProducts) ? storedRecentProducts : []);
setIsHydrated(true);
}
} catch (error) {
if (isMounted) setStorageError('Deine lokalen Daten konnten nicht geladen werden.');
}
}
@@ -68,6 +67,27 @@ export default function App() {
};
}, []);
function retryHydration() {
setStorageError('');
setIsHydrated(false);
// Reloading the app is not necessary for the normal path, but this keeps
// the retry action explicit if a storage adapter fails temporarily.
Promise.all([
storageService.loadPreferences(defaultPreferences),
storageService.loadShoppingList(initialShoppingList),
storageService.loadProductCache(),
storageService.loadManualPrices(),
storageService.loadRecentProducts()
]).then(([nextPreferences, nextList, nextCache, nextPrices, nextRecent]) => {
setPreferences({ ...defaultPreferences, ...nextPreferences });
setShoppingList({ ...initialShoppingList, ...nextList, items: nextList?.items || [] });
setProductCache(nextCache || {});
setManualPrices(nextPrices || {});
setRecentProducts(Array.isArray(nextRecent) ? nextRecent : []);
setIsHydrated(true);
}).catch(() => setStorageError('Bitte starte Shoppy erneut, um die lokalen Daten zu laden.'));
}
useEffect(() => {
if (isHydrated) storageService.savePreferences(preferences);
}, [isHydrated, preferences]);
@@ -132,11 +152,13 @@ export default function App() {
}
return (
<HomeScreen
onOpenProduct={openProduct}
onStartScan={() => setActiveTab('scan')}
preferences={preferences}
recentProducts={recentProducts}
<HomeScreen
onOpenProduct={openProduct}
onOpenList={() => setActiveTab('list')}
onStartScan={() => setActiveTab('scan')}
preferences={preferences}
recentProducts={recentProducts}
shoppingList={shoppingList}
/>
);
}, [activeTab, manualPrices, preferences, productCache, recentProducts, shoppingList]);
@@ -356,7 +378,8 @@ export default function App() {
if (!isHydrated) {
return (
<View style={styles.loading}>
<Text style={styles.loadingText}>Shoppy wird geladen...</Text>
<Text style={styles.loadingText}>{storageError || 'Shoppy wird geladen...'}</Text>
{storageError ? <Text onPress={retryHydration} style={styles.retryText}>Erneut versuchen</Text> : null}
</View>
);
}
@@ -443,5 +466,11 @@ const styles = StyleSheet.create({
color: colors.muted,
fontSize: 16,
fontWeight: '700'
},
retryText: {
color: colors.primary,
fontSize: 15,
fontWeight: '800',
marginTop: 16
}
});