mirror of
https://github.com/mudabbir-ahmad/Media-Aggregation-and-Sorting-System.git
synced 2026-10-07 19:40:20 +00:00
refactored code into components (major cleanup for reused code) added email for local acccount creation and changes to how nas and google photo connections work and added themes
This commit is contained in:
1 parent
c1d7139055
commit
6f4ec66e2e
17 files changed
+1761
-1018
No files matched your search
+32
-98
@@ -1,30 +1,25 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
StyleSheet, View, Text, TextInput, FlatList, TouchableOpacity,
|
||||
Dimensions, Image, Modal, ActivityIndicator,
|
||||
StyleSheet, View, Text, TextInput, TouchableOpacity, ActivityIndicator,
|
||||
} from 'react-native';
|
||||
import { useTheme } from '../services/ThemeContext';
|
||||
import DatabaseService from '../services/DatabaseService';
|
||||
|
||||
const { width } = Dimensions.get('window');
|
||||
const GRID_COLUMNS = 3;
|
||||
const GRID_GAP = 2;
|
||||
const imageSize = (width - GRID_GAP * (GRID_COLUMNS + 1)) / GRID_COLUMNS;
|
||||
import PhotoGrid from '../components/PhotoGrid';
|
||||
import MediaViewer from '../components/MediaViewer';
|
||||
|
||||
const SearchScreen = ({ route }) => {
|
||||
const userId = route?.params?.userId;
|
||||
const { theme } = useTheme();
|
||||
const [query, setQuery] = useState('');
|
||||
const [results, setResults] = useState([]);
|
||||
const [searching, setSearching] = useState(false);
|
||||
const [selectedMedia, setSelectedMedia] = useState(null);
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [viewerVisible, setViewerVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (query.trim().length > 0) {
|
||||
performSearch();
|
||||
} else {
|
||||
setResults([]);
|
||||
}
|
||||
if (query.trim().length > 0) performSearch();
|
||||
else setResults([]);
|
||||
}, 300);
|
||||
return () => clearTimeout(timer);
|
||||
}, [query]);
|
||||
@@ -42,36 +37,14 @@ const SearchScreen = ({ route }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getServiceIcon = (type) => ({ GOOGLE_PHOTOS: '📷', ICLOUD_PHOTOS: '🍎', NAS: '💾' }[type] || '📱');
|
||||
|
||||
const renderItem = ({ item }) => (
|
||||
<TouchableOpacity
|
||||
style={styles.gridItem}
|
||||
onPress={() => { setSelectedMedia(item); setModalVisible(true); }}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
{item.local_path ? (
|
||||
<Image source={{ uri: item.local_path }} style={styles.gridImage} resizeMode="cover" />
|
||||
) : (
|
||||
<View style={styles.placeholder}>
|
||||
<Text style={{ fontSize: 28 }}>📷</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.sourceBadge}>
|
||||
<Text style={{ fontSize: 10 }}>{getServiceIcon(item.source_service)}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{/* Search bar */}
|
||||
<View style={styles.searchBar}>
|
||||
<View style={[styles.container, { backgroundColor: theme.background }]}>
|
||||
<View style={[styles.searchBar, { backgroundColor: theme.searchBg }]}>
|
||||
<Text style={styles.searchIcon}>🔍</Text>
|
||||
<TextInput
|
||||
style={styles.searchInput}
|
||||
style={[styles.searchInput, { color: theme.text }]}
|
||||
placeholder="Search photos and videos"
|
||||
placeholderTextColor="#9aa0a6"
|
||||
placeholderTextColor={theme.textTertiary}
|
||||
value={query}
|
||||
onChangeText={setQuery}
|
||||
autoFocus
|
||||
@@ -80,105 +53,66 @@ const SearchScreen = ({ route }) => {
|
||||
/>
|
||||
{query.length > 0 && (
|
||||
<TouchableOpacity onPress={() => setQuery('')}>
|
||||
<Text style={styles.clearButton}>✕</Text>
|
||||
<Text style={[styles.clearButton, { color: theme.textSecondary }]}>✕</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{searching && (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="small" color="#1a73e8" />
|
||||
<ActivityIndicator size="small" color={theme.primary} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
{!searching && query.length > 0 && results.length === 0 && (
|
||||
<View style={styles.emptyContainer}>
|
||||
<Text style={styles.emptyIcon}>🔍</Text>
|
||||
<Text style={styles.emptyTitle}>No results</Text>
|
||||
<Text style={styles.emptyText}>Try searching with different keywords</Text>
|
||||
<Text style={[styles.emptyTitle, { color: theme.text }]}>No results</Text>
|
||||
<Text style={[styles.emptyText, { color: theme.textSecondary }]}>Try different keywords</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{query.length === 0 && (
|
||||
<View style={styles.emptyContainer}>
|
||||
<Text style={styles.emptyIcon}>🔍</Text>
|
||||
<Text style={styles.emptyTitle}>Search your photos</Text>
|
||||
<Text style={styles.emptyText}>Search by title or description</Text>
|
||||
<Text style={[styles.emptyTitle, { color: theme.text }]}>Search your photos</Text>
|
||||
<Text style={[styles.emptyText, { color: theme.textSecondary }]}>Search by title or description</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{results.length > 0 && (
|
||||
<>
|
||||
<Text style={styles.resultCount}>{results.length} result{results.length !== 1 ? 's' : ''}</Text>
|
||||
<FlatList
|
||||
<Text style={[styles.resultCount, { color: theme.textSecondary }]}>
|
||||
{results.length} result{results.length !== 1 ? 's' : ''}
|
||||
</Text>
|
||||
<PhotoGrid
|
||||
data={results}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item, i) => item.id?.toString() || i.toString()}
|
||||
numColumns={GRID_COLUMNS}
|
||||
contentContainerStyle={styles.gridContent}
|
||||
onItemPress={(item) => { setSelectedMedia(item); setViewerVisible(true); }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Photo viewer modal */}
|
||||
<Modal visible={modalVisible} transparent animationType="fade" onRequestClose={() => setModalVisible(false)}>
|
||||
<View style={styles.modalContainer}>
|
||||
<TouchableOpacity style={styles.modalClose} onPress={() => setModalVisible(false)}>
|
||||
<Text style={styles.modalCloseText}>✕</Text>
|
||||
</TouchableOpacity>
|
||||
{selectedMedia?.local_path ? (
|
||||
<Image source={{ uri: selectedMedia.local_path }} style={styles.fullImage} resizeMode="contain" />
|
||||
) : (
|
||||
<View style={styles.fullPlaceholder}>
|
||||
<Text style={{ fontSize: 64 }}>📷</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.mediaInfo}>
|
||||
<Text style={styles.mediaTitle}>{selectedMedia?.title || 'Photo'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
<MediaViewer visible={viewerVisible} media={selectedMedia} onClose={() => setViewerVisible(false)} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1, backgroundColor: '#fff' },
|
||||
container: { flex: 1 },
|
||||
searchBar: {
|
||||
flexDirection: 'row', alignItems: 'center', marginHorizontal: 12,
|
||||
marginTop: 48, marginBottom: 8, backgroundColor: '#f1f3f4',
|
||||
borderRadius: 24, paddingHorizontal: 14, paddingVertical: 10, gap: 8,
|
||||
marginTop: 48, marginBottom: 8, borderRadius: 24,
|
||||
paddingHorizontal: 14, paddingVertical: 10, gap: 8,
|
||||
},
|
||||
searchIcon: { fontSize: 16 },
|
||||
searchInput: { flex: 1, fontSize: 15, color: '#202124', padding: 0 },
|
||||
clearButton: { fontSize: 16, color: '#5f6368', padding: 4 },
|
||||
searchInput: { flex: 1, fontSize: 15, padding: 0 },
|
||||
clearButton: { fontSize: 16, padding: 4 },
|
||||
loadingContainer: { padding: 20, alignItems: 'center' },
|
||||
emptyContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 24 },
|
||||
emptyIcon: { fontSize: 48, marginBottom: 12 },
|
||||
emptyTitle: { fontSize: 18, fontWeight: '600', color: '#202124', marginBottom: 6 },
|
||||
emptyText: { fontSize: 13, color: '#5f6368', textAlign: 'center' },
|
||||
resultCount: { paddingHorizontal: 16, paddingVertical: 8, fontSize: 13, color: '#5f6368' },
|
||||
gridContent: { paddingBottom: 20 },
|
||||
gridItem: {
|
||||
width: imageSize, height: imageSize, margin: GRID_GAP / 2,
|
||||
backgroundColor: '#f1f3f4', position: 'relative',
|
||||
},
|
||||
gridImage: { width: '100%', height: '100%' },
|
||||
placeholder: { width: '100%', height: '100%', justifyContent: 'center', alignItems: 'center', backgroundColor: '#e8eaed' },
|
||||
sourceBadge: {
|
||||
position: 'absolute', bottom: 4, right: 4, backgroundColor: 'rgba(0,0,0,0.5)',
|
||||
borderRadius: 8, paddingHorizontal: 4, paddingVertical: 2,
|
||||
},
|
||||
modalContainer: { flex: 1, backgroundColor: '#000' },
|
||||
modalClose: {
|
||||
position: 'absolute', top: 44, right: 16, zIndex: 20, width: 36, height: 36,
|
||||
borderRadius: 18, backgroundColor: 'rgba(255,255,255,0.2)', justifyContent: 'center', alignItems: 'center',
|
||||
},
|
||||
modalCloseText: { color: '#fff', fontSize: 20, fontWeight: '600' },
|
||||
fullImage: { flex: 1, width: '100%' },
|
||||
fullPlaceholder: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||
mediaInfo: { backgroundColor: '#1a1a1a', padding: 16, paddingBottom: 32, borderTopLeftRadius: 16, borderTopRightRadius: 16 },
|
||||
mediaTitle: { fontSize: 16, fontWeight: '600', color: '#fff' },
|
||||
emptyTitle: { fontSize: 18, fontWeight: '600', marginBottom: 6 },
|
||||
emptyText: { fontSize: 13, textAlign: 'center' },
|
||||
resultCount: { paddingHorizontal: 16, paddingVertical: 8, fontSize: 13 },
|
||||
});
|
||||
|
||||
export default SearchScreen;
|
||||
|
||||
Reference in new issue
Block a user