|
|
@@ -15,6 +15,8 @@ import {
|
|
|
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;
|
|
|
@@ -23,19 +25,26 @@ interface Option {
|
|
|
|
|
|
export default function SearchScreen() {
|
|
|
const [keyword, setKeyword] = useState<string>('');
|
|
|
- const [criteria, setCriteria] = useState<Criteria>('title');
|
|
|
+ const [criteria, setCriteria] = useState<Criteria>(API_FIELDS.TITLE);
|
|
|
const [results, setResults] = useState<Song[]>([]);
|
|
|
const [isDropdownVisible, setIsDropdownVisible] = useState<boolean>(false);
|
|
|
+ const [isLoading, setIsLoading] = useState<boolean>(false); // 로딩 상태 추가
|
|
|
|
|
|
const options: Option[] = [
|
|
|
- { label: '제목', value: 'title' },
|
|
|
- { label: '가수', value: 'artist' },
|
|
|
+ { label: '제목', value: API_FIELDS.TITLE },
|
|
|
+ { label: '가수', value: API_FIELDS.SINGER },
|
|
|
];
|
|
|
|
|
|
const handleSearch = async (): Promise<void> => {
|
|
|
+ if (!keyword.trim()) return; // 빈 검색어 처리
|
|
|
+ setIsLoading(true); // 검색 시작 시 로딩 시작
|
|
|
Keyboard.dismiss();
|
|
|
- const filtered: Song[] = await fetchSongs(criteria, keyword);
|
|
|
- setResults(filtered);
|
|
|
+ try {
|
|
|
+ const filtered: Song[] = await fetchSongs(criteria, keyword);
|
|
|
+ setResults(filtered);
|
|
|
+ } finally {
|
|
|
+ setIsLoading(false); // 검색 완료 시 로딩 종료
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
const handleAddToFavorites = async (song: Song): Promise<void> => {
|
|
|
@@ -96,9 +105,9 @@ export default function SearchScreen() {
|
|
|
placeholder="검색어 입력"
|
|
|
value={keyword}
|
|
|
onChangeText={(text: string) => setKeyword(text)}
|
|
|
- onSubmitEditing={handleSearch} // 엔터 키 입력 시 검색 실행
|
|
|
+ onSubmitEditing={handleSearch}
|
|
|
style={styles.input}
|
|
|
- returnKeyType="search" // 키보드의 엔터 키를 "검색" 버튼으로 표시
|
|
|
+ returnKeyType="search"
|
|
|
/>
|
|
|
|
|
|
<TouchableOpacity onPress={handleSearch} style={styles.searchButton}>
|
|
|
@@ -106,21 +115,25 @@ export default function SearchScreen() {
|
|
|
</TouchableOpacity>
|
|
|
</View>
|
|
|
|
|
|
- <FlatList<Song>
|
|
|
- data={results}
|
|
|
- keyExtractor={(item) => item.id}
|
|
|
- renderItem={({ item }) => (
|
|
|
- <TouchableOpacity onPress={() => handleAddToFavorites(item)}>
|
|
|
- <View style={styles.item}>
|
|
|
- <Text>
|
|
|
- {item.id} - {item.title} ({item.artist})
|
|
|
- </Text>
|
|
|
- </View>
|
|
|
- </TouchableOpacity>
|
|
|
- )}
|
|
|
- ListEmptyComponent={<Text>검색 결과가 없습니다.</Text>}
|
|
|
- contentContainerStyle={{ flexGrow: 1 }}
|
|
|
- />
|
|
|
+ {isLoading ? (
|
|
|
+ <Loading /> // 로딩 중일 때 Loading 컴포넌트 표시
|
|
|
+ ) : (
|
|
|
+ <FlatList<Song>
|
|
|
+ data={results}
|
|
|
+ keyExtractor={(item) => item.no}
|
|
|
+ renderItem={({ item }) => (
|
|
|
+ <TouchableOpacity onPress={() => handleAddToFavorites(item)}>
|
|
|
+ <View style={styles.item}>
|
|
|
+ <Text>
|
|
|
+ {item.no} - {item.title} ({item.singer})
|
|
|
+ </Text>
|
|
|
+ </View>
|
|
|
+ </TouchableOpacity>
|
|
|
+ )}
|
|
|
+ ListEmptyComponent={<Text>검색 결과가 없습니다.</Text>}
|
|
|
+ contentContainerStyle={{ flexGrow: 1 }}
|
|
|
+ />
|
|
|
+ )}
|
|
|
</KeyboardAvoidingView>
|
|
|
);
|
|
|
}
|