| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- 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/async-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<string>('');
- 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: API_FIELDS.TITLE },
- { label: '가수', value: API_FIELDS.SINGER },
- ];
- const handleSearch = async (): Promise<void> => {
- // 빈 검색어 처리
- 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<void> => {
- 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 (
- <KeyboardAvoidingView
- style={styles.container}
- behavior={Platform.OS === 'ios' ? 'padding' : undefined}
- >
- <View style={styles.searchRow}>
- <View style={styles.dropdownWrapper}>
- <TouchableOpacity
- style={styles.dropdownButton}
- onPress={() => setIsDropdownVisible(!isDropdownVisible)}
- >
- <Text style={styles.dropdownText}>
- {options.find((opt) => opt.value === criteria)?.label || '선택'}
- </Text>
- <Text style={styles.dropdownArrow}>▼</Text>
- </TouchableOpacity>
- <Modal
- transparent
- visible={isDropdownVisible}
- animationType="fade"
- onRequestClose={() => setIsDropdownVisible(false)}
- >
- <TouchableOpacity
- style={styles.modalOverlay}
- onPress={() => setIsDropdownVisible(false)}
- >
- <View style={styles.dropdownMenu}>
- {options.map((option) => (
- <TouchableOpacity
- key={option.value}
- style={styles.dropdownItem}
- onPress={() => handleSelectOption(option.value)}
- >
- <Text style={styles.dropdownItemText}>{option.label}</Text>
- </TouchableOpacity>
- ))}
- </View>
- </TouchableOpacity>
- </Modal>
- </View>
- <TextInput
- placeholder="검색어 입력"
- value={keyword}
- onChangeText={(text: string) => setKeyword(text)}
- onSubmitEditing={handleSearch}
- style={styles.input}
- returnKeyType="search"
- />
- <TouchableOpacity onPress={handleSearch} style={styles.searchButton}>
- <Text style={styles.searchText}>검색</Text>
- </TouchableOpacity>
- </View>
- {isLoading ? (
- <Loading />
- ) : (
- <FlatList<Song>
- data={results}
- keyExtractor={(item) => item.no}
- renderItem={({ item }) => (
- <TouchableOpacity onPress={() => {}}>
- <View style={styles.item}>
- <Text>
- {item.no} - {item.title} ({item.singer})
- </Text>
- </View>
- </TouchableOpacity>
- )}
- ListEmptyComponent={<Text>검색 결과가 없습니다.</Text>}
- contentContainerStyle={{ flexGrow: 1 }}
- />
- )}
- </KeyboardAvoidingView>
- );
- }
- 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,
- },
- });
|