diff --git a/POME.xcodeproj/project.pbxproj b/POME.xcodeproj/project.pbxproj index 6aafee7..4b83962 100644 --- a/POME.xcodeproj/project.pbxproj +++ b/POME.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ 23431C322A24293500571286 /* DeleteGoalUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23431C312A24293500571286 /* DeleteGoalUseCase.swift */; }; 23431C342A242B1D00571286 /* GetRecordsOfGoalInRecordTabUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23431C332A242B1D00571286 /* GetRecordsOfGoalInRecordTabUseCase.swift */; }; 23431C362A242C8B00571286 /* GetNoSecondEmotionRecordsUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23431C352A242C8B00571286 /* GetNoSecondEmotionRecordsUseCase.swift */; }; + 2343FB032A269F7600820625 /* GoalWithRecordViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2343FB022A269F7600820625 /* GoalWithRecordViewModel.swift */; }; 2345E1C429818848003185E0 /* BaseResponseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2345E1C329818848003185E0 /* BaseResponseModel.swift */; }; 2345E1C829819471003185E0 /* UserRequestModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2345E1C729819471003185E0 /* UserRequestModel.swift */; }; 2345E1CA2981947A003185E0 /* UserResponseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2345E1C92981947A003185E0 /* UserResponseModel.swift */; }; @@ -327,6 +328,7 @@ 23431C312A24293500571286 /* DeleteGoalUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteGoalUseCase.swift; sourceTree = ""; }; 23431C332A242B1D00571286 /* GetRecordsOfGoalInRecordTabUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetRecordsOfGoalInRecordTabUseCase.swift; sourceTree = ""; }; 23431C352A242C8B00571286 /* GetNoSecondEmotionRecordsUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetNoSecondEmotionRecordsUseCase.swift; sourceTree = ""; }; + 2343FB022A269F7600820625 /* GoalWithRecordViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalWithRecordViewModel.swift; sourceTree = ""; }; 2345E1C329818848003185E0 /* BaseResponseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseResponseModel.swift; sourceTree = ""; }; 2345E1C729819471003185E0 /* UserRequestModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRequestModel.swift; sourceTree = ""; }; 2345E1C92981947A003185E0 /* UserResponseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserResponseModel.swift; sourceTree = ""; }; @@ -1586,6 +1588,7 @@ EE7D35DC29D5A16300D2AD60 /* Authentication */, EE7D35DE29D5A16300D2AD60 /* Review */, EE7D35E029D5A16300D2AD60 /* Goal */, + 2343FB022A269F7600820625 /* GoalWithRecordViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -2278,6 +2281,7 @@ 2375E4892A20397400843056 /* MyPageViewModel.swift in Sources */, 5724EC84291B29A700DC529B /* EmojiFloatingCollectionViewCell.swift in Sources */, EE38758029D917B900A26AD5 /* TextConverter.swift in Sources */, + 2343FB032A269F7600820625 /* GoalWithRecordViewModel.swift in Sources */, EE7D35EC29D5A16300D2AD60 /* GenerateGoalDateViewModel.swift in Sources */, 57543B7C2951BF5300A409E3 /* CalendarSheetCollectionViewCell.swift in Sources */, 23F57834292D5460002DD28E /* SettingViewController.swift in Sources */, diff --git a/POME/Presentation/ViewControllers/Record/RecordViewController.swift b/POME/Presentation/ViewControllers/Record/RecordViewController.swift index 7446423..0d9cc1b 100644 --- a/POME/Presentation/ViewControllers/Record/RecordViewController.swift +++ b/POME/Presentation/ViewControllers/Record/RecordViewController.swift @@ -18,9 +18,6 @@ class RecordViewController: BaseTabViewController { goalRelay.value } - private let INFO_SECTION = 0 - private let COUNT_OF_NOT_RECORD_CELL = 3 //record 이외 UI 구성하는 cell 3개 존재 - private var goalCategoryTableViewCell: GoalCategoryTableViewCell?{ recordView.recordTableView.cellForRow(at: [INFO_SECTION,0], cellType: GoalCategoryTableViewCell.self) } @@ -29,15 +26,14 @@ class RecordViewController: BaseTabViewController { return indexPath.row - 3 } + // Properties + private let INFO_SECTION = 0 + private let COUNT_OF_NOT_RECORD_CELL = 3 //record 이외 UI 구성하는 cell 3개 존재 + // GetGoal control private var isFirstLoad = true - // Goal Content - var goalContent: [GoalResponseModel] = [] -// var categorySelectedIdx = 0 - // Records - var recordsOfGoal: [RecordResponseModel] = [] - var noSecondEmotionRecords: [RecordResponseModel] = [] + // Page var recordPage: Int? // Cell Height @@ -45,6 +41,8 @@ class RecordViewController: BaseTabViewController { // Refresh Control var refreshControl = UIRefreshControl() + + // Life Cycles override func viewDidLoad() { super.viewDidLoad() viewModel.refreshData() @@ -57,6 +55,7 @@ class RecordViewController: BaseTabViewController { recordView.recordTableView.delegate = self recordView.recordTableView.dataSource = self } + override func layout() { super.layout() @@ -75,6 +74,7 @@ class RecordViewController: BaseTabViewController { make.top.equalTo(super.navigationView.snp.bottom) } } + override func initialize() { super.initialize() @@ -258,23 +258,6 @@ extension RecordViewController: UICollectionViewDelegate, UICollectionViewDataSo return cell } -// func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { -// goalRelay.accept(indexPath.row) - - // 기간이 지난 목표 -// if isGoalDateEnd(goalContent[itemIdx]) || self.noSecondEmotionRecords.count != 0 {self.showNoSecondEmotionWarning()} - - // 목표에 저장된 씀씀이 조회 -// self.recordPage = 0 -// self.recordsOfGoal.removeAll() -// getRecordsOfGoal(id: goalContent[itemIdx].id) -// getNoSecondEmotionRecords(id: goalContent[itemIdx].id) -// -// self.recordView.recordTableView.reloadData() - -// return true -// } - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { collectionView.cellForItem(at: indexPath, cellType: GoalTagCollectionViewCell.self)?.do{ $0.setSelectState() @@ -406,7 +389,7 @@ extension RecordViewController: UITableViewDelegate, UITableViewDataSource { // MARK: - Record Cell delegate extension RecordViewController: RecordCellDelegate{ func presentReactionSheet(indexPath: IndexPath) { - let data = recordsOfGoal[dataIndexBy(indexPath)].friendReactions + let data = viewModel.records[dataIndexBy(indexPath)].friendReactions FriendReactionSheetViewController(reactions: data).show(in: self) } @@ -460,111 +443,3 @@ extension RecordViewController { } } } -////MARK: - API -//extension RecordViewController { -// // MARK: 목표 리스트 조회 API -// func requestGetGoals(){ -// // api 호출할 때마다 Goal 배열 초기화 -// self.goalContent.removeAll() -// GoalService.shared.getUserGoals{ result in -// defer{ -// self.isFirstLoad = false -// } -// switch result{ -// case .success(let data): -// print("success goal:", data.content) -// for x in data.content { -// if !x.isEnd { -// self.goalContent.append(x) -// } -// } -// // 목표에 맞는 기록들 조회 -// if !self.goalContent.isEmpty { -// // 기록 초기화 후 다시 호출 -// self.recordPage = 0 -// self.recordsOfGoal.removeAll() -// self.getRecordsOfGoal(id: self.goalContent[self.selectedGoalIndex].id) -// } -// -// if let cell = self.recordView.recordTableView.cellForRow(at: [0,0]) as? GoalCategoryTableViewCell { -// cell.goalCollectionView.reloadData() -// self.recordView.recordTableView.reloadData() -// } -// self.refreshControl.endRefreshing() -// break -// default: -// print(result) -// NetworkAlert.show(in: self){ [weak self] in -// self?.requestGetGoals() -// } -// break -// } -// } -// } -// // MARK: 목표 삭제 API -// private func deleteGoal(id: Int){ -// GoalService.shared.deleteGoal(id: id) { result in -// switch result{ -// case .success(let data): -// if data.success { -// print("목표 삭제 성공") -// self.selectedGoalIndex = 0 -// self.requestGetGoals() -// GoalObserver.shared.deleteGoal.onNext(Void()) -// } -// break -// default: -// print(result) -// break -// } -// } -// } -// // MARK: 목표에 해당하는 기록 조회 API -// private func getRecordsOfGoal(id: Int) { -// let pageModel = PageableModel(page: self.recordPage ?? 0) -// RecordService.shared.getRecordsOfGoalAtRecordTab(id: id, pageable: pageModel) { result in -// switch result{ -// case .success(let data): -//// print("LOG: 씀씀이 조회", data) -// // Paging 때문에 append하는 방식으로 작업 -// for recordData in data.content { -// self.recordsOfGoal.append(recordData) -// } -// self.getNoSecondEmotionRecords(id: id) -// -// break -// default: -// print(result) -// NetworkAlert.show(in: self){ [weak self] in -// self?.getRecordsOfGoal(id: id) -// } -// break -// } -// -// } -// } -// // MARK: 기록 삭제 API -// private func deleteRecord(id: Int) { -// RecordService.shared.deleteRecord(id: id) { result in -// print("기록 삭제 성공") -// self.requestGetGoals() -// } -// } -// // MARK: 일주일이 지났고, 두 번째 감정이 없는 기록 조회 API -// private func getNoSecondEmotionRecords(id: Int) { -// RecordService.shared.getNoSecondEmotionRecords(id: id) { result in -// switch result{ -// case .success(let data): -//// print("LOG: 일주일이 지났고, 두 번째 감정이 없는 기록 조회", data) -// -// self.noSecondEmotionRecords = data.content -// self.recordView.recordTableView.reloadData() -// -// break -// default: -// print(result) -// break -// } -// } -// } -//} diff --git a/POME/Presentation/ViewControllers/Record/Register/ModifyRecordViewController.swift b/POME/Presentation/ViewControllers/Record/Register/ModifyRecordViewController.swift index dccca61..df46aaa 100644 --- a/POME/Presentation/ViewControllers/Record/Register/ModifyRecordViewController.swift +++ b/POME/Presentation/ViewControllers/Record/Register/ModifyRecordViewController.swift @@ -12,11 +12,11 @@ final class ModifyRecordViewController: Recordable{ var completion: ((RecordResponseModel) -> Void)? //ViewModel 활용안하는 VC에게 데이터 전달 용도 private let record: RecordResponseModel - private var modifyViewModel: (any ReviewViewModelInterface)? + private var modifyViewModel: (any GoalWithRecordViewModelInterface)? private var index: Int! private var tableViewIndexPath: IndexPath! - init(modifyViewModel: any ReviewViewModelInterface, index: Int, goal: GoalResponseModel){ + init(modifyViewModel: any GoalWithRecordViewModelInterface, index: Int, goal: GoalResponseModel){ self.modifyViewModel = modifyViewModel self.index = index self.record = modifyViewModel.records[index] diff --git a/POME/Presentation/ViewModel/Record/RecordTabViewModel.swift b/POME/Presentation/ViewModel/Record/RecordTabViewModel.swift index e335340..2076b7f 100644 --- a/POME/Presentation/ViewModel/Record/RecordTabViewModel.swift +++ b/POME/Presentation/ViewModel/Record/RecordTabViewModel.swift @@ -17,137 +17,50 @@ import RxCocoa 일주일이 지났고 두번째 감정이 필요한 기록 조회 */ -protocol NoSecondEmotionViewModelInterface { +protocol NoSecondEmotionRecordViewModelInterface { var noSecondEmotionRecords: Int { get } } -protocol RecordTabViewModelInterface: BaseViewModel, ModifyRecordInterface { - var goals: [GoalResponseModel] { get } - var records: [RecordResponseModel] { get } -} +//protocol RecordTabViewModelInterface: BaseViewModel, ModifyRecordInterface { +// var goals: [GoalResponseModel] { get } +// var records: [RecordResponseModel] { get } +//} -final class RecordTabViewModel: ReviewViewModelInterface, NoSecondEmotionViewModelInterface { - - var goals = [GoalResponseModel]() - var records = [RecordResponseModel]() - var noSecondEmotionRecords: Int = 0 - - private var page: Int = 0 - private var selectedGoalIndex: Int = 0 - var hasNextPage: Bool = false +final class RecordTabViewModel: GoalWithRecordViewModel, NoSecondEmotionViewModelInterface { - let changeGoalSelect = PublishSubject() - let reloadTableView = PublishRelay() - let deleteGoalSubject = PublishSubject() - let deleteRecordSubject = PublishSubject() - let modifyRecordSubject = PublishSubject() - - private let getGoalsUseCase: GetGoalUseCaseInterface + internal var noSecondEmotionRecords: Int = 0 + private let deleteGoalUseCase: DeleteGoalUseCaseInterface - private let getRecordsOfGoalInRecordTabUseCase: GetRecordsOfGoalInRecordTabUseCaseInterface - private let deleteRecordUseCase: DeleteRecordUseCaseInterface + private let getRecordsUseCase: GetRecordsOfGoalInRecordTabUseCaseInterface private let getNoSecondEmotionRecordsUseCase: GetNoSecondEmotionRecordsUseCaseInterface + - - private let disposeBag = DisposeBag() - - struct Input{ - let selectedGoalIndex: Observable - } - - struct Output{ - - } - - init(getGoalsUseCase: GetGoalUseCaseInterface = GetGoalUseCase(), - deleteGoalUseCase: DeleteGoalUseCaseInterface = DeleteGoalUseCase(), + init(deleteGoalUseCase: DeleteGoalUseCaseInterface = DeleteGoalUseCase(), getRecordsOfGoalInRecordTabUseCase: GetRecordsOfGoalInRecordTabUseCaseInterface = GetRecordsOfGoalInRecordTabUseCase(), - deleteRecordUseCase: DeleteRecordUseCaseInterface = DeleteRecordUseCase(), getNoSecondEmotionRecordsUseCase: GetNoSecondEmotionRecordsUseCaseInterface = GetNoSecondEmotionRecordsUseCase()) { - self.getGoalsUseCase = getGoalsUseCase self.deleteGoalUseCase = deleteGoalUseCase - self.getRecordsOfGoalInRecordTabUseCase = getRecordsOfGoalInRecordTabUseCase - self.deleteRecordUseCase = deleteRecordUseCase + self.getRecordsUseCase = getRecordsOfGoalInRecordTabUseCase self.getNoSecondEmotionRecordsUseCase = getNoSecondEmotionRecordsUseCase } - @discardableResult - func transform(_ input: Input) -> Output{ - -// let getGoalsResponse = getGoalsUseCase.execute() - - input.selectedGoalIndex - .subscribe{ [weak self] in - self?.selectedGoalIndex = $0 - self?.initializeStateAndRequestRecord() - }.disposed(by: disposeBag) - - - return Output() - } - func deleteGoal() { let deleteGoalResponse = deleteGoalUseCase.execute(id: self.goals[selectedGoalIndex].id) .subscribe{ [weak self] in if $0 == .success { self?.goals.remove(at: self?.selectedGoalIndex ?? 0) - self?.deleteGoalSubject.onNext(Void()) self?.changeGoalSelect.onNext(Void()) } } } - func deleteRecord(index: Int) { - deleteRecordUseCase.execute(requestValue: DeleteRecordRequestModel(recordId: records[index].id)) - .subscribe{ [weak self] in - if $0 == .success { - self?.records.remove(at: index) - self?.deleteRecordSubject.onNext(index) - } - }.disposed(by: disposeBag) - } - - func modifyRecord(_ record: RecordResponseModel, index: Int){ - records[index] = record - modifyRecordSubject.onNext(index) - } - -} - -extension RecordTabViewModel { - func refreshData(){ - getGoalsUseCase.execute() - .subscribe(onNext: { [weak self] goals in - self?.goals = goals.filter{ !$0.isEnd } - self?.initializeStateAndRequestRecord() - }).disposed(by: disposeBag) - } - private func initializeStateAndRequestRecord(){ - hasNextPage = false - page = 0 - canRequestRecord() - } - - private func canRequestRecord(){ - //goal이 존재할 때만 기록 조회 요청 - if goals.isEmpty { - records = [] - reloadTableView.accept(Void()) - } else if selectedGoalIndex >= goals.count { //현재 선택 중인 목표가 삭제되었을 경우, 목표 변경 VC으로 전달 - changeGoalSelect.onNext(Void()) - } else { - requestRecords() - requestNoSecondEmotionRecords() - } - } - - private func requestRecords(){ - let recordResponse = getRecordsOfGoalInRecordTabUseCase + override func requestRecords(){ + requestNoSecondEmotionRecords() + + let recordResponse = getRecordsUseCase .execute(id: goals[self.selectedGoalIndex].id, pageable: PageableModel(page: page)) .share() - - + recordResponse .do(onNext: { self.hasNextPage = !$0.last @@ -168,5 +81,5 @@ extension RecordTabViewModel { self?.noSecondEmotionRecords = $0.content.count }.disposed(by: disposeBag) } - + } diff --git a/POME/Presentation/ViewModel/Review/ReviewViewModel.swift b/POME/Presentation/ViewModel/Review/ReviewViewModel.swift index a9b4e66..591c91d 100644 --- a/POME/Presentation/ViewModel/Review/ReviewViewModel.swift +++ b/POME/Presentation/ViewModel/Review/ReviewViewModel.swift @@ -9,54 +9,28 @@ import Foundation import RxSwift import RxCocoa -protocol ModifyRecordInterface { - var modifyRecordSubject: PublishSubject { get } - func modifyRecord(_ record: RecordResponseModel, index: Int) -} - -protocol DeleteRecord{ - var deleteRecordSubject: PublishSubject { get } - func deleteRecord(index: Int) -} +/* + 목표 리스트 조회 + 목표에 해당하는 기록 조회 + 기록 삭제 + 감정 필터링 + */ -protocol PageableInterface{ - var hasNextPage: Bool { get } -} -protocol ReviewViewModelInterface: BaseViewModel, ModifyRecordInterface{ - var goals: [GoalResponseModel] { get } - var records: [RecordResponseModel] { get } -} +//protocol ReviewViewModelInterface: BaseViewModel, ModifyRecordInterface{ +// var goals: [GoalResponseModel] { get } +// var records: [RecordResponseModel] { get } +//} -class ReviewViewModel: ReviewViewModelInterface, DeleteRecord{ +final class ReviewViewModel: GoalWithRecordViewModel { - private let getGoalsUseCase: GetGoalUseCaseInterface private let getRecordsUseCase: GetRecordInReviewUseCaseInterface - private let deleteRecordUseCase: DeleteRecordUseCaseInterface + private var emotionFilter: Review.EmotionFiltering = (nil, nil) - init(getGoalsUseCase: GetGoalUseCaseInterface = GetGoalUseCase(), - getRecordsUseCase: GetRecordInReviewUseCaseInterface = GetRecordInReviewUseCase(), - deleteRecordUseCase: DeleteRecordUseCaseInterface = DeleteRecordUseCase()){ - self.getGoalsUseCase = getGoalsUseCase + init(getRecordsUseCase: GetRecordInReviewUseCaseInterface = GetRecordInReviewUseCase()){ self.getRecordsUseCase = getRecordsUseCase - self.deleteRecordUseCase = deleteRecordUseCase } - var hasNextPage: Bool = false - var goals = [GoalResponseModel]() - var records = [RecordResponseModel]() - - let changeGoalSelect = PublishSubject() - let reloadTableView = PublishRelay() - let deleteRecordSubject = PublishSubject() - let modifyRecordSubject = PublishSubject() - - private var page: Int = 0 - private var selectedGoalIndex: Int = 0 - private var emotionFilter: Review.EmotionFiltering = (nil, nil) - - private let disposeBag = DisposeBag() - struct Input{ let selectedGoalIndex: Observable let filteringEmotion: Observable @@ -83,38 +57,8 @@ class ReviewViewModel: ReviewViewModelInterface, DeleteRecord{ return Output() } -} - -extension ReviewViewModel{ - func refreshData(){ - getGoalsUseCase.execute() - .subscribe(onNext: { [weak self] goals in - self?.goals = goals.filter{ !$0.isEnd } - self?.initializeStateAndRequestRecord() - }).disposed(by: disposeBag) - } - - private func initializeStateAndRequestRecord(){ - hasNextPage = false - page = 0 - canRequestRecord() - } - - private func canRequestRecord(){ - //goal이 존재할 때만 기록 조회 요청 - if goals.isEmpty { - records = [] - reloadTableView.accept(Void()) - } else if selectedGoalIndex >= goals.count { //현재 선택 중인 목표가 삭제되었을 경우, 목표 변경 VC으로 전달 - changeGoalSelect.onNext(Void()) - } else { - requestRecords() - } - } - - private func requestRecords(){ - + override func requestRecords(){ let recordResponse = getRecordsUseCase .execute( goalId: goals[self.selectedGoalIndex].id, @@ -142,19 +86,4 @@ extension ReviewViewModel{ page += 1 requestRecords() } - - func deleteRecord(index: Int) { - deleteRecordUseCase.execute(requestValue: DeleteRecordRequestModel(recordId: records[index].id)) - .subscribe{ [weak self] in - if $0 == .success { - self?.records.remove(at: index) - self?.deleteRecordSubject.onNext(index) - } - }.disposed(by: disposeBag) - } - - func modifyRecord(_ record: RecordResponseModel, index: Int){ - records[index] = record - modifyRecordSubject.onNext(index) - } }