feat: enhance database service with centralized error logging and improve media handling across services

This commit is contained in:
bobbert committed 2026-02-25 00:14:00 +00:00
1 parent 04046702d3
commit ad0cc1a7f5
5 files changed
+384 -165

No files matched your search

+28 -17
View File
@@ -1,4 +1,4 @@
import React, { useRef, useState, useEffect, useCallback } from 'react';
import React, { useRef, useState, useEffect } from 'react';
import {
Modal, View, Text, TouchableOpacity, Image, StyleSheet,
Dimensions, FlatList,
@@ -57,22 +57,22 @@ const MediaViewer = ({ visible, media, allMedia, onClose }) => {
}, 100);
}, [visible, media]);
const onMainScroll = useCallback(
(event) => {
const offsetX = event.nativeEvent.contentOffset.x;
const idx = Math.round(offsetX / SCREEN_WIDTH);
if (idx >= 0 && idx < mediaList.length && idx !== activeIndex) {
setActiveIndex(idx);
setCurrentMedia(mediaList[idx]);
if (thumbListRef.current) {
try {
thumbListRef.current.scrollToIndex({ index: idx, animated: true, viewPosition: 0.5 });
} catch (e) { /* ignore */ }
}
const activeIndexRef = useRef(0);
const onMainScroll = (event) => {
const offsetX = event.nativeEvent.contentOffset.x;
const idx = Math.round(offsetX / SCREEN_WIDTH);
if (idx >= 0 && idx < mediaList.length && idx !== activeIndexRef.current) {
activeIndexRef.current = idx;
setActiveIndex(idx);
setCurrentMedia(mediaList[idx]);
if (thumbListRef.current) {
try {
thumbListRef.current.scrollToIndex({ index: idx, animated: true, viewPosition: 0.5 });
} catch (_) { /* ignore */ }
}
},
[activeIndex, mediaList, thumbListRef]
);
}
};
const onThumbPress = (index) => {
setActiveIndex(index);
@@ -189,9 +189,20 @@ const MediaViewer = ({ visible, media, allMedia, onClose }) => {
<View style={styles.row}>
<Text style={styles.label}>Source</Text>
<Text style={styles.value}>
{getServiceIcon(displayMedia.source_service)} {getServiceName(displayMedia.source_service)}
{displayMedia.sources && displayMedia.sources.length > 1
? displayMedia.sources.map((s) => `${getServiceIcon(s)} ${getServiceName(s)}`).join(' · ')
: `${getServiceIcon(displayMedia.source_service)} ${getServiceName(displayMedia.source_service)}`
}
</Text>
</View>
{displayMedia.sources && displayMedia.sources.length > 1 && (
<View style={styles.row}>
<Text style={styles.label}>Available on</Text>
<Text style={[styles.value, { color: '#8ab4f8' }]}>
{displayMedia.sources.length} services
</Text>
</View>
)}
{displayMedia.created_date && (
<View style={styles.row}>
<Text style={styles.label}>Date</Text>
+31 -4
View File
@@ -21,7 +21,31 @@ const PhotoGrid = ({
const itemSize = (width - GRID_GAP * (columns + 1)) / columns;
const getServiceIcon = (type) =>
({ GOOGLE_PHOTOS: '📷', ICLOUD_PHOTOS: '🍎', NAS: '💾' }[type] || '📱');
({ DEVICE: '📱', GOOGLE_PHOTOS: '📷', ICLOUD_PHOTOS: '🍎', NAS: '💾' }[type] || '📱');
const renderSourceBadges = (item) => {
const sources = item.sources && item.sources.length > 0
? item.sources
: [item.source_service];
if (sources.length === 1) {
// Single source — simple badge
return (
<View style={styles.sourceBadge}>
<Text style={{ fontSize: 10 }}>{getServiceIcon(sources[0])}</Text>
</View>
);
}
// Multiple sources — show all icons with a subtle indicator
return (
<View style={styles.multiSourceBadge}>
{sources.map((src, i) => (
<Text key={src + i} style={{ fontSize: 9 }}>{getServiceIcon(src)}</Text>
))}
</View>
);
};
const renderItem = ({ item }) => (
<TouchableOpacity
@@ -41,9 +65,7 @@ const PhotoGrid = ({
<Text style={{ color: '#fff', fontSize: 20 }}>▶</Text>
</View>
)}
<View style={styles.sourceBadge}>
<Text style={{ fontSize: 10 }}>{getServiceIcon(item.source_service)}</Text>
</View>
{renderSourceBadges(item)}
</TouchableOpacity>
);
@@ -82,6 +104,11 @@ const styles = StyleSheet.create({
position: 'absolute', bottom: 4, right: 4, backgroundColor: 'rgba(0,0,0,0.5)',
borderRadius: 8, paddingHorizontal: 4, paddingVertical: 2,
},
multiSourceBadge: {
position: 'absolute', bottom: 4, right: 4, backgroundColor: 'rgba(0,0,0,0.6)',
borderRadius: 8, paddingHorizontal: 4, paddingVertical: 2,
flexDirection: 'row', gap: 2,
},
});
export default PhotoGrid;