import React, { useState } from 'react'; import { View, Text, TextInput, TouchableOpacity, FlatList, StyleSheet, Dimensions, KeyboardAvoidingView, Platform, Modal, Keyboard, } from 'react-native'; import { fetchSongs } from '@/service/api'; import { addToFavoritesStorage } from '@/service/storage'; import { Song, Criteria } from '@/types/song'; import { API_FIELDS } from '@/constants/apiFields'; import Loading from '@/components/Loading'; interface Option { label: string; value: Criteria; } export default function SearchScreen() { const [keyword, setKeyword] = useState(''); const [criteria, setCriteria] = useState(API_FIELDS.TITLE); const [results, setResults] = useState([]); const [isDropdownVisible, setIsDropdownVisible] = useState(false); const [isLoading, setIsLoading] = useState(false); // 로딩 상태 추가 const options: Option[] = [ { label: '제목', value: API_FIELDS.TITLE }, { label: '가수', value: API_FIELDS.SINGER }, ]; const handleSearch = async (): Promise => { if (!keyword.trim()) return; // 빈 검색어 처리 setIsLoading(true); // 검색 시작 시 로딩 시작 Keyboard.dismiss(); try { const filtered: Song[] = await fetchSongs(criteria, keyword); setResults(filtered); } finally { setIsLoading(false); // 검색 완료 시 로딩 종료 } }; const handleAddToFavorites = async (song: Song): Promise => { const success: boolean = await addToFavoritesStorage(song); if (!success) { console.log('Failed to add to favorites'); } }; const handleSelectOption = (value: Criteria): void => { setCriteria(value); setIsDropdownVisible(false); }; return ( setIsDropdownVisible(!isDropdownVisible)} > {options.find((opt) => opt.value === criteria)?.label || '선택'} setIsDropdownVisible(false)} > setIsDropdownVisible(false)} > {options.map((option) => ( handleSelectOption(option.value)} > {option.label} ))} setKeyword(text)} onSubmitEditing={handleSearch} style={styles.input} returnKeyType="search" /> 검색 {isLoading ? ( // 로딩 중일 때 Loading 컴포넌트 표시 ) : ( data={results} keyExtractor={(item) => item.no} renderItem={({ item }) => ( handleAddToFavorites(item)}> {item.no} - {item.title} ({item.singer}) )} ListEmptyComponent={검색 결과가 없습니다.} contentContainerStyle={{ flexGrow: 1 }} /> )} ); } const styles = StyleSheet.create({ container: { flex: 1, padding: 16, }, searchRow: { flexDirection: 'row', alignItems: 'center', marginBottom: 16, width: '100%', }, dropdownWrapper: { width: Dimensions.get('window').width * 0.3 - 24, marginRight: 8, }, dropdownButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', borderWidth: 1, borderColor: '#ccc', borderRadius: 8, paddingHorizontal: 10, paddingVertical: 10, height: 40, backgroundColor: '#fff', }, dropdownText: { fontSize: 16, color: '#000', }, dropdownArrow: { fontSize: 12, color: '#000', }, modalOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.2)', justifyContent: 'flex-start', paddingTop: 120, paddingHorizontal: 16, }, dropdownMenu: { backgroundColor: '#fff', borderRadius: 8, width: Dimensions.get('window').width * 0.3 - 24, elevation: 5, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4, }, dropdownItem: { paddingVertical: 10, paddingHorizontal: 10, borderBottomWidth: 1, borderBottomColor: '#eee', }, dropdownItemText: { fontSize: 16, color: '#000', }, input: { flex: 1, height: 40, borderWidth: 1, borderColor: '#ccc', borderRadius: 8, paddingHorizontal: 8, marginRight: 8, }, searchButton: { backgroundColor: '#007AFF', paddingHorizontal: 8, paddingVertical: 10, borderRadius: 8, }, searchText: { color: '#fff', fontWeight: 'bold', }, item: { padding: 12, borderBottomWidth: 1, borderBottomColor: '#eee', width: Dimensions.get('window').width - 32, }, });