| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- import React, { useEffect, useState } from 'react';
- import {
- View,
- Text,
- StyleSheet,
- TouchableOpacity,
- FlatList,
- TextInput,
- Modal,
- Alert,
- } from 'react-native';
- import AsyncStorage from '@react-native-async-storage/async-storage';
- import { AntDesign } from '@expo/vector-icons';
- import { Song } from '@/types/song';
- import {loadFavoriteFolders, saveFavoriteFolders} from "@/service/async-storage";
- interface Folder {
- id: string;
- name: string;
- songs: Song[];
- expanded: boolean;
- }
- export default function FavoriteScreen() {
- const [folders, setFolders] = useState<Folder[]>([]);
- const [modalVisible, setModalVisible] = useState(false);
- const [newFolderName, setNewFolderName] = useState('');
-
- useEffect(() => {
- loadFavoriteFolders().then((folders) => setFolders(folders))
- }, []);
- const toggleFolder = (id: string) => {
- const updated = folders.map((f) =>
- f.id === id ? { ...f, expanded: !f.expanded } : f
- );
- setFolders(updated);
- saveFavoriteFolders(updated);
- };
- const addFolder = () => {
- if (!newFolderName.trim()) return;
- const newFolder: Folder = {
- id: Date.now().toString(),
- name: newFolderName.trim(),
- songs: [],
- expanded: false,
- };
- const updated = [...folders, newFolder];
- setFolders(updated);
- saveFavoriteFolders(updated);
- setNewFolderName('');
- setModalVisible(false);
- };
- const deleteFolder = (id: string) => {
- Alert.alert('삭제 확인', '이 폴더를 삭제하시겠습니까?', [
- { text: '취소', style: 'cancel' },
- {
- text: '삭제',
- style: 'destructive',
- onPress: () => {
- const updated = folders.filter((f) => f.id !== id);
- setFolders(updated);
- saveFavoriteFolders(updated);
- },
- },
- ]);
- };
- const renderFolder = ({ item }: { item: Folder }) => (
- <View style={styles.folderContainer}>
- <TouchableOpacity
- onPress={() => toggleFolder(item.id)}
- style={styles.folderHeader}
- >
- <Text style={styles.folderTitle}>{item.name}</Text>
- <View style={{ flexDirection: 'row' }}>
- <TouchableOpacity onPress={() => deleteFolder(item.id)}>
- <AntDesign name="delete" size={20} color="gray" style={{ marginLeft: 10 }} />
- </TouchableOpacity>
- <AntDesign
- name={item.expanded ? 'up' : 'down'}
- size={16}
- color="black"
- style={{ marginLeft: 10 }}
- />
- </View>
- </TouchableOpacity>
- {item.expanded &&
- item.songs.map((song) => (
- <View key={song.no} style={styles.songItem}>
- <Text style={styles.songText}>
- 🎵 {song.title} - {song.singer}
- </Text>
- </View>
- ))}
- </View>
- );
- return (
- <View style={styles.container}>
- <Text style={styles.title}>⭐ 즐겨찾기</Text>
- <FlatList
- data={folders}
- renderItem={renderFolder}
- keyExtractor={(item) => item.id}
- contentContainerStyle={{ paddingBottom: 80 }}
- />
- <TouchableOpacity
- style={styles.addButton}
- onPress={() => setModalVisible(true)}
- >
- <Text style={styles.addButtonText}>+ 새 폴더 추가</Text>
- </TouchableOpacity>
- {/* 폴더 추가 모달 */}
- <Modal visible={modalVisible} transparent animationType="slide">
- <View style={styles.modalOverlay}>
- <View style={styles.modalContent}>
- <Text style={{ fontWeight: 'bold', marginBottom: 10 }}>폴더 이름 입력</Text>
- <TextInput
- placeholder="예: 나만의 명곡"
- value={newFolderName}
- onChangeText={setNewFolderName}
- style={styles.input}
- />
- <View style={{ flexDirection: 'row', marginTop: 10 }}>
- <TouchableOpacity
- style={[styles.modalButton, { backgroundColor: '#ccc' }]}
- onPress={() => setModalVisible(false)}
- >
- <Text>취소</Text>
- </TouchableOpacity>
- <TouchableOpacity
- style={[styles.modalButton, { backgroundColor: '#4CAF50' }]}
- onPress={addFolder}
- >
- <Text style={{ color: 'white' }}>확인</Text>
- </TouchableOpacity>
- </View>
- </View>
- </View>
- </Modal>
- </View>
- );
- }
- const styles = StyleSheet.create({
- container: { flex: 1, padding: 16 },
- title: { fontSize: 20, fontWeight: 'bold', marginBottom: 12 },
- folderContainer: { marginBottom: 12, borderBottomWidth: 1, borderColor: '#ddd' },
- folderHeader: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- paddingVertical: 8,
- alignItems: 'center',
- },
- folderTitle: { fontSize: 16, fontWeight: 'bold' },
- songItem: { paddingLeft: 16, paddingVertical: 4 },
- songText: { fontSize: 14 },
- addButton: {
- position: 'absolute',
- bottom: 20,
- left: 20,
- right: 20,
- backgroundColor: '#007AFF',
- padding: 16,
- borderRadius: 10,
- alignItems: 'center',
- },
- addButtonText: { color: 'white', fontWeight: 'bold' },
- modalOverlay: {
- flex: 1,
- backgroundColor: '#00000099',
- justifyContent: 'center',
- alignItems: 'center',
- },
- modalContent: {
- width: 300,
- backgroundColor: 'white',
- borderRadius: 10,
- padding: 16,
- },
- input: {
- borderWidth: 1,
- borderColor: '#ddd',
- borderRadius: 6,
- padding: 8,
- },
- modalButton: {
- flex: 1,
- padding: 10,
- marginHorizontal: 5,
- borderRadius: 6,
- alignItems: 'center',
- },
- });
|