diff --git a/assets/images/study/1.png b/assets/images/study/1.png index d817780..d7a7b7d 100644 Binary files a/assets/images/study/1.png and b/assets/images/study/1.png differ diff --git a/assets/images/study/2.png b/assets/images/study/2.png index 44bff8a..a14099a 100644 Binary files a/assets/images/study/2.png and b/assets/images/study/2.png differ diff --git a/assets/images/study/3.png b/assets/images/study/3.png index 6bdcc68..071e5df 100644 Binary files a/assets/images/study/3.png and b/assets/images/study/3.png differ diff --git a/assets/images/study/4.png b/assets/images/study/4.png index cfbd96e..8cbc4e7 100644 Binary files a/assets/images/study/4.png and b/assets/images/study/4.png differ diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 2418583..5c24565 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,8 @@ + io.flutter.embedded_views_preview + CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion diff --git a/lib/main.dart b/lib/main.dart index f08d9ab..92c8077 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:earlips/main_app.dart'; +import 'package:flutter/services.dart'; void main() async { /* Open .env file */ @@ -12,6 +13,10 @@ void main() async { await initializeDateFormatting(); await EasyLocalization.ensureInitialized(); + await SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); diff --git a/lib/models/word_card_model.dart b/lib/models/word_card_model.dart index aaaf5eb..b20e37d 100644 --- a/lib/models/word_card_model.dart +++ b/lib/models/word_card_model.dart @@ -3,12 +3,14 @@ class WordCard { final String word; final String speaker; final String video; + final int type; WordCard({ required this.id, required this.word, required this.speaker, required this.video, + required this.type, }); Map toMap() { @@ -17,6 +19,7 @@ class WordCard { 'word': word, 'speaker': speaker, 'video': video, + 'type': type, }; } @@ -26,6 +29,7 @@ class WordCard { word: doc['word'], speaker: doc['speaker'], video: doc['video'], + type: doc['type'], ); } @@ -33,15 +37,15 @@ class WordCard { int? id, String? word, String? speaker, - bool? isDone, - String? doneDate, String? video, + int? type, }) { return WordCard( id: id ?? this.id, word: word ?? this.word, speaker: speaker ?? this.speaker, video: video ?? this.video, + type: type ?? this.type, ); } } diff --git a/lib/viewModels/word/word_viewmodel.dart b/lib/viewModels/word/word_viewmodel.dart index 17260c7..0039bca 100644 --- a/lib/viewModels/word/word_viewmodel.dart +++ b/lib/viewModels/word/word_viewmodel.dart @@ -9,15 +9,17 @@ import 'package:intl/intl.dart'; class WordViewModel extends GetxController { final FirebaseFirestore _firestore = FirebaseFirestore.instance; final FirebaseAuth _auth = FirebaseAuth.instance; + final int type; RxList wordList = RxList([]); RxInt currentIndex = 0.obs; + WordViewModel({required this.type}); + @override void onInit() { super.onInit(); - fetchWords(); - fetchWords(); + fetchWords(type); wordList.refresh(); } @@ -26,11 +28,15 @@ class WordViewModel extends GetxController { } // 단어 데이터 가져오기 - Future fetchWords() async { + Future fetchWords(int type) async { final uid = _auth.currentUser?.uid; if (uid != null) { // 모든 단어 데이터 가져오기 - final wordsQuery = await _firestore.collection('words').get(); + final wordsQuery = await _firestore + .collection('words') + .where('type', isEqualTo: type) + .get(); + final allWords = wordsQuery.docs .map((doc) => WordCard.fromDocument(doc.data())) .toList(); @@ -62,8 +68,10 @@ class WordViewModel extends GetxController { // 단어 완료 처리 Future markWordAsDone(WordCard word) async { final uid = _auth.currentUser?.uid; + String currentDate = DateFormat('yyyy/MM/dd').format(DateTime.now()); + if (uid != null) { - // Add or update data in the user's 'words' subcollection in Firestore + // 유저 단어 데이터 업데이트 await _firestore .collection('users') .doc(uid) @@ -72,13 +80,47 @@ class WordViewModel extends GetxController { .set({ 'wordId': word.id, 'isDone': true, - 'doneDate': DateFormat('yyyy/MM/dd').format(DateTime.now()), + 'doneDate': currentDate, }); + // 유저 record 업데이트 + DocumentReference recordRef = _firestore + .collection('users') + .doc(uid) + .collection('records') + .doc(DateFormat('yyyyMMdd').format(DateTime.now())); + + try { + await _firestore.runTransaction((transaction) async { + // Get the current record + DocumentSnapshot recordSnapshot = await transaction.get(recordRef); + + if (recordSnapshot.exists) { + String existingDate = recordSnapshot.get('date'); + if (existingDate == currentDate) { + transaction.update(recordRef, { + 'cnt': FieldValue.increment(1), + }); + } + } else { + print('different date!!!!!!!!!!!!!'); + transaction.set(recordRef, { + 'cnt': 1, + 'date': currentDate, + 'dateFormat': DateTime.now(), + }); + } + }); + } catch (e) { + print("Transaction failed: $e"); + } + // 로컬에서 단어 데이터 업데이트 final index = wordList.indexWhere((element) => element.wordCard.id == word.id); + + // 만약 로컬에서 단어를 찾지 못했을 경우를 대비 if (index != -1) { - // Handle the scenario where we don't find the card locally. + // 단어 데이터 업데이트 wordList[index] = WordData( wordCard: word, userWord: UserWord( diff --git a/lib/views/study/date_study_screen.dart b/lib/views/study/date_study_screen.dart index 8c06193..3efe831 100644 --- a/lib/views/study/date_study_screen.dart +++ b/lib/views/study/date_study_screen.dart @@ -1,3 +1,4 @@ +import 'package:earlips/views/base/default_back_appbar.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; // ViewModel import 경로는 실제 프로젝트 구조에 따라 달라질 수 있습니다. @@ -13,14 +14,11 @@ class DateStudyScreen extends StatelessWidget { final sessions = viewModel.getSessions(); return Scaffold( - appBar: AppBar( - title: Text(DateFormat('yyyy/MM/dd').format(date), - style: const TextStyle( - color: Colors.black, - fontSize: 18, - fontWeight: FontWeight.w500, - )), // 동적으로 날짜를 표시 - centerTitle: true, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(kToolbarHeight), + child: DefaultBackAppbar( + title: DateFormat('yyyy/MM/dd').format(date), + ), ), body: ListView.separated( padding: const EdgeInsets.fromLTRB(25, 20, 25, 20), diff --git a/lib/views/word/widget/word_list_widget.dart b/lib/views/word/widget/word_list_widget.dart index 1aa7311..724c2da 100644 --- a/lib/views/word/widget/word_list_widget.dart +++ b/lib/views/word/widget/word_list_widget.dart @@ -1,4 +1,3 @@ -import 'package:earlips/models/word_card_model.dart'; import 'package:earlips/models/word_data_model.dart'; import 'package:earlips/utilities/style/color_styles.dart'; import 'package:earlips/viewModels/word/word_viewmodel.dart'; @@ -7,11 +6,15 @@ import 'package:get/get.dart'; class WordList extends StatefulWidget { final List wordDataList; + final int type; PageController pageController; WordList( - {super.key, required this.wordDataList, required this.pageController}); + {super.key, + required this.wordDataList, + required this.pageController, + required this.type}); @override _WordListState createState() => _WordListState(); @@ -38,7 +41,6 @@ class _WordListState extends State { setState(() { wordViewModel.currentIndex.value = index; }); - print('currentIndex: ${wordViewModel.currentIndex.value}'); }, itemBuilder: (context, index) { final wordData = widget.wordDataList[index]; @@ -102,8 +104,8 @@ class _WordListState extends State { tileColor: Colors.white, title: Text( wordData.wordCard.word, - style: const TextStyle( - fontSize: 24, + style: TextStyle( + fontSize: widget.type == 2 ? 19 : 24, fontWeight: FontWeight.w600, color: ColorSystem.black, ), @@ -111,8 +113,8 @@ class _WordListState extends State { ), subtitle: Text( wordData.wordCard.speaker, - style: const TextStyle( - fontSize: 16, + style: TextStyle( + fontSize: widget.type == 2 ? 16 : 20, fontWeight: FontWeight.w600, color: ColorSystem.gray4, ), diff --git a/lib/views/word/word_screen.dart b/lib/views/word/word_screen.dart index d7167a8..508edf5 100644 --- a/lib/views/word/word_screen.dart +++ b/lib/views/word/word_screen.dart @@ -14,7 +14,9 @@ class WordScreen extends StatelessWidget { @override Widget build(BuildContext context) { - final wordViewModel = Get.put(WordViewModel()); + final wordViewModel = Get.put(WordViewModel( + type: type, + )); final PageController pageController = PageController(initialPage: wordViewModel.currentIndex.value); @@ -43,6 +45,7 @@ class WordScreen extends StatelessWidget { builder: (controller) => WordList( // viewmodel wordDataList: controller.wordList, + type: type, pageController: pageController, ), ), @@ -67,20 +70,40 @@ class WordScreen extends StatelessWidget { ], ), ), - + const Spacer(), + // wordViewModel final String video로 영상 유튜브 링크를 바로 볼 수 있게 하기 + const Padding( + padding: EdgeInsets.all(20.0), + // child: YoutubeWordPlayer(), + ), const Spacer(), // wordViewModel final String video로 영상 유튜브 링크를 바로 볼 수 있게 하기 ElevatedButton( - onPressed: () { - Get.toNamed( - '/video', - arguments: wordViewModel - .wordList[wordViewModel.currentIndex.value] - .wordCard - .video, + onPressed: () async { + await wordViewModel.markWordAsDone(wordViewModel + .wordList[wordViewModel.currentIndex.value].wordCard); + + // YouTubePlayer 위젯 추가 + Get.dialog( + AlertDialog( + title: const Text('동영상 재생'), + content: Column( + children: [ + YoutubePlayer( + controller: YoutubePlayerController( + initialVideoId: wordViewModel + .wordList[wordViewModel.currentIndex.value] + .wordCard + .video + .split('v=')[1], + ), + ), + ], + ), + ), ); }, - child: const Text("영상 보기"), + child: const Text("므"), ), ElevatedButton( onPressed: () async { @@ -121,3 +144,27 @@ class WordScreen extends StatelessWidget { ); } } + +class YoutubeWordPlayer extends StatefulWidget { + const YoutubeWordPlayer({super.key}); + + @override + State createState() => _YoutubeWordPlayerState(); +} + +class _YoutubeWordPlayerState extends State { + final wordViewModel = Get.find(); + + @override + Widget build(BuildContext context) { + print( + 'video: ${wordViewModel.wordList[wordViewModel.currentIndex.value].wordCard.video}'); + return YoutubePlayer( + controller: YoutubePlayerController( + initialVideoId: wordViewModel + .wordList[wordViewModel.currentIndex.value].wordCard.video + .split('v=')[1], + ), + ); + } +}