From 527c76aec7e372a489da8b5bb2f0e5ee6d42f7d4 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:22:40 +0900 Subject: [PATCH 01/42] =?UTF-8?q?[Add]=20#197=20-=20=EC=BA=98=EB=A6=B0?= =?UTF-8?q?=EB=8D=94=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/Calendar/CalendarView.swift | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift index 418aef97..6645ba41 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift @@ -140,6 +140,8 @@ extension CalendarView { } } +// MARK: - Action + extension CalendarView { @objc @@ -151,10 +153,36 @@ extension CalendarView { func nextBtnTapped(_sender: UIButton) { scrollCurrentPage(calendar: calendar, isPrev: false) } +} + +extension CalendarView { func setCalendarSelectedDate(_ dates: [Date]) { dates.forEach { calendar.select($0) } } + + func configure(delegate: FSCalendarDelegate, datasource: FSCalendarDataSource) { + calendar.delegate = delegate + calendar.dataSource = datasource + } + + func configureYearMonth(to text: String) { + yearMonthLabel.text = text + } + + func reloadCollectionView() { + calendar.collectionView.reloadData() + } + + func updateDetailConstraints() { + horizonStackView.snp.updateConstraints { + $0.top.equalToSuperview().offset(54) + } + calendar.snp.updateConstraints { + $0.bottom.equalToSuperview().inset(45) + } + } + } From ae2fd5d5a39c18b80c453d3ef5697cd1fa1a5e7b Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:23:29 +0900 Subject: [PATCH 02/42] =?UTF-8?q?[Chore]=20#197=20-=20=EB=94=94=ED=85=8C?= =?UTF-8?q?=EC=9D=BC=20header=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...sableView.swift => DetailHeaderView.swift} | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) rename iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/{DetailHeaderReusableView.swift => DetailHeaderView.swift} (70%) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailHeaderReusableView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailHeaderView.swift similarity index 70% rename from iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailHeaderReusableView.swift rename to iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailHeaderView.swift index b918b378..666b681a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailHeaderReusableView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailHeaderView.swift @@ -10,24 +10,23 @@ import UIKit import SnapKit import Then -final class DetailHeaderReusableView: UICollectionReusableView { +final class DetailHeaderView: UIView { // MARK: - Properties - static let identifier = "DetailHeaderReusableView" var cancelClosure: (() -> Void)? var editClosure: (() -> Void)? // MARK: - UI Components - - private let horizontalStackview = UIStackView() - private let emptyView = UIView() + private let cancelButton = UIButton() private let editButton = UIButton() // MARK: - Life Cycle + override init(frame: CGRect) { super.init(frame: .zero) + setUI() setLayout() } @@ -40,41 +39,36 @@ final class DetailHeaderReusableView: UICollectionReusableView { // MARK: - Method -extension DetailHeaderReusableView { +extension DetailHeaderView { + private func setUI() { cancelButton.do { $0.backgroundColor = .clear $0.setImage(.icCancel, for: .normal) $0.addTarget(self, action: #selector(cancelButtonTapped), for: .touchUpInside) } + editButton.do { $0.setTitle(I18N.detailEdit, for: .normal) $0.setTitleColor(.gray4, for: .normal) $0.titleLabel?.font = .Pretendard(.medium, size: 16) $0.addTarget(self, action: #selector(editButtonTapped), for: .touchUpInside) } - horizontalStackview.do { - $0.addArrangedSubviews(cancelButton, emptyView, editButton) - $0.axis = .horizontal - } } private func setLayout() { - addSubview(horizontalStackview) + addSubviews(cancelButton, editButton) cancelButton.snp.makeConstraints { $0.centerY.equalToSuperview() $0.size.equalTo(CGSize(width: 24, height: 24)) - $0.leading.equalToSuperview().offset(7) + $0.leading.equalToSuperview().inset(24) } + editButton.snp.makeConstraints { $0.centerY.equalToSuperview() $0.size.equalTo(CGSize(width: 44, height: 35)) - } - horizontalStackview.snp.makeConstraints { - $0.directionalHorizontalEdges.equalToSuperview().inset(17) - $0.top.equalToSuperview().offset(-5) - $0.height.equalTo(35) + $0.trailing.equalToSuperview().inset(17) } } From 0d0de0df13cf113703723ce696de7f1f77e31b48 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:23:48 +0900 Subject: [PATCH 03/42] =?UTF-8?q?[Chore]=20#197=20-=20=EB=94=94=ED=85=8C?= =?UTF-8?q?=EC=9D=BC=20footer=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Home/Cells/DetailFooterReusableView.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailFooterReusableView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailFooterReusableView.swift index 951fd1b5..ac560136 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailFooterReusableView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailFooterReusableView.swift @@ -10,11 +10,12 @@ import UIKit import SnapKit import Then -final class DetailFooterReusableView: UICollectionReusableView { +final class DetailFooterView: UICollectionReusableView { // MARK: - Properties static let identifier = "DetailFooterReusableView" + var footerClosure: (() -> Void)? // MARK: - UI Components @@ -23,6 +24,7 @@ final class DetailFooterReusableView: UICollectionReusableView { private let dateButton = UIButton(configuration: .plain()) // MARK: - Life Cycle + override init(frame: CGRect) { super.init(frame: .zero) setUI() @@ -37,7 +39,7 @@ final class DetailFooterReusableView: UICollectionReusableView { // MARK: - Method -extension DetailFooterReusableView { +extension DetailFooterView { private func setUI() { dateLabel.do { $0.text = I18N.detailDate From 39e077d3460f0e2ba603feab40223aeca40ce132 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:24:21 +0900 Subject: [PATCH 04/42] =?UTF-8?q?[Fix]=20#197=20-=20=EB=94=94=ED=85=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=BA=98=EB=A6=B0=EB=8D=94=20UI=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DetailCalendarViewController.swift | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift index 039783d2..d21f78dd 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift @@ -22,10 +22,11 @@ final class DetailCalendarViewController: UIViewController { } var invalidDate: [String] = [] var dataSource: [String: Float] = [:] - var detailModel: [MissionDetailResponseDTO] = [] + var detailModel: MissionDetailResponseDTO? var userId: Int? var count: Int? var movedateClosure: ((_ date: String) -> Void)? + private lazy var safeArea = self.view.safeAreaLayoutGuide private lazy var today: Date = { return Date() }() @@ -40,6 +41,7 @@ final class DetailCalendarViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.SelectDate.appearAnotherDayModal) + if let id = self.userId { requestParticualrDatesAPI(id: id) } @@ -47,6 +49,7 @@ final class DetailCalendarViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + setUI() setLayout() } @@ -73,12 +76,14 @@ extension DetailCalendarViewController { $0.setTitle(I18N.detailComplete, for: .normal) $0.titleLabel?.font = .Pretendard(.medium, size: 16) $0.addTarget(self, action: #selector(completeBtnTapped(sender:)), for: .touchUpInside) + } + monthCalendar.do { $0.layer.cornerRadius = 12 - $0.calendar.delegate = self - $0.calendar.dataSource = self + $0.configure(delegate: self, datasource: self) } + subLabel.do { $0.text = I18N.subText $0.font = .Pretendard(.regular, size: 12) @@ -109,19 +114,15 @@ extension DetailCalendarViewController { $0.trailing.equalToSuperview().inset(16) $0.size.equalTo(CGSize(width: 44, height: 35)) } - - monthCalendar.horizonStackView.snp.updateConstraints { - $0.top.equalToSuperview().offset(54) - } - + monthCalendar.snp.makeConstraints { $0.centerY.equalTo(safeArea) $0.directionalHorizontalEdges.equalTo(safeArea).inset(15) $0.height.equalTo((getDeviceWidth()-30)*1.2) } - monthCalendar.calendar.snp.updateConstraints { - $0.bottom.equalToSuperview().inset(45) - } + + monthCalendar.updateDetailConstraints() + subLabel.snp.makeConstraints { $0.bottom.equalToSuperview().inset(25) $0.left.equalToSuperview().offset(17) @@ -130,11 +131,17 @@ extension DetailCalendarViewController { } extension DetailCalendarViewController { + @objc func completeBtnTapped(sender: UIButton) { + guard let data = self.detailModel else { return } if let id = self.userId { requestAddAnotherDay(id: id, dates: anotherDate) - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.SelectDate.completeAddMissionAnotherDay(title: self.detailModel[0].title, situation: self.detailModel[0].situation, goal: self.detailModel[0].goal, action: [self.detailModel[0].actions[0].name], date: anotherDate)) + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.SelectDate.completeAddMissionAnotherDay(title: data.title, + situation: data.situation, + goal: data.goal, + action: data.actions.map { $0.name } + , date: anotherDate)) } } } @@ -142,7 +149,8 @@ extension DetailCalendarViewController { extension DetailCalendarViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { func calendarCurrentPageDidChange(_ calendar: FSCalendar) { - monthCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage) + monthCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, + date: calendar.currentPage)) guard let id = self.userId else { return } requestParticualrDatesAPI(id: id) } @@ -218,7 +226,7 @@ extension DetailCalendarViewController { HomeAPI.shared.particularMissionDates(id: id) { [weak self] response in guard let dates = response.data else { return } self?.invalidDate = dates - self?.monthCalendar.calendar.reloadData() + self?.monthCalendar.reloadCollectionView() } } } From 1f0b3cf98529e7341038708e1b66038d55b9de00 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:24:54 +0900 Subject: [PATCH 05/42] =?UTF-8?q?[Fix]=20#197=20-=20=EB=AF=B8=EC=85=98=20?= =?UTF-8?q?=EB=94=94=ED=85=8C=EC=9D=BC=20cell=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/Cells/MissionDetailCollectionViewCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift index 495725c4..2b187d29 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift @@ -92,7 +92,7 @@ extension MissionDetailCollectionViewCell { accumulateView.addSubviews(accumulateSubView, accumulateLabel) missionTagLabel.snp.makeConstraints { - $0.top.equalToSuperview().offset(30) + $0.top.equalToSuperview() $0.leading.equalToSuperview().offset(29) } From a81c10ba1f645e9ad26b72d757edef19c0caf052 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:25:15 +0900 Subject: [PATCH 06/42] =?UTF-8?q?[Refactor]=20#197=20-=20=EB=AF=B8?= =?UTF-8?q?=EC=85=98=20=EB=94=94=ED=85=8C=EC=9D=BC=20VC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MissionDetailViewController.swift | 259 ++++++++++-------- 1 file changed, 150 insertions(+), 109 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift index 87f41253..87d6de30 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift @@ -10,158 +10,199 @@ import UIKit import Then import SnapKit +import UIKit + +import Then +import SnapKit + final class MissionDetailViewController: UIViewController { // MARK: - Properties - var deleteClosure: (() -> Void)? - var moveDateClosure: ((_ date: String) -> Void)? + typealias CellRegistration = UICollectionView.CellRegistration + typealias FooterRegistration = UICollectionView.SupplementaryRegistration + typealias Item = MissionDetailResponseDTO + typealias DataSource = UICollectionViewDiffableDataSource + typealias SnapShot = NSDiffableDataSourceSnapshot - private lazy var safeArea = self.view.safeAreaLayoutGuide enum Section { - case mission + case main } - typealias Item = AnyHashable - var dataSource: UICollectionViewDiffableDataSource! = nil - var detailModel: [MissionDetailResponseDTO] = [] + + private var dataSource: DataSource? + private var detailModel: MissionDetailResponseDTO? var userId: Int? + var deleteClosure: (() -> Void)? + var moveDateClosure: ((_ date: String) -> Void)? + + private lazy var safeArea = self.view.safeAreaLayoutGuide + // MARK: - UI Components + private let headerView = DetailHeaderView() private let deleteButton = UIButton(configuration: .filled()) - private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) - private let completeButton = UIButton() + private var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) // MARK: - Life Cycle override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + guard let id = self.userId else { return } requestDailyMissionAPI(id: id) } override func viewDidLoad() { super.viewDidLoad() - register() + setUI() setLayout() setupDataSource() - reloadData() + setSnapShot() } } // MARK: - Methods extension MissionDetailViewController { - private func register() { - collectionView.register(MissionDetailCollectionViewCell.self, forCellWithReuseIdentifier: MissionDetailCollectionViewCell.identifier) - collectionView.register(DetailHeaderReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: DetailHeaderReusableView.identifier) - collectionView.register(DetailFooterReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: DetailFooterReusableView.identifier) - } + private func setUI() { + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.25) { UIView.animate(withDuration: 0.5) { self.view.backgroundColor = .black.withAlphaComponent(0.6) } } - deleteButton.do { - $0.configuration?.title = I18N.detailDelete - $0.configuration?.cornerStyle = .capsule - $0.configuration?.attributedTitle?.font = .Pretendard(.semiBold, size: 16) - $0.configuration?.baseBackgroundColor = .black - $0.configuration?.baseForegroundColor = .white - $0.addTarget(self, action: #selector(deleteBtnTapped), for: .touchUpInside) - } - collectionView.do { + headerView.do { $0.backgroundColor = .white $0.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] $0.layer.cornerRadius = 10 + } + + collectionView.do { + $0.collectionViewLayout = layout() $0.bounces = false - $0.isScrollEnabled = false + $0.backgroundColor = .white + $0.showsVerticalScrollIndicator = false + $0.contentInset = .init(top: -30, left: 0, bottom: 88, right: 0) } + + deleteButton.do { + + var config = UIButton.Configuration.filled() + config.title = I18N.detailDelete + config.cornerStyle = .capsule + config.baseBackgroundColor = .black + config.baseForegroundColor = .white + config.attributedTitle?.font = .Pretendard(.semiBold, size: 16) + + $0.configuration = config + $0.addTarget(self, action: #selector(deleteBtnTapped), for: .touchUpInside) + } + + configureHeaderView() } private func setLayout() { - view.addSubviews(collectionView, deleteButton) + view.addSubviews(headerView, collectionView, deleteButton) - collectionView.snp.makeConstraints { - $0.height.equalTo(getDeviceHeight()*0.88) - $0.directionalHorizontalEdges.equalTo(safeArea) - $0.bottom.equalToSuperview() + headerView.snp.makeConstraints { + $0.top.equalTo(safeArea).inset(58) + $0.horizontalEdges.equalTo(safeArea) + $0.height.equalTo(74) } + deleteButton.snp.makeConstraints { - $0.directionalHorizontalEdges.equalToSuperview().inset(15) + $0.horizontalEdges.equalToSuperview().inset(15) $0.height.equalTo(50) $0.bottom.equalTo(safeArea).inset(10) } + + collectionView.snp.makeConstraints { + $0.top.equalTo(headerView.snp.bottom) + $0.horizontalEdges.bottom.equalTo(safeArea) + } } // MARK: - Data private func setupDataSource() { - dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MissionDetailCollectionViewCell.identifier, for: indexPath) as? MissionDetailCollectionViewCell else {return UICollectionViewCell()} - cell.configure(model: item as! MissionDetailResponseDTO) - return cell + + let cellRegistration = CellRegistration {cell, _, item in + cell.configure(model: item) + } + + let footerRegistration = FooterRegistration(elementKind: UICollectionView.elementKindSectionFooter) { footerView, _, _ in + footerView.footerClosure = { + let modalViewController = DetailCalendarViewController() + modalViewController.detailModel = self.detailModel + modalViewController.modalPresentationStyle = .overFullScreen + modalViewController.modalTransitionStyle = .crossDissolve + guard let id = self.userId else {return} + modalViewController.userId = id + modalViewController.movedateClosure = { [weak self] date in + self?.dismiss(animated: true) + self?.moveDateClosure?(date) + } + self.present(modalViewController, animated: false) + } + } + + dataSource = DataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in + return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, + for: indexPath, + item: item) }) + + dataSource?.supplementaryViewProvider = { collectionView, _, indexPath in + return collectionView.dequeueConfiguredReusableSupplementary(using: footerRegistration, + for: indexPath) + } } - private func reloadData() { - var snapShot = NSDiffableDataSourceSnapshot() - defer { - dataSource.apply(snapShot, animatingDifferences: false) + private func configureHeaderView() { + headerView.cancelClosure = { + self.view.alpha = 0 + self.dismiss(animated: true) { + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.closeDetailMission) + } } - snapShot.appendSections([.mission]) - snapShot.appendItems(detailModel, toSection: .mission) - dataSource.supplementaryViewProvider = { (collectionView, kind, indexPath) in - if kind == UICollectionView.elementKindSectionHeader { - guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: DetailHeaderReusableView.identifier, for: indexPath) as? DetailHeaderReusableView else {return UICollectionReusableView()} - header.cancelClosure = { - - self.view.alpha = 0 - self.dismiss(animated: true) - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.closeDetailMission) - } - header.editClosure = { - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickEditMission(section: "detail")) - - let updateMissionViewController = AddMissionViewController() - guard let rootViewController = self.presentingViewController as? UINavigationController else { return } - updateMissionViewController.setMissionId(self.userId ?? 0) - updateMissionViewController.setViewType(.update) - self.dismiss(animated: true) { - rootViewController.pushViewController(updateMissionViewController, animated: true) - } - } - return header - } else { - guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: DetailFooterReusableView.identifier, for: indexPath) as? DetailFooterReusableView else { return UICollectionReusableView() } - footer.footerClosure = { - let modalViewController = DetailCalendarViewController() - modalViewController.detailModel = self.detailModel - modalViewController.modalPresentationStyle = .overFullScreen - modalViewController.modalTransitionStyle = .crossDissolve - guard let id = self.userId else {return} - modalViewController.userId = id - modalViewController.movedateClosure = { [weak self] date in - self?.dismiss(animated: true) - self?.moveDateClosure?(date) - } - self.present(modalViewController, animated: false) - } - return footer + headerView.editClosure = { + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickEditMission(section: "detail")) + + let updateMissionViewController = AddMissionViewController() + guard let rootViewController = self.presentingViewController as? UINavigationController else { return } + updateMissionViewController.setMissionId(self.userId ?? 0) + updateMissionViewController.setViewType(.update) + self.dismiss(animated: true) { + rootViewController.pushViewController(updateMissionViewController, animated: true) } } } + private func setSnapShot() { + + var snapShot = SnapShot() + + if let detailModel = self.detailModel { + snapShot.appendSections([.main]) + snapShot.appendItems([detailModel], toSection: .main) + } + + dataSource?.applySnapshotUsingReloadData(snapShot) + } + private func layout() -> UICollectionViewCompositionalLayout { - var config = UICollectionLayoutListConfiguration(appearance: .plain) + var config = UICollectionLayoutListConfiguration(appearance: .grouped) + config.backgroundColor = .white config.showsSeparators = false - config.headerMode = .supplementary config.footerMode = .supplementary + return UICollectionViewCompositionalLayout.list(using: config) + } } @@ -169,53 +210,53 @@ extension MissionDetailViewController { @objc func deleteBtnTapped() { - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "detail", title: self.detailModel[0].title, situation: self.detailModel[0].situation, goal: self.detailModel[0].goal, action: [self.detailModel[0].actions[0].name])) + + guard let data = detailModel else { return } + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "detail", title: data.title, situation: data.situation, goal: data.goal, action: [data.actions[0].name])) + let modalViewController = HomeDeleteViewController() modalViewController.modalPresentationStyle = .overFullScreen modalViewController.modalTransitionStyle = .crossDissolve modalViewController.deleteClosure = { guard let id = self.userId else { return } - self.requestDeleteMission(id: id) } + present(modalViewController, animated: false) } } extension MissionDetailViewController { + func requestDailyMissionAPI(id: Int) { - HomeAPI.shared.getDailyDetailMission(id: id) { [weak self] result in - switch result { - case let .success(data): - if let missionData = data as? MissionDetailResponseDTO { - print("data: \(missionData)") - self?.detailModel = [missionData] - self?.reloadData() - } else { - print("Failed to cast data to MissionDetailResponseDTO") - } - case .pathErr: - print("pathErr") - case .serverErr: - print("serverErr") - case .networkFail: - print("networkFail") - case .requestErr: - print("networkFail") - } + HomeAPI.shared.getDailyDetailMission(id: id) { [weak self] response in + guard let self else { return } + guard let response = response else { return } + guard let data = response.data else { return } + + self.detailModel = data + self.setSnapShot() } } private func requestDeleteMission(id: Int) { HomeAPI.shared.deleteMission(id: id) { [weak self] _ in - for index in 0..<(self?.detailModel.count ?? 0) { - if self?.detailModel[index].id == id { - self?.deleteClosure?() - self?.reloadData() - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.completeDeleteMission(section: "detail", title: (self?.detailModel[index].title)!, situation: (self?.detailModel[index].situation)!, goal: self?.detailModel[index].goal ?? "", action: [self?.detailModel[index].actions[index].name ?? ""])) - self?.dismiss(animated: true) + guard let self else { return } + guard let data = self.detailModel else { return } + if data.id == id { + self.deleteClosure?() + self.setSnapShot() + + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.completeDeleteMission(section: "detail", + title: data.title, + situation: data.situation, + goal: data.goal, + action: data.actions.map { $0.name })) + + self.dismiss(animated: true) { + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.closeDetailMission) - } else {} + } } } } From e13758df457720f6c9038ec43cfc6a20688e6f4c Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:25:31 +0900 Subject: [PATCH 07/42] =?UTF-8?q?[Fix]=20#197=20-=20=EB=84=A4=ED=8A=B8?= =?UTF-8?q?=EC=9B=8C=ED=81=AC=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO.xcodeproj/project.pbxproj | 8 ++--- .../Network/API/Home/HomeAPI.swift | 24 ++++++++------- .../AddMissionViewController.swift | 30 ++++++------------- 3 files changed, 27 insertions(+), 35 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 92673262..a1e037d8 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -25,7 +25,7 @@ 0943A9F92A53239200614761 /* Bundle+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0943A9F82A53239200614761 /* Bundle+.swift */; }; 09582B4829BDA7F600EF3207 /* DetailStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B4729BDA7F600EF3207 /* DetailStackView.swift */; }; 09582B4B29BDE37C00EF3207 /* DetailFooterReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B4A29BDE37C00EF3207 /* DetailFooterReusableView.swift */; }; - 09582B4D29BE277800EF3207 /* DetailHeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B4C29BE277800EF3207 /* DetailHeaderReusableView.swift */; }; + 09582B4D29BE277800EF3207 /* DetailHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B4C29BE277800EF3207 /* DetailHeaderView.swift */; }; 09582B4F29BEBAFA00EF3207 /* DetailCalendarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B4E29BEBAFA00EF3207 /* DetailCalendarViewController.swift */; }; 09582B5129C0BC3600EF3207 /* DetailAchievementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */; }; 0960C0D42A38BC6500A3D8DB /* KeychainUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960C0D32A38BC6500A3D8DB /* KeychainUtil.swift */; }; @@ -183,7 +183,7 @@ 0943A9F82A53239200614761 /* Bundle+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+.swift"; sourceTree = ""; }; 09582B4729BDA7F600EF3207 /* DetailStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailStackView.swift; sourceTree = ""; }; 09582B4A29BDE37C00EF3207 /* DetailFooterReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailFooterReusableView.swift; sourceTree = ""; }; - 09582B4C29BE277800EF3207 /* DetailHeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailHeaderReusableView.swift; sourceTree = ""; }; + 09582B4C29BE277800EF3207 /* DetailHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailHeaderView.swift; sourceTree = ""; }; 09582B4E29BEBAFA00EF3207 /* DetailCalendarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCalendarViewController.swift; sourceTree = ""; }; 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewController.swift; sourceTree = ""; }; 0960C0D32A38BC6500A3D8DB /* KeychainUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainUtil.swift; sourceTree = ""; }; @@ -465,7 +465,7 @@ 097C003529AB8270008CAEF3 /* MissionListCollectionViewCell.swift */, 092E04B029BD9C86008A5892 /* MissionDetailCollectionViewCell.swift */, 09582B4A29BDE37C00EF3207 /* DetailFooterReusableView.swift */, - 09582B4C29BE277800EF3207 /* DetailHeaderReusableView.swift */, + 09582B4C29BE277800EF3207 /* DetailHeaderView.swift */, ); path = Cells; sourceTree = ""; @@ -1327,7 +1327,7 @@ 3BBB6C8F2A1E7BEA00B8745A /* CollectionViewLeftAlignLayout.swift in Sources */, 09A1465F2A192C4900DDC308 /* WeekMissionResponseDTO.swift in Sources */, 0930D37329B4FCAE0000C4AE /* StatisticsView.swift in Sources */, - 09582B4D29BE277800EF3207 /* DetailHeaderReusableView.swift in Sources */, + 09582B4D29BE277800EF3207 /* DetailHeaderView.swift in Sources */, 6CF4706A29A71D71008D145C /* Strings.swift in Sources */, 6CF4705F29A69025008D145C /* GeneralResponse.swift in Sources */, 097C003629AB8270008CAEF3 /* MissionListCollectionViewCell.swift in Sources */, diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift index f753522b..28c56828 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift @@ -55,16 +55,20 @@ final class HomeAPI { } } - func getDailyDetailMission(id: Int, completion: @escaping (NetworkResult) -> Void) { - homeProvider.request(.dailyDetailMission(id: id)) { response in - switch response { - case let .success(response): - let statusCode = response.statusCode - let data = response.data - let networkResult = NetworkBase.judgeStatus(by: statusCode, data, MissionDetailResponseDTO.self) - completion(networkResult) - case let .failure(err): - print(err) + func getDailyDetailMission(id: Int, completion: @escaping (GeneralResponse?) -> Void) { + homeProvider.request(.dailyDetailMission(id: id)) { result in + switch result { + case .success(let response): + do { + self.missionDetailDailyData = try response.map(GeneralResponse?.self) + guard let missionDetailDailyData = self.missionDetailDailyData else { return } + completion(missionDetailDailyData) + } catch let err { + print(err.localizedDescription, 500) + } + case .failure(let err): + print(err.localizedDescription) + completion(nil) } } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift index cb89a443..574aecd6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift @@ -319,27 +319,15 @@ extension AddMissionViewController { } private func requestDailyMissionAPI(id: Int) { - HomeAPI.shared.getDailyDetailMission(id: id) { [weak self] result in - switch result { - case let .success(data): - if let missionData = data as? MissionDetailResponseDTO { - self?.nottodoInfoList[1] = missionData.title - self?.nottodoInfoList[2] = missionData.situation - self?.nottodoInfoList[3] = missionData.actions.first!.name - self?.nottodoInfoList[4] = missionData.goal - self?.addMissionCollectionView.reloadData() - } else { - print("Failed to cast data to MissionDetailResponseDTO") - } - case .pathErr: - print("pathErr") - case .serverErr: - print("serverErr") - case .networkFail: - print("networkFail") - case .requestErr: - print("networkFail") - } + HomeAPI.shared.getDailyDetailMission(id: id) { [weak self] response in + guard let self else { return } + guard let response = response else { return } + guard let data = response.data else { return } + self.nottodoInfoList[1] = data.title + self.nottodoInfoList[2] = data.situation + self.nottodoInfoList[3] = data.actions.first!.name + self.nottodoInfoList[4] = data.goal + self.addMissionCollectionView.reloadData() } } From 112a4c8a0c821c4b5641fa8974a03d789de5c9d4 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:31:36 +0900 Subject: [PATCH 08/42] =?UTF-8?q?[Fix]=20#199=20-=20=EC=BA=98=EB=A6=B0?= =?UTF-8?q?=EB=8D=94=20cell=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/MissionCalendarCell.swift | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift index 377c8ed1..962ca16b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift @@ -35,13 +35,21 @@ final class MissionCalendarCell: FSCalendarCell { static let identifier = "MissionCalendarCell" - var mode: FSCalendarScope = .week - var state: ToDoState = .none + var mode: FSCalendarScope = .week { + didSet { + setLayout() + } + } + var state: ToDoState = .none { + didSet { + updateUI(state: self.state) + } + } // MARK: - UI Components - let iconView = UIImageView() - private var padding = 8 + private let iconView = UIImageView() + private let padding = 8 // MARK: - Life Cycle @@ -71,14 +79,32 @@ extension MissionCalendarCell { } private func setLayout() { - iconView.snp.makeConstraints { - $0.height.width.equalTo(contentView.bounds.width - 8) - $0.centerX.equalToSuperview() - $0.bottom.equalToSuperview().inset(5) - } - subtitleLabel.snp.updateConstraints { - $0.centerY.equalTo(iconView.snp.centerY) + switch mode { + case .month: + iconView.snp.remakeConstraints { + $0.height.equalToSuperview() + $0.width.equalTo(contentView.bounds.size.height) + $0.center.equalToSuperview() + } + + titleLabel.snp.remakeConstraints { + $0.centerY.equalToSuperview().offset(-1) + $0.centerX.equalToSuperview() + } + case .week: + iconView.snp.remakeConstraints { + $0.height.width.equalTo(contentView.bounds.width - 8) + $0.centerX.equalToSuperview() + $0.bottom.equalToSuperview().inset(5) + } + subtitleLabel.snp.updateConstraints { + $0.centerY.equalTo(iconView.snp.centerY) + } + + @unknown default: + break } + } private func updateUI(state: ToDoState) { @@ -88,7 +114,5 @@ extension MissionCalendarCell { func configure(_ state: ToDoState, _ mode: FSCalendarScope) { self.mode = mode self.state = state - setLayout() - updateUI(state: state) } } From e526c2e84d01a4c8fc1b5c57bbb46af5422d9882 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:32:31 +0900 Subject: [PATCH 09/42] =?UTF-8?q?[Fix]=20#199=20-=20=EC=84=B1=EC=B7=A8=20V?= =?UTF-8?q?C=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AchievementViewController.swift | 61 ++++++++++--------- .../Common/Calendar/CalendarView.swift | 2 +- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index 234bf429..a799c2d3 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -15,15 +15,16 @@ final class AchievementViewController: UIViewController { // MARK: - Properties - private lazy var safeArea = self.view.safeAreaLayoutGuide - private var currentPage: Date? = Date() - var count: Int? - var dataSource: [String: Float] = [:] { + var selectDate: Date? + private var count: Int? + private var currentPage = Date() + private var dataSource: [String: Float] = [:] { didSet { self.monthCalendar.calendar.reloadData() } } - var selectDate: Date? + + private lazy var safeArea = self.view.safeAreaLayoutGuide // MARK: - UI Components @@ -39,14 +40,15 @@ final class AchievementViewController: UIViewController { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.viewAccomplish) if let today = monthCalendar.calendar.today { - monthCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: today) monthCalendar.calendar.currentPage = today + monthCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: today) requestMonthAPI(month: Utils.dateFormatterString(format: "yyyy-MM", date: today)) } } override func viewDidLoad() { super.viewDidLoad() + setUI() setLayout() } @@ -62,13 +64,18 @@ extension AchievementViewController { private func setUI() { view.backgroundColor = .ntdBlack - scrollView.backgroundColor = .clear + + scrollView.do { + $0.backgroundColor = .clear + $0.showsVerticalScrollIndicator = false + } achievementLabel.do { $0.text = I18N.achievement $0.font = .Pretendard(.semiBold, size: 18) $0.textColor = .white } + monthCalendar.do { $0.layer.cornerRadius = 12 $0.layer.borderWidth = 1 @@ -76,19 +83,22 @@ extension AchievementViewController { $0.calendar.register(MissionCalendarCell.self, forCellReuseIdentifier: MissionCalendarCell.identifier) $0.calendar.delegate = self $0.calendar.dataSource = self - $0.monthCalendarClosure = { [self] result in - let month = result + $0.monthCalendarClosure = { [weak self] month in + guard let self else { return } self.reloadMonthData(month: month) } } } + private func setLayout() { view.addSubview(scrollView) + scrollView.addSubviews(achievementLabel, monthCalendar, statisticsView) scrollView.snp.makeConstraints { $0.edges.equalTo(safeArea) } + achievementLabel.snp.makeConstraints { $0.top.equalToSuperview().offset(23) $0.leading.equalTo(safeArea).offset(21) @@ -107,11 +117,12 @@ extension AchievementViewController { $0.bottom.equalTo(scrollView.snp.bottom).inset(20) } } + func requestMonthAPI(month: String) { AchieveAPI.shared.getAchieveCalendar(month: month) { [weak self] result in + guard let self else { return } guard let result = result?.data else { return } - self?.dataSource = [:] - guard let currentPage = self?.currentPage else { return } + self.dataSource = [:] let currentMonth = Calendar.current.component(.month, from: currentPage) let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" @@ -124,19 +135,21 @@ extension AchievementViewController { } actionDate.forEach { - self?.dataSource[$0.actionDate] = $0.percentage - self?.count = self?.dataSource.count + self.dataSource[$0.actionDate] = $0.percentage + self.count = self.dataSource.count } - self?.monthCalendar.calendar.reloadData() + self.monthCalendar.calendar.collectionView.reloadData() } } } extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { + func calendarCurrentPageDidChange(_ calendar: FSCalendar) { self.currentPage = calendar.currentPage - monthCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage) + monthCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, + date: calendar.currentPage) reloadMonthData(month: Utils.dateFormatterString(format: "yyyy-MM", date: calendar.currentPage)) } @@ -160,6 +173,7 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool { Calendar.current.isDate(date, equalTo: calendar.currentPage, toGranularity: .month) } + func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleSelectionColorFor date: Date) -> UIColor? { guard let count = self.count else { return .white } @@ -175,7 +189,6 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F } func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? { - guard let currentPage = self.currentPage else { return .white } let currentMonth = Calendar.current.component(.month, from: currentPage) let dateMonth = Calendar.current.component(.month, from: date) @@ -197,22 +210,14 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell - cell.iconView.contentMode = .scaleAspectFit - cell.iconView.snp.remakeConstraints { - $0.edges.equalToSuperview() - $0.center.equalToSuperview() - } - cell.titleLabel.snp.remakeConstraints { - $0.centerY.equalToSuperview().offset(-1) - $0.centerX.equalToSuperview() - } + guard let count = self.count else { return cell } let dateString = Utils.dateFormatterString(format: nil, date: date) if let percentage = self.dataSource[dateString] { switch (count, percentage) { - case (_, 1.0): cell.configure(.rateFull, .week) - case (_, 0.0): cell.configure(.none, .week) - case (2, 0.5), (3, 0.0..<1.0), (_, _): cell.configure(.rateHalf, .week) + case (_, 1.0): cell.configure(.rateFull, .month) + case (_, 0.0): cell.configure(.none, .month) + case (2, 0.5), (3, 0.0..<1.0), (_, _): cell.configure(.rateHalf, .month) } } return cell diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift index 418aef97..853c047d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift @@ -15,7 +15,7 @@ final class CalendarView: UIView { // MARK: - UI Components - let yearMonthLabel = UILabel() + var yearMonthLabel = UILabel() let todayButton = UIButton(configuration: .filled()) let horizonStackView = UIStackView() let leftButton = UIButton() From 99a37f7fb251426f049ec0d6fab2ab5f554b4511 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:54:11 +0900 Subject: [PATCH 10/42] =?UTF-8?q?[Fix]=20#198=20-=20=EC=BA=98=EB=A6=B0?= =?UTF-8?q?=EB=8D=94=20UI=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AchievementViewController.swift | 4 +- .../Cells/DateCollectionViewCell.swift | 2 +- .../Common/Calendar/CalendarView.swift | 98 +++++++++++++++---- .../DetailCalendarViewController.swift | 14 +-- 4 files changed, 85 insertions(+), 33 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index 234bf429..5b10ab65 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -39,7 +39,7 @@ final class AchievementViewController: UIViewController { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.viewAccomplish) if let today = monthCalendar.calendar.today { - monthCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: today) + monthCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: today)) monthCalendar.calendar.currentPage = today requestMonthAPI(month: Utils.dateFormatterString(format: "yyyy-MM", date: today)) } @@ -136,7 +136,7 @@ extension AchievementViewController { extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { func calendarCurrentPageDidChange(_ calendar: FSCalendar) { self.currentPage = calendar.currentPage - monthCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage) + monthCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage)) reloadMonthData(month: Utils.dateFormatterString(format: "yyyy-MM", date: calendar.currentPage)) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift index 509f44c5..dab64b92 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift @@ -222,7 +222,7 @@ extension DateCollectionViewCell { extension DateCollectionViewCell: FSCalendarDelegate, FSCalendarDelegateAppearance { func calendarCurrentPageDidChange(_ calendar: FSCalendar) { - calendarView.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage) + calendarView.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage)) } func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift index 418aef97..341258e9 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift @@ -11,18 +11,27 @@ import FSCalendar import Then import SnapKit +protocol CalendarViewDelegate { + + func todayBtnTapped() +} + final class CalendarView: UIView { + // MARK: - Properties + + let today = Date() + var monthCalendarClosure: ((_ month: String) -> Void)? + var delegate: CalendarViewDelegate? + // MARK: - UI Components - let yearMonthLabel = UILabel() + private let yearMonthLabel = UILabel() let todayButton = UIButton(configuration: .filled()) - let horizonStackView = UIStackView() - let leftButton = UIButton() - let rightButton = UIButton() - var calendar = WeekMonthFSCalendar() - private lazy var today: Date = { return Date() }() - var monthCalendarClosure: ((_ month: String) -> Void)? + private let horizonStackView = UIStackView() + private let leftButton = UIButton() + private let rightButton = UIButton() + var calendar = WeekMonthFSCalendar() // MARK: - Life Cycle @@ -43,10 +52,12 @@ final class CalendarView: UIView { extension CalendarView { private func setCalendar(scope: FSCalendarScope, scrollDirection: FSCalendarScrollDirection) { + calendar = WeekMonthFSCalendar(calendarScope: scope, scrollDirection: scrollDirection) } private func setUI() { + backgroundColor = .ntdBlack yearMonthLabel.do { @@ -56,15 +67,20 @@ extension CalendarView { } todayButton.do { - $0.configuration?.image = .icBackToday - $0.configuration?.title = I18N.todayButton - $0.configuration?.imagePadding = 2 - $0.configuration?.contentInsets = NSDirectionalEdgeInsets.init(top: 3, leading: 6, bottom: 2, trailing: 7) - $0.configuration?.cornerStyle = .capsule - $0.configuration?.attributedTitle?.font = .Pretendard(.regular, size: 14) - $0.configuration?.baseBackgroundColor = .gray2 - $0.configuration?.baseForegroundColor = .gray5 + var config = UIButton.Configuration.filled() + config.image = .icBackToday + config.title = I18N.todayButton + config.imagePadding = 2 + config.contentInsets = NSDirectionalEdgeInsets.init(top: 3, leading: 6, bottom: 2, trailing:7) + config.cornerStyle = .capsule + config.attributedTitle?.font = .Pretendard(.regular, size: 14) + config.baseBackgroundColor = .gray2 + config.baseForegroundColor = .gray5 + + $0.configuration = config + $0.addTarget(self, action: #selector(todayBtnTapped), for: .touchUpInside) } + horizonStackView.do { $0.axis = .horizontal $0.spacing = 16 @@ -79,9 +95,17 @@ extension CalendarView { $0.setImage(.calendarRight, for: .normal) $0.addTarget(self, action: #selector(nextBtnTapped), for: .touchUpInside) } + + calendar.do { + $0.collectionView.register(MissionCalendarCell.self, + forCellWithReuseIdentifier: MissionCalendarCell.identifier) + } + + } private func setLayout(scope: FSCalendarScope) { + switch scope { case .week: addSubviews(calendar, yearMonthLabel, todayButton) @@ -102,16 +126,15 @@ extension CalendarView { $0.directionalHorizontalEdges.equalToSuperview().inset(11) $0.bottom.equalToSuperview().inset(20) } + case .month: addSubviews(horizonStackView, calendar) horizonStackView.addArrangedSubviews(leftButton, yearMonthLabel, rightButton) - - leftButton.snp.makeConstraints { - $0.size.equalTo(CGSize(width: 25, height: 25)) - } - rightButton.snp.makeConstraints { - $0.size.equalTo(CGSize(width: 25, height: 25)) + [leftButton, rightButton].forEach { + $0.snp.makeConstraints { + $0.size.equalTo(CGSize(width: 25, height: 25)) + } } horizonStackView.snp.makeConstraints { @@ -131,6 +154,7 @@ extension CalendarView { } func scrollCurrentPage(calendar: WeekMonthFSCalendar, isPrev: Bool) { + let gregorian = Calendar(identifier: .gregorian) calendar.setCurrentPage( gregorian.date(byAdding: calendar.scope == .week ? .weekOfMonth : .month, value: isPrev ? -1 : 1, to: calendar.currentPage)!, animated: true) let monthDateFormatter = DateFormatter() @@ -152,9 +176,41 @@ extension CalendarView { scrollCurrentPage(calendar: calendar, isPrev: false) } + @objc + func todayBtnTapped(_sender: UIButton) { + delegate?.todayBtnTapped() + } + +} + +extension CalendarView { + func setCalendarSelectedDate(_ dates: [Date]) { dates.forEach { calendar.select($0) } } + + func configure(delegate: FSCalendarDelegate, datasource: FSCalendarDataSource) { + calendar.delegate = delegate + calendar.dataSource = datasource + } + + func configureYearMonth(to text: String) { + yearMonthLabel.text = text + } + + func reloadCollectionView() { + calendar.collectionView.reloadData() + } + + func updateDetailConstraints() { + horizonStackView.snp.updateConstraints { + $0.top.equalToSuperview().offset(54) + } + calendar.snp.updateConstraints { + $0.bottom.equalToSuperview().inset(45) + } + } + } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift index 039783d2..a7734815 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift @@ -109,23 +109,19 @@ extension DetailCalendarViewController { $0.trailing.equalToSuperview().inset(16) $0.size.equalTo(CGSize(width: 44, height: 35)) } - - monthCalendar.horizonStackView.snp.updateConstraints { - $0.top.equalToSuperview().offset(54) - } - + monthCalendar.snp.makeConstraints { $0.centerY.equalTo(safeArea) $0.directionalHorizontalEdges.equalTo(safeArea).inset(15) $0.height.equalTo((getDeviceWidth()-30)*1.2) } - monthCalendar.calendar.snp.updateConstraints { - $0.bottom.equalToSuperview().inset(45) - } + subLabel.snp.makeConstraints { $0.bottom.equalToSuperview().inset(25) $0.left.equalToSuperview().offset(17) } + + monthCalendar.updateConstraints() } } @@ -142,7 +138,7 @@ extension DetailCalendarViewController { extension DetailCalendarViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { func calendarCurrentPageDidChange(_ calendar: FSCalendar) { - monthCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage) + monthCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage)) guard let id = self.userId else { return } requestParticualrDatesAPI(id: id) } From 1a72b60dcac961eda4014c31e0a34fcfce2e73c9 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:54:48 +0900 Subject: [PATCH 11/42] =?UTF-8?q?[Fix]=20#198=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/Cells/HomeEmptyCollectionViewCell.swift | 3 +++ .../Home/Cells/MissionListCollectionViewCell.swift | 6 +++++- .../ViewControllers/HomeDeleteViewController.swift | 10 +++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/HomeEmptyCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/HomeEmptyCollectionViewCell.swift index c02504f8..04764571 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/HomeEmptyCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/HomeEmptyCollectionViewCell.swift @@ -21,6 +21,8 @@ final class HomeEmptyCollectionViewCell: UICollectionViewCell { private let logoImage = UIImageView() private let emptyLabel = UILabel() + // MARK: - Life Cycle + override init(frame: CGRect) { super.init(frame: .zero) setUI() @@ -35,6 +37,7 @@ final class HomeEmptyCollectionViewCell: UICollectionViewCell { // MARK: - Methods extension HomeEmptyCollectionViewCell { + private func setUI() { backgroundColor = .clear diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift index b6eb4015..6063f709 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift @@ -16,13 +16,13 @@ final class MissionListCollectionViewCell: UICollectionViewCell { static let identifier = "MissionListCollectionViewCell" + var userId: Int = 0 var isTappedClosure: ((_ result: Bool, _ userId: Int) -> Void)? var isTapped: Bool = false { didSet { setUI() } } - var userId: Int = 0 // MARK: - UI Components @@ -121,12 +121,16 @@ extension MissionListCollectionViewCell { func configure(model: DailyMissionResponseDTO) { self.userId = model.id + tagLabel.text = model.situationName missionLabel.text = model.title + missionLabel.lineBreakMode = .byTruncatingTail + switch model.completionStatus { case .UNCHECKED: isTapped = false case .CHECKED: isTapped = true } + checkButton.isSelected = isTapped } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDeleteViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDeleteViewController.swift index f3e835a1..92f94bf1 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDeleteViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDeleteViewController.swift @@ -14,15 +14,19 @@ final class HomeDeleteViewController: UIViewController { // MARK: - Properties - private lazy var safeArea = self.view.safeAreaLayoutGuide var deleteClosure: (() -> Void)? + private lazy var safeArea = self.view.safeAreaLayoutGuide + // MARK: - UI Components private let deleteModalView = DeleteModalView() + // MARK: - Life Cycle + override func viewDidLoad() { super.viewDidLoad() + setUI() setLayout() } @@ -32,7 +36,7 @@ final class HomeDeleteViewController: UIViewController { let touch = touches.first! let location = touch.location(in: self.view) - if !self.view.frame.contains(location) { + if !view.frame.contains(location) { dismiss(animated: true) } } @@ -47,7 +51,7 @@ extension HomeDeleteViewController { deleteModalView.do { $0.deleteClosure = { self.deleteClosure?() - self.dismiss(animated: true) + self.dismiss(animated: true) } $0.cancelClosure = { self.dismiss(animated: true) From a23f5071e3267c3cf3ec899ac0d0db07f3b9fd2d Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:55:07 +0900 Subject: [PATCH 12/42] =?UTF-8?q?[Fix]=20#198=20-=20layout=20=EC=9E=84?= =?UTF-8?q?=EC=9D=98=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/Layout/CompositionalLayout.swift | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift index bf78e680..1be1df66 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift @@ -9,13 +9,18 @@ import UIKit final class CompositionalLayout { - class func _vertical(_ itemWidth: NSCollectionLayoutDimension, _ itemHeight: NSCollectionLayoutDimension, _ groupWidth: NSCollectionLayoutDimension, _ groupHeight: NSCollectionLayoutDimension, count: Int, edge: NSDirectionalEdgeInsets?) -> NSCollectionLayoutSection { + class func vertical(itemWidth: NSCollectionLayoutDimension = .fractionalWidth(1), + itemHeight: NSCollectionLayoutDimension = .fractionalHeight(1), + groupWidth: NSCollectionLayoutDimension = .fractionalWidth(1), + groupHeight: NSCollectionLayoutDimension = .fractionalHeight(1), + count: Int, + edge: NSDirectionalEdgeInsets = .zero) -> NSCollectionLayoutSection { let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: itemWidth, heightDimension: itemHeight)) - let group = NSCollectionLayoutGroup.vertical(layoutSize: .init(widthDimension: groupWidth, heightDimension: groupHeight), subitem: item, count: count ) - return section(group, edge ?? NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + let group = NSCollectionLayoutGroup.vertical(layoutSize: .init(widthDimension: groupWidth, heightDimension: groupHeight), subitem: item, count: count) + return createSection(group, edge) } - class func section(_ group: NSCollectionLayoutGroup, _ edge: NSDirectionalEdgeInsets) -> NSCollectionLayoutSection { + class func createSection(_ group: NSCollectionLayoutGroup, _ edge: NSDirectionalEdgeInsets) -> NSCollectionLayoutSection { let section = NSCollectionLayoutSection(group: group) section.contentInsets = edge return section From 092e89e75f672bf7b671c813cb3e5c5559ecbfb7 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:55:33 +0900 Subject: [PATCH 13/42] =?UTF-8?q?[Fix]=20#198=20-=20=EB=8D=B0=EC=9D=BC?= =?UTF-8?q?=EB=A6=AC=20=EB=AF=B8=EC=85=98=20DTO=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/DailyMissionResponseDTO.swift | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift index 1805e850..f031d03f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift @@ -14,8 +14,23 @@ enum CompletionStatus: String, Codable, Hashable { // MARK: - DailyMissionResponseDTO struct DailyMissionResponseDTO: Codable, Hashable { - var id: Int - var title: String - var situationName: String - var completionStatus: CompletionStatus + + var uuid = UUID() + let id: Int + let title: String + let situationName: String + let completionStatus: CompletionStatus + + enum CodingKeys: String, CodingKey { + + case id = "id" + case title = "title" + case situationName = "situationName" + case completionStatus = "completionStatus" + } + + static func == (lhs: DailyMissionResponseDTO, rhs: DailyMissionResponseDTO) -> Bool { + lhs.uuid == rhs.uuid + } + } From af4c171f93ca42eb86bfad373bf5b5c23d5e8fa9 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:55:54 +0900 Subject: [PATCH 14/42] =?UTF-8?q?[Fix]=20#198=20-=20=EB=84=A4=ED=8A=B8?= =?UTF-8?q?=EC=9B=8C=ED=81=AC=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/API/Home/HomeAPI.swift | 51 +++++++++++-------- .../DetailAchievementViewController.swift | 23 +++------ 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift index f753522b..57e28a4b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift @@ -20,37 +20,44 @@ final class HomeAPI { public private(set) var missionDailyData: GeneralArrayResponse? public private(set) var missionDetailDailyData: GeneralResponse? public private(set) var updateMissionStatus: GeneralResponse? - public private(set) var missionWeekly: GeneralResponse? + public private(set) var missionWeekly: GeneralArrayResponse? public private(set) var addAnotherDay: GeneralResponse? public private(set) var particularDays: GeneralArrayResponse? // MARK: - GET - func getDailyMission(date: String, completion: @escaping (NetworkResult) -> Void) { - homeProvider.request(.dailyMission(date: date)) { response in - switch response { - case let .success(response): - let statusCode = response.statusCode - let data = response.data - let networkResult = NetworkBase.judgeStatus(by: statusCode, data, [DailyMissionResponseDTO].self) - completion(networkResult) - case let .failure(err): - print(err) + func getDailyMission(date: String, completion: @escaping (GeneralArrayResponse?) -> Void) { + homeProvider.request(.dailyMission(date: date)) { result in + switch result { + case .success(let response): + do { + self.missionDailyData = try response.map(GeneralArrayResponse?.self) + guard let missionDailtData = self.missionDailyData else { return } + completion(missionDailtData) + } catch let err { + print(err.localizedDescription, 500) + } + case .failure(let err): + print(err.localizedDescription) + completion(nil) } } } - func getWeeklyMissoin(startDate: String, completion: @escaping (NetworkResult) -> Void) { - homeProvider.request(.missionWeekly(startDate: startDate)) { response in - switch response { - case let .success(response): - let statusCode = response.statusCode - let data = response.data - let networkResult = NetworkBase.judgeStatus(by: statusCode, data, - [WeekMissionResponseDTO].self) - completion(networkResult) - case let .failure(err): - print(err) + func getWeeklyMissoin(startDate: String, completion: @escaping (GeneralArrayResponse?) -> Void) { + homeProvider.request(.missionWeekly(startDate: startDate)) { result in + switch result { + case .success(let response): + do { + self.missionWeekly = try response.map(GeneralArrayResponse?.self) + guard let missionWeekly = self.missionWeekly else { return } + completion(missionWeekly) + } catch let err { + print(err.localizedDescription, 500) + } + case .failure(let err): + print(err.localizedDescription) + completion(nil) } } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift index 924729f4..0543ad10 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift @@ -146,22 +146,13 @@ extension DetailAchievementViewController { } extension DetailAchievementViewController { - func requestDetailAPI(date: String) { - HomeAPI.shared.getDailyMission(date: date) { [self] result in - switch result { - case let .success(data): - guard let data = data as? [DailyMissionResponseDTO] else { return } - self.missionList = data - updateData(item: missionList) - case .requestErr: - print("requestErr") - case .pathErr: - print("pathErr") - case .serverErr: - print("serverErr") - case .networkFail: - print("networkFail") - } + + private func requestDetailAPI(date: String) { + HomeAPI.shared.getDailyMission(date: date) { response in + guard let response = response else { return } + guard let data = response.data else { return } + let missionList = data + self.updateData(item: missionList) } } } From b06e7140064ae564f061ae544a896cd757375323 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:56:11 +0900 Subject: [PATCH 15/42] =?UTF-8?q?[Feat]=20#198=20-=20=ED=99=88=20DataSourc?= =?UTF-8?q?e=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO.xcodeproj/project.pbxproj | 4 + .../Home/ViewControllers/HomeDataSource.swift | 214 ++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 92673262..acdc4569 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 09A1465F2A192C4900DDC308 /* WeekMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A1465E2A192C4900DDC308 /* WeekMissionResponseDTO.swift */; }; 09A146652A1964B500DDC308 /* AddAnotherDayResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A146642A19649A00DDC308 /* AddAnotherDayResponseDTO.swift */; }; 09C8602D2AB14B4800C4F4B1 /* FSCalendar in Frameworks */ = {isa = PBXBuildFile; productRef = 09C8602C2AB14B4800C4F4B1 /* FSCalendar */; }; + 09CF56042B09F23800526C8C /* HomeDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CF56032B09F23800526C8C /* HomeDataSource.swift */; }; 09DCCD1F2A18ED76003DCF8A /* DailyMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */; }; 09DCCD212A18EF43003DCF8A /* HomeSevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD202A18EF43003DCF8A /* HomeSevice.swift */; }; 09DCCD232A18EFB0003DCF8A /* HomeAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD222A18EFB0003DCF8A /* HomeAPI.swift */; }; @@ -202,6 +203,7 @@ 099FC98829B3233D005B37E6 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; 09A1465E2A192C4900DDC308 /* WeekMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekMissionResponseDTO.swift; sourceTree = ""; }; 09A146642A19649A00DDC308 /* AddAnotherDayResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAnotherDayResponseDTO.swift; sourceTree = ""; }; + 09CF56032B09F23800526C8C /* HomeDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDataSource.swift; sourceTree = ""; }; 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyMissionResponseDTO.swift; sourceTree = ""; }; 09DCCD202A18EF43003DCF8A /* HomeSevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeSevice.swift; sourceTree = ""; }; 09DCCD222A18EFB0003DCF8A /* HomeAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeAPI.swift; sourceTree = ""; }; @@ -451,6 +453,7 @@ isa = PBXGroup; children = ( 3B027A9D299C34DA00BEB65C /* HomeViewController.swift */, + 09CF56032B09F23800526C8C /* HomeDataSource.swift */, 092C09B42A484DD900E9B06B /* HomeDeleteViewController.swift */, 0930DE6129B80550007958DE /* MissionDetailViewController.swift */, 09582B4E29BEBAFA00EF3207 /* DetailCalendarViewController.swift */, @@ -1276,6 +1279,7 @@ 09022D4629C44BC300DE6E49 /* MissionCalendarCell.swift in Sources */, 6CA2083A2A195906001C4247 /* AuthResponseDTO.swift in Sources */, 09582B4B29BDE37C00EF3207 /* DetailFooterReusableView.swift in Sources */, + 09CF56042B09F23800526C8C /* HomeDataSource.swift in Sources */, 093DB03F2A15FCC100ECA5F6 /* MissionDetailResponseDTO.swift in Sources */, 6CF4705B29A68EA9008D145C /* URLConstant.swift in Sources */, 3BD3B5C829B8F82C00D3575B /* AddMissionTextFieldView.swift in Sources */, diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift new file mode 100644 index 00000000..ca96f2bf --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift @@ -0,0 +1,214 @@ +// +// HomeDataSource.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 11/19/23. +// + +import UIKit + +protocol HomeModalDelegate { + + func updateMissionStatus(id: Int, status: CompletionStatus) + func modifyMission(id: Int, type: MissionType) + func deleteMission(index: Int, id: Int) +} + +enum Sections: Int, Hashable { + + case mission, empty +} + +enum Item: Hashable { + + case mission(DailyMissionResponseDTO) + case empty +} + +final class HomeDataSource { + + // MARK: - Properties + + typealias CellRegistration = UICollectionView.CellRegistration + typealias DataSource = UICollectionViewDiffableDataSource + typealias SnapShot = NSDiffableDataSourceSnapshot + + private var currentSection: [Sections] = [.empty] + private var missionList: [DailyMissionResponseDTO] + + var dataSource: DataSource? + var modalDelegate: HomeModalDelegate? + + // MARK: - UI Components + + private let collectionView: UICollectionView + + init(collectionView: UICollectionView, missionList: [DailyMissionResponseDTO]) { + self.collectionView = collectionView + self.missionList = missionList + + setCollectionView() + setDataSource() + setSnapShot() + } + + private func setCollectionView() { + + collectionView.collectionViewLayout = createLayout() + } + + private func setDataSource() { + + let cellRegistration = createMissionCellRegistration() + let emptyRegistration = createEmptyCellRegistration() + + dataSource = DataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in + + switch item { + case .mission: + return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, + for: indexPath, + item: item) + case .empty: + return collectionView.dequeueConfiguredReusableCell(using: emptyRegistration, + for: indexPath, + item: item) + } + }) + } + + private func createMissionCellRegistration() -> CellRegistration { + + return CellRegistration { cell, _, item in + guard let missionItem = self.getMissionItem(from: item) else { return } + + cell.configure(model: missionItem) + + cell.isTappedClosure = { [weak self] result, id in + guard let self else { return } + + let status = result ? CompletionStatus.UNCHECKED : CompletionStatus.CHECKED + self.modalDelegate?.updateMissionStatus(id: id, status: status) + + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.completeCheckMission(title: missionItem.title, situation: missionItem.situationName)) + } + } + } + + private func createEmptyCellRegistration() -> CellRegistration { + return CellRegistration { _, _, _ in } + } + + private func setSnapShot() { + + var snapshot = NSDiffableDataSourceSnapshot() + + defer { + dataSource?.apply(snapshot, animatingDifferences: false) + } + + snapshot.appendSections([.empty]) + snapshot.appendItems([.empty], toSection: .empty) + } + + func updateSnapShot(missioList: [DailyMissionResponseDTO]) { + + self.missionList = missioList + guard var snapshot = dataSource?.snapshot() else { return } + + let newSections: [Sections] = self.missionList.isEmpty ? [.empty] : [.mission] + let item: [Item] = self.missionList.isEmpty ? [.empty] : missioList.map { .mission($0) } + + snapshot.deleteSections(currentSection) + snapshot.appendSections(newSections) + snapshot.appendItems(item, toSection: newSections.first) + + currentSection = newSections + dataSource?.apply(snapshot) + } + + private func createLayout() -> UICollectionViewLayout { + + let layout = UICollectionViewCompositionalLayout { sectionIndex, env in + + guard let section = Sections(rawValue: sectionIndex) else { return nil } + switch section { + case .mission: + return self.missionSection(env: env) + case .empty: + return CompositionalLayout.vertical(count: 1, edge: .init(top: 30, leading: 0, bottom: 0, trailing: 0)) + } + } + return layout + } + + private func missionSection(env: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { + + var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped) + config.backgroundColor = .clear + config.showsSeparators = false + config.trailingSwipeActionsConfigurationProvider = self.makeSwipeActions + + let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: env) + section.orthogonalScrollingBehavior = .none + section.interGroupSpacing = 18 + section.contentInsets = NSDirectionalEdgeInsets(top: 32, leading: 0, bottom: 0, trailing: 18) + + return section + } + + private func makeSwipeActions(for indexPath: IndexPath?) -> UISwipeActionsConfiguration? { + + guard let index = indexPath?.item, + let result = findMissionItem(with: missionList[index].uuid) else { return nil } + + let indexPath = result.index + let data = result.mission + + let deleteAction = UIContextualAction(style: .normal, title: "") { [unowned self] _, _, completion in + + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "home", + title: data.title, + situation: data.situationName, + goal: "", + action: [])) + + self.modalDelegate?.deleteMission(index: indexPath, id: data.id) + completion(true) + } + + let modifyAction = UIContextualAction(style: .normal, title: "") { _, _, completionHandler in + + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickEditMission(section: "home")) + + self.modalDelegate?.modifyMission(id: data.id, type: .update) + completionHandler(true) + } + + deleteAction.backgroundColor = .ntdRed + modifyAction.backgroundColor = .ntdBlue + + deleteAction.image = .icTrash + modifyAction.image = .icFix + + let swipeConfiguration = UISwipeActionsConfiguration(actions: [deleteAction, modifyAction]) + swipeConfiguration.performsFirstActionWithFullSwipe = false + + return swipeConfiguration + } + + private func getMissionItem(from item: Item) -> DailyMissionResponseDTO? { + if case let .mission(missionItem) = item { + return missionItem + } + return nil + } + + private func findMissionItem(with id: UUID) -> (index: Int, mission: DailyMissionResponseDTO)? { + guard let index = missionList.firstIndex(where: { $0.uuid == id }) else { + return nil + } + return (index, missionList[index]) + } + +} From 03b07a4013a065015b3c205db06d388651dc426b Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:56:25 +0900 Subject: [PATCH 16/42] =?UTF-8?q?[Refactor]=20#198=20-=20=ED=99=88=20VC=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewControllers/HomeViewController.swift | 464 ++++++++---------- 1 file changed, 195 insertions(+), 269 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index 8d75533a..63aa0f15 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -16,21 +16,19 @@ final class HomeViewController: UIViewController { // MARK: - Properties private var missionList: [DailyMissionResponseDTO] = [] - private lazy var today: Date = { return Date() }() - private var selectedDate: Date? // 눌렀을 떄 date - dailymissionAPI 호출 시 사용 - private var current: Date? // 스와이프했을 때 일요일 date 구하기 위함 - weeklyAPI 호출 시 사용 - private var count: Int? private var calendarDataSource: [String: Float] = [:] + + private let today = Date() + private var selectedDate: Date? + private var current: Date? + private lazy var safeArea = self.view.safeAreaLayoutGuide - - enum Sections: Int, Hashable { - case mission, empty - } - var dataSource: UICollectionViewDiffableDataSource! = nil // MARK: - UI Components - private lazy var missionCollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) + private var missionCollectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) + private lazy var missionDataSource = HomeDataSource(collectionView: missionCollectionView, missionList: missionList) + private let weekCalendar = CalendarView(calendarScope: .week, scrollDirection: .horizontal) private let addButton = UIButton() @@ -39,16 +37,17 @@ final class HomeViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.viewHome) + dailyLoadData() weeklyLoadData() } override func viewDidLoad() { super.viewDidLoad() + setUI() - register() setLayout() - setupDataSource() + } } @@ -56,53 +55,34 @@ final class HomeViewController: UIViewController { extension HomeViewController { - private func dailyLoadData() { - let todayString = Utils.dateFormatterString(format: nil, date: self.selectedDate ?? today) - requestDailyMissionAPI(date: todayString) - } - - private func weeklyLoadData() { - let sunday = getSunday(date: self.current ?? today) - requestWeeklyMissoinAPI(startDate: Utils.dateFormatterString(format: nil, date: sunday)) - } - - func getSunday(date: Date) -> Date { - let cal = Calendar.current - var comps = cal.dateComponents([.weekOfYear, .yearForWeekOfYear], from: date) - comps.weekday = 1 - let sundayInWeek = cal.date(from: comps)! - return sundayInWeek - } - - private func register() { - missionCollectionView.register(MissionListCollectionViewCell.self, forCellWithReuseIdentifier: MissionListCollectionViewCell.identifier) - missionCollectionView.register(HomeEmptyCollectionViewCell.self, forCellWithReuseIdentifier: HomeEmptyCollectionViewCell.identifier) - } - private func setUI() { + view.backgroundColor = .ntdBlack weekCalendar.do { - $0.calendar.delegate = self - $0.calendar.dataSource = self - $0.calendar.register(MissionCalendarCell.self, forCellReuseIdentifier: MissionCalendarCell.identifier) - $0.todayButton.addTarget(self, action: #selector(todayBtnTapped), for: .touchUpInside) + $0.configure(delegate: self, datasource: self) + $0.delegate = self } missionCollectionView.do { $0.backgroundColor = .bg - $0.bounces = false $0.autoresizingMask = [.flexibleWidth, .flexibleHeight] + $0.bounces = false $0.delegate = self + $0.dataSource = missionDataSource.dataSource } addButton.do { $0.setImage(.addMission, for: .normal) $0.addTarget(self, action: #selector(addBtnTapped), for: .touchUpInside) } + + missionDataSource.modalDelegate = self + } private func setLayout() { + view.addSubviews(weekCalendar, missionCollectionView, addButton) weekCalendar.calendar.select(today) @@ -111,196 +91,142 @@ extension HomeViewController { $0.directionalHorizontalEdges.equalTo(safeArea) $0.height.equalTo(172) } + missionCollectionView.snp.makeConstraints { $0.top.equalTo(weekCalendar.snp.bottom) $0.directionalHorizontalEdges.equalTo(safeArea) $0.bottom.equalToSuperview() } + addButton.snp.makeConstraints { $0.width.height.equalTo(convertByHeightRatio(60)) $0.trailing.equalTo(safeArea).inset(18) $0.bottom.equalTo(safeArea).inset(20) } } +} + +// MARK: - Action + +extension HomeViewController: CalendarViewDelegate { - private func setupDataSource() { - dataSource = UICollectionViewDiffableDataSource(collectionView: missionCollectionView, cellProvider: { collectionView, indexPath, item in - let section = self.dataSource.snapshot().sectionIdentifiers[indexPath.section] - switch section { - case .mission: - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MissionListCollectionViewCell.identifier, for: indexPath) as! MissionListCollectionViewCell - cell.configure(model: item as! DailyMissionResponseDTO ) - cell.isTappedClosure = { [self] result, id in - if result { - self.requestPatchUpdateMissionAPI(id: id, status: CompletionStatus.UNCHECKED ) - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.completeCheckMission(title: self.missionList[indexPath.row].title, situation: self.missionList[indexPath.row].situationName)) - } else { - self.requestPatchUpdateMissionAPI(id: id, status: CompletionStatus.CHECKED ) - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.completeCheckMission(title: self.missionList[indexPath.row].title, situation: self.missionList[indexPath.row].situationName)) - } - } - return cell - case .empty: - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeEmptyCollectionViewCell.identifier, for: indexPath) as! HomeEmptyCollectionViewCell - return cell - } - }) + @objc + func addBtnTapped(_sender: UIButton) { + + let nextViewController = RecommendViewController() + nextViewController.setSelectDate(Utils.dateFormatterString(format: "yyyy.MM.dd", date: selectedDate ?? Date())) + + Utils.push(navigationController, nextViewController) } - private func reloadData() { - var snapshot = NSDiffableDataSourceSnapshot() - defer { - dataSource.apply(snapshot, animatingDifferences: false) - } - snapshot.appendSections([.empty]) - snapshot.appendItems([0], toSection: .empty) - dataSource.apply(snapshot, animatingDifferences: true) + func todayBtnTapped() { + + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.clickReturnToday) + + weekCalendar.calendar.select(today) + weekCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: today)) + requestDailyMissionAPI(date: Utils.dateFormatterString(format: nil, date: today)) } - private func updateData() { - var snapshot = dataSource.snapshot() - if missionList.isEmpty { - if snapshot.sectionIdentifiers.contains(.mission) { - snapshot.deleteSections([.mission]) - snapshot.appendSections([.empty]) - snapshot.appendItems([0], toSection: .empty) - } else if snapshot.sectionIdentifiers.contains(.empty) { - - } else { - snapshot.appendSections([.empty]) - snapshot.appendItems([0], toSection: .empty) - } - } else { - if snapshot.sectionIdentifiers.contains(.empty) { - snapshot.deleteSections([.empty]) - snapshot.appendSections([.mission]) - snapshot.appendItems(missionList, toSection: .mission) - - } else if snapshot.sectionIdentifiers.contains(.mission) { - snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .mission)) - snapshot.appendItems(missionList, toSection: .mission) - } else { - snapshot.appendSections([.mission]) - snapshot.appendItems(missionList, toSection: .mission) - } - } - dataSource.apply(snapshot) - } +} - private func layout() -> UICollectionViewLayout { - let layout = UICollectionViewCompositionalLayout { sectionIndex, layoutEnvirnment in - let section = self.dataSource.snapshot().sectionIdentifiers[sectionIndex] - switch section { - case .mission: - var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped) - config.backgroundColor = .clear - config.showsSeparators = false - config.trailingSwipeActionsConfigurationProvider = self.makeSwipeActions - - let layoutSection = NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvirnment) - layoutSection.orthogonalScrollingBehavior = .none - layoutSection.interGroupSpacing = 18 - layoutSection.contentInsets = NSDirectionalEdgeInsets(top: 32, leading: 0, bottom: 0, trailing: 18) - - return layoutSection - - case .empty: - return CompositionalLayout._vertical(.fractionalWidth(1), .fractionalWidth(1), .fractionalWidth(1), .fractionalWidth(1), count: 1, edge: .init(top: 30, leading: 0, bottom: 0, trailing: 0)) - } - } - return layout +// MARK: - HomeModalDelegate + +extension HomeViewController: HomeModalDelegate { + + func updateMissionStatus(id: Int, status: CompletionStatus) { + + self.requestPatchUpdateMissionAPI(id: id, status: status) } - private func makeSwipeActions(for indexPath: IndexPath?) -> UISwipeActionsConfiguration? { - let deleteAction = UIContextualAction(style: .normal, title: "") { [unowned self] _, _, completion in - - guard let index = indexPath?.item else { return } - - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "home", title: self.missionList[index].title, situation: self.missionList[index].situationName, goal: "", action: [])) - - let modalViewController = HomeDeleteViewController() - modalViewController.modalPresentationStyle = .overFullScreen - modalViewController.modalTransitionStyle = .crossDissolve - modalViewController.deleteClosure = { - self.requestDeleteMission(index: index) - } - present(modalViewController, animated: false) - completion(true) - } + func modifyMission(id: Int, type: MissionType) { - let modifyAction = UIContextualAction(style: .normal, title: "") { _, _, completionHandler in - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickEditMission(section: "home")) - - guard let index = indexPath?.item else { return } - let id = self.missionList[index].id - let updateMissionViewController = AddMissionViewController() - updateMissionViewController.setMissionId(id) - updateMissionViewController.setViewType(.update) - Utils.push(self.navigationController, updateMissionViewController) - completionHandler(true) + let updateMissionViewController = AddMissionViewController() + + updateMissionViewController.setMissionId(id) + updateMissionViewController.setViewType(type) + + Utils.push(self.navigationController, updateMissionViewController) + } + + func deleteMission(index: Int, id: Int) { + + let modalViewController = HomeDeleteViewController() + + modalViewController.modalPresentationStyle = .overFullScreen + modalViewController.modalTransitionStyle = .crossDissolve + + modalViewController.deleteClosure = { + self.requestDeleteMission(index: index, id: id) } - deleteAction.backgroundColor = .ntdRed - modifyAction.backgroundColor = .ntdBlue - deleteAction.image = .icTrash - modifyAction.image = .icFix - - let swipeConfiguration = UISwipeActionsConfiguration(actions: [deleteAction, modifyAction]) - swipeConfiguration.performsFirstActionWithFullSwipe = false - return swipeConfiguration + + present(modalViewController, animated: false) } } // MARK: - Collectionview Delegate extension HomeViewController: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if !missionList.isEmpty { + let modalViewController = MissionDetailViewController() modalViewController.modalPresentationStyle = .overFullScreen modalViewController.userId = missionList[indexPath.item].id - + modalViewController.deleteClosure = { [weak self] in - self?.dailyLoadData() - self?.weeklyLoadData() - self?.updateData() + guard let self else { return } + + self.dailyLoadData() + self.weeklyLoadData() + self.missionDataSource.updateSnapShot(missioList: self.missionList) } + modalViewController.moveDateClosure = { [weak self] date in + guard let self else { return } + let modifiedDate: Date = date.toDate(withFormat: "YYYY.MM.dd") - self?.weekCalendar.calendar.select(modifiedDate) - self?.requestDailyMissionAPI(date: Utils.dateFormatterString(format: nil, date: modifiedDate)) + self.weekCalendar.calendar.select(modifiedDate) + self.requestDailyMissionAPI(date: Utils.dateFormatterString(format: nil, date: modifiedDate)) } + self.present(modalViewController, animated: true) } } } -extension HomeViewController { - - @objc - func addBtnTapped(_sender: UIButton) { - let nextViewController = RecommendViewController() - nextViewController.setSelectDate(Utils.dateFormatterString(format: "yyyy.MM.dd", date: selectedDate ?? Date())) - Utils.push(navigationController, nextViewController) - } - - @objc - func todayBtnTapped(_sender: UIButton) { - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.clickReturnToday) - - weekCalendar.calendar.select(today) - weekCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: today) - requestDailyMissionAPI(date: Utils.dateFormatterString(format: nil, date: today)) - } -} - // MARK: - FSCalendar Delegate, DataSource, DelegateAppearance extension HomeViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { + func calendarCurrentPageDidChange(_ calendar: FSCalendar) { - weekCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage) - self.current = calendar.currentPage - let sunday = getSunday(date: calendar.currentPage) - requestWeeklyMissoinAPI(startDate: Utils.dateFormatterString(format: nil, date: sunday)) + + updateCalendar(for: calendar.currentPage) + } + + func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { + + self.selectedDate = date + + weekCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: date)) + requestDailyMissionAPI(date: Utils.dateFormatterString(format: nil, date: date)) + + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.clickWeeklyDate(date: Utils.dateFormatterString(format: nil, date: date))) + } + + func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { + let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell + + guard let percentage = getPercentage(for: date) else { return cell } + + switch percentage { + case 0.0: cell.configure(.none, .week) + case 1.0: cell.configure(.rateFull, .week) + default: cell.configure(.rateHalf, .week) + } + + return cell } func calendar(_ calendar: FSCalendar, titleFor date: Date) -> String? { @@ -311,49 +237,12 @@ extension HomeViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalend Utils.dateFormatterString(format: "dd", date: date) } - func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { - self.selectedDate = date - weekCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: date) - requestDailyMissionAPI(date: Utils.dateFormatterString(format: nil, date: date)) - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.clickWeeklyDate(date: Utils.dateFormatterString(format: nil, date: date))) - } - func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, subtitleSelectionColorFor date: Date) -> UIColor? { - guard let count = self.count else { return .white } - let dateString = Utils.dateFormatterString(format: nil, date: date) - if let percentage = self.calendarDataSource[dateString] { - switch (count, percentage) { - case (_, 1.0): return .black - default: return .white - } - } - return .white + return subtitleColorFor(date: date) } func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, subtitleDefaultColorFor date: Date) -> UIColor? { - guard let count = self.count else { return .white } - let dateString = Utils.dateFormatterString(format: nil, date: date) - if let percentage = self.calendarDataSource[dateString] { - switch (count, percentage) { - case (_, 1.0): return .black - default: return .white - } - } - return .white - } - - func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { - let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell - guard let count = self.count else { return cell } - let dateString = Utils.dateFormatterString(format: nil, date: date) - if let percentage = self.calendarDataSource[dateString] { - switch (count, percentage) { - case (_, 1.0): cell.configure(.rateFull, .week) - case (_, 0.0): cell.configure(.none, .week) - case (2, 0.5), (3, 0.0..<1.0), (_, _): cell.configure(.rateHalf, .week) - } - } - return cell + return subtitleColorFor(date: date) } } @@ -362,65 +251,102 @@ extension HomeViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalend extension HomeViewController { func requestDailyMissionAPI(date: String) { - HomeAPI.shared.getDailyMission(date: date) { [weak self] result in - switch result { - case let .success(data): - guard let data = data as? [DailyMissionResponseDTO] else {return} - self?.missionList = [] - if !data.isEmpty { - self?.missionList = data - } - self?.updateData() - - case .pathErr: print("pathErr") - case .serverErr: print("serverErr") - case .networkFail: print("networkFail") - case .requestErr: print("networkFail") - } + + HomeAPI.shared.getDailyMission(date: date) { [weak self] response in + guard let self, let response = response, let data = response.data else { return } + + self.missionList = data + self.missionDataSource.updateSnapShot(missioList: data) } } private func requestWeeklyMissoinAPI(startDate: String) { - HomeAPI.shared.getWeeklyMissoin(startDate: startDate) { result in - switch result { - case let .success(data): - guard let data = data as? [WeekMissionResponseDTO] else { return } - self.calendarDataSource = [:] - for item in data { - self.calendarDataSource[item.actionDate] = item.percentage - self.count = self.calendarDataSource.count - } - self.weekCalendar.calendar.reloadData() - case .requestErr: print("requestErr") - case .pathErr: print("pathErr") - case .serverErr: print("serverErr") - case .networkFail: print("networkFail") - } + + HomeAPI.shared.getWeeklyMissoin(startDate: startDate) { [weak self] response in + guard let self, let response = response, let data = response.data else { return } + + let calendarData = data.compactMap { ($0.actionDate, $0.percentage) } + self.calendarDataSource = Dictionary(uniqueKeysWithValues: calendarData) + + self.weekCalendar.reloadCollectionView() } } private func requestPatchUpdateMissionAPI(id: Int, status: CompletionStatus) { - HomeAPI.shared.patchUpdateMissionStatus(id: id, status: status.rawValue) { [weak self] result in - guard let result = result else { return } - for index in 0..<(self?.missionList.count ?? 0) { - if self?.missionList[index].id == id { - guard let data = result.data else { return } - self?.missionList[index] = data - self?.weeklyLoadData() - self?.updateData() - } else {} + + HomeAPI.shared.patchUpdateMissionStatus(id: id, status: status.rawValue) { [weak self] response in + guard let self, let response = response, let data = response.data else { return } + + if let index = self.missionList.firstIndex(where: { $0.id == id }) { + self.missionList[index] = data + self.weeklyLoadData() + self.missionDataSource.updateSnapShot(missioList: self.missionList) } } } - private func requestDeleteMission(index: Int) { - let id = self.missionList[index].id - HomeAPI.shared.deleteMission(id: id) { [weak self] _ in - self?.dailyLoadData() - self?.weeklyLoadData() - self?.updateData() + private func requestDeleteMission(index: Int, id: Int) { + HomeAPI.shared.deleteMission(id: id) { [weak self] _ in + guard let self else { return } + + self.dailyLoadData() + self.weeklyLoadData() + self.missionDataSource.updateSnapShot(missioList: self.missionList) - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "home", title: (self?.missionList[index].title)!, situation: (self?.missionList[index].situationName)!, goal: "", action: [])) + let data = self.missionList[index] + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "home", + title: data.title, + situation: data.situationName, + goal: "", + action: [])) } } } + +// MARK: - Others + +extension HomeViewController { + + private func dailyLoadData() { + + let todayString = Utils.dateFormatterString(format: nil, date: self.selectedDate ?? today) + requestDailyMissionAPI(date: todayString) + } + + private func weeklyLoadData() { + + let sunday = getSunday(date: self.current ?? today) + requestWeeklyMissoinAPI(startDate: Utils.dateFormatterString(format: nil, date: sunday)) + } + + private func getSunday(date: Date) -> Date { + + let cal = Calendar.current + var comps = cal.dateComponents([.weekOfYear, .yearForWeekOfYear], from: date) + comps.weekday = 1 + let sundayInWeek = cal.date(from: comps)! + return sundayInWeek + } + + private func getPercentage(for date: Date) -> Float? { + + let dateString = Utils.dateFormatterString(format: nil, date: date) + return self.calendarDataSource[dateString] + } + + private func updateCalendar(for date: Date) { + + self.current = date + weekCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: date)) + requestWeeklyMissoinAPI(startDate: Utils.dateFormatterString(format: nil, date: getSunday(date: date))) + } + + private func subtitleColorFor(date: Date) -> UIColor { + + if let percentage = getPercentage(for: date) { + return percentage == 1.0 ? .black : .white + } + + return .white + } +} From 1d8e4d29031dd481fab3e9b78fdc51bb79708cc1 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 17:33:54 +0900 Subject: [PATCH 17/42] =?UTF-8?q?[Fix]=20#199=20-=20=EB=84=A4=ED=8A=B8?= =?UTF-8?q?=EC=9B=8C=ED=81=AC=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/API/Achieve/AchieveAPI.swift | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift index 8754a855..b9cd3053 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift @@ -40,17 +40,16 @@ final class AchieveAPI { // MARK: - GET func getAchieveCalendar(month: String, completion: @escaping (GeneralArrayResponse?) -> Void) { - achieveProvider.request(.achieveCalendar(month: month)) { response in - switch response { - case .success(let result): - do { - let responseData = try result.map(GeneralArrayResponse.self) - self.achieveCalendarData = responseData - completion(self.achieveCalendarData) - } catch let error { - print(error.localizedDescription) - completion(nil) - } + achieveProvider.request(.achieveCalendar(month: month)) { result in + switch result { + case .success(let response): + do { + self.achieveCalendarData = try response.map(GeneralArrayResponse?.self) + guard let achieveCalendarData = self.achieveCalendarData else { return } + completion(achieveCalendarData) + } catch let err { + print(err.localizedDescription, 500) + } case .failure(let err): print(err.localizedDescription) completion(nil) From ba00144d2ba5044d3253454127540dbb49608768 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 17:34:07 +0900 Subject: [PATCH 18/42] =?UTF-8?q?[Fix]=20#199=20-=20=EC=84=B1=EC=B7=A8=20V?= =?UTF-8?q?C=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AchievementViewController.swift | 110 +++++++----------- 1 file changed, 44 insertions(+), 66 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index a799c2d3..5fdc4e12 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -15,14 +15,8 @@ final class AchievementViewController: UIViewController { // MARK: - Properties - var selectDate: Date? - private var count: Int? private var currentPage = Date() - private var dataSource: [String: Float] = [:] { - didSet { - self.monthCalendar.calendar.reloadData() - } - } + private var dataSource: [String: Float] = [:] private lazy var safeArea = self.view.safeAreaLayoutGuide @@ -58,10 +52,6 @@ final class AchievementViewController: UIViewController { extension AchievementViewController { - func reloadMonthData(month: String) { - requestMonthAPI(month: month) - } - private func setUI() { view.backgroundColor = .ntdBlack @@ -85,7 +75,7 @@ extension AchievementViewController { $0.calendar.dataSource = self $0.monthCalendarClosure = { [weak self] month in guard let self else { return } - self.reloadMonthData(month: month) + self.requestMonthAPI(month: month) } } } @@ -119,26 +109,12 @@ extension AchievementViewController { } func requestMonthAPI(month: String) { - AchieveAPI.shared.getAchieveCalendar(month: month) { [weak self] result in - guard let self else { return } - guard let result = result?.data else { return } - self.dataSource = [:] - let currentMonth = Calendar.current.component(.month, from: currentPage) - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy-MM-dd" - let actionDate = result.filter { result in - if let date = dateFormatter.date(from: result.actionDate) { - let month = Calendar.current.component(.month, from: date) - return month == currentMonth - } - return false - } - - actionDate.forEach { - self.dataSource[$0.actionDate] = $0.percentage - self.count = self.dataSource.count - } - + AchieveAPI.shared.getAchieveCalendar(month: month) { [weak self] response in + + guard let self, let response = response, let data = response.data else { return } + + let calendarData = data.compactMap { ($0.actionDate, $0.percentage) } + self.dataSource = Dictionary(uniqueKeysWithValues: calendarData) self.monthCalendar.calendar.collectionView.reloadData() } } @@ -149,10 +125,10 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F func calendarCurrentPageDidChange(_ calendar: FSCalendar) { self.currentPage = calendar.currentPage monthCalendar.yearMonthLabel.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, - date: calendar.currentPage) - reloadMonthData(month: Utils.dateFormatterString(format: "yyyy-MM", date: calendar.currentPage)) + date: calendar.currentPage) + requestMonthAPI(month: Utils.dateFormatterString(format: "yyyy-MM", date: calendar.currentPage)) } - + func calendar(_ calendar: FSCalendar, titleFor date: Date) -> String? { Utils.dateFormatterString(format: "dd", date: date) } @@ -176,50 +152,52 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleSelectionColorFor date: Date) -> UIColor? { - guard let count = self.count else { return .white } - let dateString = Utils.dateFormatterString(format: nil, date: date) - - if let percentage = self.dataSource[dateString] { - switch (count, percentage) { - case (_, 1.0): return .black - default: return .white - } - } - return .white + titleColorFor(date: date) } func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? { let currentMonth = Calendar.current.component(.month, from: currentPage) let dateMonth = Calendar.current.component(.month, from: date) - - guard let count = self.count else { return .white } - let dateString = Utils.dateFormatterString(format: nil, date: date) - if let percentage = self.dataSource[dateString] { - switch (count, percentage) { - case (_, 1.0): return .black - default: return .white - } + + + if let percentage = getPercentage(for: date) { + return percentage == 1.0 ? .black : .white } else { - if currentMonth != dateMonth { - return .gray3 - } + return currentMonth != dateMonth ? .gray3 : .white } - - return .white } func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell - guard let count = self.count else { return cell } - let dateString = Utils.dateFormatterString(format: nil, date: date) - if let percentage = self.dataSource[dateString] { - switch (count, percentage) { - case (_, 1.0): cell.configure(.rateFull, .month) - case (_, 0.0): cell.configure(.none, .month) - case (2, 0.5), (3, 0.0..<1.0), (_, _): cell.configure(.rateHalf, .month) - } + guard let percentage = getPercentage(for: date) else { return cell } + + switch percentage { + case 0.0: cell.configure(.none, .week) + case 1.0: cell.configure(.rateFull, .week) + default: cell.configure(.rateHalf, .week) } + return cell } } + +// MARK: - Others + +extension AchievementViewController { + + private func getPercentage(for date: Date) -> Float? { + + let dateString = Utils.dateFormatterString(format: nil, date: date) + return self.dataSource[dateString] + } + + private func titleColorFor(date: Date) -> UIColor { + + if let percentage = getPercentage(for: date) { + return percentage == 1.0 ? .black : .white + } + + return .white + } +} From 743a6be1e709d6c3d47a409b00b8dfcc0143d825 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 17:42:02 +0900 Subject: [PATCH 19/42] =?UTF-8?q?[Del]=20#197=20-=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/ViewControllers/MissionDetailViewController.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift index 87d6de30..0c0bc305 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift @@ -10,11 +10,6 @@ import UIKit import Then import SnapKit -import UIKit - -import Then -import SnapKit - final class MissionDetailViewController: UIViewController { // MARK: - Properties From 84bc1fa96c4231ee58f868b8d48a1b4b60b7d6c5 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 18:16:39 +0900 Subject: [PATCH 20/42] =?UTF-8?q?[Fix]=20#199=20-=20=EB=A6=B0=ED=8A=B8=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Achievement/ViewControllers/AchievementViewController.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index 5fdc4e12..d4499845 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -159,7 +159,6 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F let currentMonth = Calendar.current.component(.month, from: currentPage) let dateMonth = Calendar.current.component(.month, from: date) - if let percentage = getPercentage(for: date) { return percentage == 1.0 ? .black : .white } else { From 12ef4259959dff8e705f2004cc40b2641aab2b8a Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 21 Nov 2023 15:56:28 +0900 Subject: [PATCH 21/42] =?UTF-8?q?[Fix]=20#198=20-=20custom=20layout=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/Layout/CompositionalLayout.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift index 1be1df66..761869f6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift @@ -10,11 +10,11 @@ import UIKit final class CompositionalLayout { class func vertical(itemWidth: NSCollectionLayoutDimension = .fractionalWidth(1), - itemHeight: NSCollectionLayoutDimension = .fractionalHeight(1), - groupWidth: NSCollectionLayoutDimension = .fractionalWidth(1), - groupHeight: NSCollectionLayoutDimension = .fractionalHeight(1), - count: Int, - edge: NSDirectionalEdgeInsets = .zero) -> NSCollectionLayoutSection { + itemHeight: NSCollectionLayoutDimension = .fractionalWidth(1), + groupWidth: NSCollectionLayoutDimension = .fractionalWidth(1), + groupHeight: NSCollectionLayoutDimension = .fractionalWidth(1), + count: Int, + edge: NSDirectionalEdgeInsets = .zero) -> NSCollectionLayoutSection { let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: itemWidth, heightDimension: itemHeight)) let group = NSCollectionLayoutGroup.vertical(layoutSize: .init(widthDimension: groupWidth, heightDimension: groupHeight), subitem: item, count: count) return createSection(group, edge) From 5bb2322dc6ae2505723f26627f7ef5820fea2f8b Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 21 Nov 2023 15:59:42 +0900 Subject: [PATCH 22/42] =?UTF-8?q?[Fix]=20#198=20-=20Home=20DataSource=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/ViewControllers/HomeDataSource.swift | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift index ca96f2bf..e22077bc 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift @@ -14,17 +14,6 @@ protocol HomeModalDelegate { func deleteMission(index: Int, id: Int) } -enum Sections: Int, Hashable { - - case mission, empty -} - -enum Item: Hashable { - - case mission(DailyMissionResponseDTO) - case empty -} - final class HomeDataSource { // MARK: - Properties @@ -33,6 +22,17 @@ final class HomeDataSource { typealias DataSource = UICollectionViewDiffableDataSource typealias SnapShot = NSDiffableDataSourceSnapshot + enum Sections: Int, Hashable { + + case mission, empty + } + + enum Item: Hashable { + + case mission(DailyMissionResponseDTO) + case empty + } + private var currentSection: [Sections] = [.empty] private var missionList: [DailyMissionResponseDTO] @@ -40,7 +40,7 @@ final class HomeDataSource { var modalDelegate: HomeModalDelegate? // MARK: - UI Components - + private let collectionView: UICollectionView init(collectionView: UICollectionView, missionList: [DailyMissionResponseDTO]) { @@ -63,7 +63,7 @@ final class HomeDataSource { let emptyRegistration = createEmptyCellRegistration() dataSource = DataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in - + switch item { case .mission: return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, @@ -101,7 +101,7 @@ final class HomeDataSource { private func setSnapShot() { - var snapshot = NSDiffableDataSourceSnapshot() + var snapshot = SnapShot() defer { dataSource?.apply(snapshot, animatingDifferences: false) @@ -118,7 +118,7 @@ final class HomeDataSource { let newSections: [Sections] = self.missionList.isEmpty ? [.empty] : [.mission] let item: [Item] = self.missionList.isEmpty ? [.empty] : missioList.map { .mission($0) } - + snapshot.deleteSections(currentSection) snapshot.appendSections(newSections) snapshot.appendItems(item, toSection: newSections.first) @@ -131,7 +131,8 @@ final class HomeDataSource { let layout = UICollectionViewCompositionalLayout { sectionIndex, env in - guard let section = Sections(rawValue: sectionIndex) else { return nil } + guard let section = self.dataSource?.snapshot().sectionIdentifiers[sectionIndex] else { return nil } + switch section { case .mission: return self.missionSection(env: env) @@ -158,7 +159,7 @@ final class HomeDataSource { } private func makeSwipeActions(for indexPath: IndexPath?) -> UISwipeActionsConfiguration? { - + guard let index = indexPath?.item, let result = findMissionItem(with: missionList[index].uuid) else { return nil } @@ -196,7 +197,7 @@ final class HomeDataSource { return swipeConfiguration } - + private func getMissionItem(from item: Item) -> DailyMissionResponseDTO? { if case let .mission(missionItem) = item { return missionItem From 24bbb2c8e19145fcdd4fa17aaede56553c887122 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 21 Nov 2023 15:59:53 +0900 Subject: [PATCH 23/42] =?UTF-8?q?[Fix]=20#198=20-=20=EB=A6=B0=ED=8A=B8=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift index 341258e9..4f9f6c44 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift @@ -100,8 +100,6 @@ extension CalendarView { $0.collectionView.register(MissionCalendarCell.self, forCellWithReuseIdentifier: MissionCalendarCell.identifier) } - - } private func setLayout(scope: FSCalendarScope) { From 945ba60b08ffe0a9bf66ba46e68dac33f79a3766 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Thu, 23 Nov 2023 17:34:23 +0900 Subject: [PATCH 24/42] =?UTF-8?q?[Add]=20#193=20-=204xx=20error=20?= =?UTF-8?q?=EB=B6=84=EA=B8=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/Base/NetworkLoggerPlugin.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift index fc6ff0c3..f756373e 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift @@ -5,7 +5,7 @@ // Created by 김민서 on 2023/02/23. // -import Foundation +import UIKit import Moya @@ -56,6 +56,15 @@ final class MoyaLoggingPlugin: PluginType { } log.append("------------------- END HTTP (\(response.data.count)-byte body) -------------------") print(log) + + switch statusCode { + case 200..<300: + return + default: + + let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as! SceneDelegate + sceneDelegate.window?.rootViewController = UINavigationController(rootViewController: AuthViewController()) + } } func onFail(_ error: MoyaError, target: TargetType) { From c0bba45a4ad2efaae602137ed76b1f6f88993bd9 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Thu, 23 Nov 2023 20:40:38 +0900 Subject: [PATCH 25/42] =?UTF-8?q?[Add]=20#193=20-=20validataion=20Type=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/Service/Achieve/AchieveService.swift | 4 ++++ .../Network/Service/AddMission/AddMissionService.swift | 6 +++++- .../iOS-NOTTODO/Network/Service/Auth/AuthService.swift | 5 +++++ .../iOS-NOTTODO/Network/Service/Home/HomeSevice.swift | 4 ++++ .../Network/Service/Recommend/RecommendActionService.swift | 4 ++++ .../Network/Service/Recommend/RecommendService.swift | 4 ++++ 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Achieve/AchieveService.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Achieve/AchieveService.swift index d346f06e..33234b96 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Achieve/AchieveService.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Achieve/AchieveService.swift @@ -35,6 +35,10 @@ extension AchieveService: TargetType { } } + var validationType: ValidationType { + return .successCodes + } + var task: Moya.Task { switch self { case .achieveCalendar, .achieveDetail: diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/AddMission/AddMissionService.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/AddMission/AddMissionService.swift index 865f96a2..e0418240 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/AddMission/AddMissionService.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/AddMission/AddMissionService.swift @@ -48,6 +48,10 @@ extension AddMissionService: TargetType { } } + var validationType: ValidationType { + return .successCodes + } + var task: Moya.Task { switch self { case .recommendSituation, .recentMission, .missionDates: @@ -64,7 +68,7 @@ extension AddMissionService: TargetType { encoding: JSONEncoding.default) } } - + var headers: [String: String]? { switch self { default: diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Auth/AuthService.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Auth/AuthService.swift index e1ee0cb0..7d670375 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Auth/AuthService.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Auth/AuthService.swift @@ -42,6 +42,11 @@ extension AuthService: TargetType { return .delete } } + + var validationType: ValidationType { + return .successCodes + } + var task: Moya.Task { switch self { case .kakaoAuth(_, let socialToken, let fcmToken): diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Home/HomeSevice.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Home/HomeSevice.swift index 2bda38da..107856b9 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Home/HomeSevice.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Home/HomeSevice.swift @@ -53,6 +53,10 @@ extension HomeService: TargetType { } } + var validationType: ValidationType { + return .successCodes + } + var task: Moya.Task { switch self { case .dailyMission, .deleteMission, .missionWeekly, .dailyDetailMission, .particularMission: diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendActionService.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendActionService.swift index 6a6f0804..31115cf8 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendActionService.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendActionService.swift @@ -32,6 +32,10 @@ extension RecommendActionService: TargetType { } } + var validationType: ValidationType { + return .successCodes + } + var task: Moya.Task { switch self { case .recommendAction: diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendService.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendService.swift index 7687ab7e..10f037e6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendService.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendService.swift @@ -32,6 +32,10 @@ extension RecommendService: TargetType { } } + var validationType: ValidationType { + return .successCodes + } + var task: Moya.Task { switch self { case .recommend: From 8ec4d46a872fb9c68c53d283bafe63b8f0b7408a Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Thu, 23 Nov 2023 20:40:55 +0900 Subject: [PATCH 26/42] =?UTF-8?q?[Del]=20#193=20-=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift index f756373e..f7c39585 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift @@ -56,15 +56,6 @@ final class MoyaLoggingPlugin: PluginType { } log.append("------------------- END HTTP (\(response.data.count)-byte body) -------------------") print(log) - - switch statusCode { - case 200..<300: - return - default: - - let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as! SceneDelegate - sceneDelegate.window?.rootViewController = UINavigationController(rootViewController: AuthViewController()) - } } func onFail(_ error: MoyaError, target: TargetType) { From 07654d74141ecf2019d29dae211f75bb2856ba50 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Thu, 23 Nov 2023 20:41:51 +0900 Subject: [PATCH 27/42] =?UTF-8?q?[Feat]=20#193=20-=20Interceptor=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO.xcodeproj/project.pbxproj | 4 +++ .../Network/API/Achieve/AchieveAPI.swift | 2 +- .../API/AddMission/AddMissionAPI.swift | 2 +- .../Network/API/Auth/AuthAPI.swift | 2 +- .../Network/API/Home/HomeAPI.swift | 2 +- .../Network/API/Recommend/RecommendAPI.swift | 2 +- .../API/Recommend/RecommendActionAPI.swift | 2 +- .../Network/Base/AuthInterceptor.swift | 31 +++++++++++++++++++ 8 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 92673262..725e2926 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ 09582B5129C0BC3600EF3207 /* DetailAchievementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */; }; 0960C0D42A38BC6500A3D8DB /* KeychainUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960C0D32A38BC6500A3D8DB /* KeychainUtil.swift */; }; 0960C0D62A38BC8100A3D8DB /* DefaultKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960C0D52A38BC8100A3D8DB /* DefaultKeys.swift */; }; + 0964BA4A2B0F6BFB00A8984B /* AuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0964BA492B0F6BFB00A8984B /* AuthInterceptor.swift */; }; 097568362A2FEF630001EC46 /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097568352A2FEF3F0001EC46 /* String+.swift */; }; 097C003629AB8270008CAEF3 /* MissionListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097C003529AB8270008CAEF3 /* MissionListCollectionViewCell.swift */; }; 0982DE5429ADCCE000D933D2 /* HomeEmptyCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982DE5329ADCCE000D933D2 /* HomeEmptyCollectionViewCell.swift */; }; @@ -188,6 +189,7 @@ 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewController.swift; sourceTree = ""; }; 0960C0D32A38BC6500A3D8DB /* KeychainUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainUtil.swift; sourceTree = ""; }; 0960C0D52A38BC8100A3D8DB /* DefaultKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultKeys.swift; sourceTree = ""; }; + 0964BA492B0F6BFB00A8984B /* AuthInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthInterceptor.swift; sourceTree = ""; }; 097568352A2FEF3F0001EC46 /* String+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; 097C003529AB8270008CAEF3 /* MissionListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionListCollectionViewCell.swift; sourceTree = ""; }; 0982DE5329ADCCE000D933D2 /* HomeEmptyCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeEmptyCollectionViewCell.swift; sourceTree = ""; }; @@ -1039,6 +1041,7 @@ isa = PBXGroup; children = ( 6CF4705A29A68EA9008D145C /* URLConstant.swift */, + 0964BA492B0F6BFB00A8984B /* AuthInterceptor.swift */, 6CF4705C29A68F5C008D145C /* NetworkLoggerPlugin.swift */, 6CF4705E29A69025008D145C /* GeneralResponse.swift */, 6CF4706029A69096008D145C /* NetworkConstant.swift */, @@ -1307,6 +1310,7 @@ 09582B4829BDA7F600EF3207 /* DetailStackView.swift in Sources */, 09F6718429CADB1100708725 /* OnboardingModel.swift in Sources */, 3B027A7A299C31B500BEB65C /* SceneDelegate.swift in Sources */, + 0964BA4A2B0F6BFB00A8984B /* AuthInterceptor.swift in Sources */, 09582B5129C0BC3600EF3207 /* DetailAchievementViewController.swift in Sources */, 0982DE5829AE40FB00D933D2 /* UITabBar+.swift in Sources */, 3BC1A27929C9BE6C0088376B /* AddMissionFooterCollectionReusableView.swift in Sources */, diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift index 8754a855..1ec660b2 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift @@ -13,7 +13,7 @@ final class AchieveAPI { static let shared: AchieveAPI = AchieveAPI() - private let achieveProvider = MoyaProvider(plugins: [MoyaLoggingPlugin()]) + private let achieveProvider = MoyaProvider(session: Session(interceptor: AuthInterceptor.shared), plugins: [MoyaLoggingPlugin()]) private init() { } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/AddMission/AddMissionAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/AddMission/AddMissionAPI.swift index 36e10eaa..d2529488 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/AddMission/AddMissionAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/AddMission/AddMissionAPI.swift @@ -13,7 +13,7 @@ final class AddMissionAPI { static let shared: AddMissionAPI = AddMissionAPI() - private let addMissionProvider = MoyaProvider(plugins: [MoyaLoggingPlugin()]) + private let addMissionProvider = MoyaProvider(session: Session(interceptor: AuthInterceptor.shared), plugins: [MoyaLoggingPlugin()]) private init() { } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Auth/AuthAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Auth/AuthAPI.swift index 63361e0c..e9289aa4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Auth/AuthAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Auth/AuthAPI.swift @@ -13,7 +13,7 @@ final class AuthAPI { static let shared: AuthAPI = AuthAPI() - private let authProvider = MoyaProvider(plugins: [MoyaLoggingPlugin()]) + private let authProvider = MoyaProvider(session: Session(interceptor: AuthInterceptor.shared), plugins: [MoyaLoggingPlugin()]) private init() { } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift index f753522b..b9f44fe5 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift @@ -13,7 +13,7 @@ final class HomeAPI { static let shared: HomeAPI = HomeAPI() - var homeProvider = MoyaProvider(plugins: [MoyaLoggingPlugin()]) + var homeProvider = MoyaProvider(session: Session(interceptor: AuthInterceptor.shared), plugins: [MoyaLoggingPlugin()]) private init() { } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Recommend/RecommendAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Recommend/RecommendAPI.swift index 3e95b447..acbfc412 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Recommend/RecommendAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Recommend/RecommendAPI.swift @@ -13,7 +13,7 @@ final class RecommendAPI { static let shared: RecommendAPI = RecommendAPI() - private let recommendProvider = MoyaProvider(plugins: [MoyaLoggingPlugin()]) + private let recommendProvider = MoyaProvider(session: Session(interceptor: AuthInterceptor.shared), plugins: [MoyaLoggingPlugin()]) private init() { } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Recommend/RecommendActionAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Recommend/RecommendActionAPI.swift index 58453cce..7f0ced3b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Recommend/RecommendActionAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Recommend/RecommendActionAPI.swift @@ -13,7 +13,7 @@ final class RecommendActionAPI { static let shared: RecommendActionAPI = RecommendActionAPI() - private let recommendActionProvider = MoyaProvider(plugins: [MoyaLoggingPlugin()]) + private let recommendActionProvider = MoyaProvider(session: Session(interceptor: AuthInterceptor.shared), plugins: [MoyaLoggingPlugin()]) private init() { } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift new file mode 100644 index 00000000..c36cbe3e --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift @@ -0,0 +1,31 @@ +// +// AuthInterceptor.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 11/23/23. +// + +import UIKit + +import Alamofire + +final class AuthInterceptor: RequestInterceptor { + + static let shared = AuthInterceptor() + + private init() {} + + func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { + print("retry") + guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 + else { + completion(.doNotRetry) + return + } + + DispatchQueue.main.async { + let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as! SceneDelegate + sceneDelegate.window?.rootViewController = UINavigationController(rootViewController: AuthViewController()) + } + } +} From 3c28afd1df236e616c7e4d41b8332bf55ec39e6f Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Fri, 24 Nov 2023 01:00:42 +0900 Subject: [PATCH 28/42] =?UTF-8?q?[Feat]=20#193=20-=20change=20root=20VC=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/SceneDelegate.swift | 27 ++++++++++++++----- .../Network/Base/AuthInterceptor.swift | 9 +++---- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift b/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift index d9739736..5751950f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift @@ -9,14 +9,15 @@ import UIKit import KakaoSDKAuth class SceneDelegate: UIResponder, UIWindowSceneDelegate { - + var window: UIWindow? + static var shared: SceneDelegate? { UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate } func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.overrideUserInterfaceStyle = UIUserInterfaceStyle.light - + let rootViewController = ValueOnboardingViewController() let navigationController = UINavigationController(rootViewController: rootViewController) navigationController.isNavigationBarHidden = true @@ -33,19 +34,31 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } } - + func sceneDidDisconnect(_ scene: UIScene) { } - + func sceneDidBecomeActive(_ scene: UIScene) { } - + func sceneWillResignActive(_ scene: UIScene) { } - + func sceneWillEnterForeground(_ scene: UIScene) { } - + func sceneDidEnterBackground(_ scene: UIScene) { } } + +extension SceneDelegate { + + func changeRootViewControllerTo(_ viewController: UIViewController) { + guard let window = window else { return } + + let rootViewController = viewController + let navigationController = UINavigationController(rootViewController: rootViewController) + navigationController.isNavigationBarHidden = true + window.rootViewController = navigationController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift index c36cbe3e..64f21c98 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift @@ -5,7 +5,7 @@ // Created by JEONGEUN KIM on 11/23/23. // -import UIKit +import Foundation import Alamofire @@ -16,7 +16,7 @@ final class AuthInterceptor: RequestInterceptor { private init() {} func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { - print("retry") + guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 else { completion(.doNotRetry) @@ -24,8 +24,7 @@ final class AuthInterceptor: RequestInterceptor { } DispatchQueue.main.async { - let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as! SceneDelegate - sceneDelegate.window?.rootViewController = UINavigationController(rootViewController: AuthViewController()) - } + SceneDelegate.shared?.changeRootViewControllerTo(AuthViewController()) + } } } From db4086c8edbb9e35cae5943a247ab9768f3c2de5 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 16:48:24 +0900 Subject: [PATCH 29/42] =?UTF-8?q?[Fix]=20#193=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift index f7c39585..fc6ff0c3 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkLoggerPlugin.swift @@ -5,7 +5,7 @@ // Created by 김민서 on 2023/02/23. // -import UIKit +import Foundation import Moya From beb03395d9d965b62a188749104f7a2d1fb2f76e Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 16:59:21 +0900 Subject: [PATCH 30/42] =?UTF-8?q?[Fix]=20#198=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift index 57e28a4b..7408ef1f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift @@ -32,7 +32,7 @@ final class HomeAPI { case .success(let response): do { self.missionDailyData = try response.map(GeneralArrayResponse?.self) - guard let missionDailtData = self.missionDailyData else { return } + guard let missionDailtData = self.missionDailyData else { return completion(nil) } completion(missionDailtData) } catch let err { print(err.localizedDescription, 500) @@ -50,7 +50,7 @@ final class HomeAPI { case .success(let response): do { self.missionWeekly = try response.map(GeneralArrayResponse?.self) - guard let missionWeekly = self.missionWeekly else { return } + guard let missionWeekly = self.missionWeekly else { return completion(nil) } completion(missionWeekly) } catch let err { print(err.localizedDescription, 500) @@ -120,7 +120,7 @@ final class HomeAPI { case .success(let response): do { self.updateMissionStatus = try response.map(GeneralResponse?.self) - guard self.updateMissionStatus != nil else { return } + guard self.updateMissionStatus != nil else { return completion(nil) } completion(self.updateMissionStatus) } catch let err { print(err.localizedDescription, 500) @@ -140,7 +140,7 @@ final class HomeAPI { case .success(let response): do { self.addAnotherDay = try response.map(GeneralResponse?.self) - guard let addAnotherDay = self.addAnotherDay else { return } + guard let addAnotherDay = self.addAnotherDay else { return completion(nil) } completion(addAnotherDay) } catch let err { print(err.localizedDescription, 500) From dabac045bee12a6737220eda0bb60201ed96ec0e Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 17:00:16 +0900 Subject: [PATCH 31/42] =?UTF-8?q?[Fix]=20#198=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewControllers/DetailAchievementViewController.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift index 0543ad10..56cb9f3d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift @@ -148,7 +148,8 @@ extension DetailAchievementViewController { extension DetailAchievementViewController { private func requestDetailAPI(date: String) { - HomeAPI.shared.getDailyMission(date: date) { response in + HomeAPI.shared.getDailyMission(date: date) { [weak self] response in + guard let self else { return } guard let response = response else { return } guard let data = response.data else { return } let missionList = data From 5bc7cdb1519a0fab5367b57d7886ce8aaa4854f7 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 17:00:54 +0900 Subject: [PATCH 32/42] =?UTF-8?q?[Fix]=20#198=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Common/Calendar/CalendarView.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift index 4f9f6c44..5c9a0a57 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift @@ -25,13 +25,13 @@ final class CalendarView: UIView { var delegate: CalendarViewDelegate? // MARK: - UI Components - + private let yearMonthLabel = UILabel() let todayButton = UIButton(configuration: .filled()) private let horizonStackView = UIStackView() private let leftButton = UIButton() private let rightButton = UIButton() - var calendar = WeekMonthFSCalendar() + var calendar = WeekMonthFSCalendar() // MARK: - Life Cycle @@ -163,7 +163,7 @@ extension CalendarView { } extension CalendarView { - + @objc func prevBtnTapped(_sender: UIButton) { scrollCurrentPage(calendar: calendar, isPrev: true) @@ -178,7 +178,7 @@ extension CalendarView { func todayBtnTapped(_sender: UIButton) { delegate?.todayBtnTapped() } - + } extension CalendarView { @@ -210,5 +210,4 @@ extension CalendarView { $0.bottom.equalToSuperview().inset(45) } } - } From c6f899937dd12309588e07ec1774dca53b9d791e Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 17:01:35 +0900 Subject: [PATCH 33/42] =?UTF-8?q?[Fix]=20#198=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81=204?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/ViewControllers/HomeDataSource.swift | 6 +++--- .../Home/ViewControllers/HomeViewController.swift | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift index e22077bc..4060b807 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift @@ -111,13 +111,13 @@ final class HomeDataSource { snapshot.appendItems([.empty], toSection: .empty) } - func updateSnapShot(missioList: [DailyMissionResponseDTO]) { + func updateSnapShot(missionList: [DailyMissionResponseDTO]) { - self.missionList = missioList + self.missionList = missionList guard var snapshot = dataSource?.snapshot() else { return } let newSections: [Sections] = self.missionList.isEmpty ? [.empty] : [.mission] - let item: [Item] = self.missionList.isEmpty ? [.empty] : missioList.map { .mission($0) } + let item: [Item] = self.missionList.isEmpty ? [.empty] : missionList.map { .mission($0) } snapshot.deleteSections(currentSection) snapshot.appendSections(newSections) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index 63aa0f15..4e2755b0 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -180,7 +180,7 @@ extension HomeViewController: UICollectionViewDelegate { self.dailyLoadData() self.weeklyLoadData() - self.missionDataSource.updateSnapShot(missioList: self.missionList) + self.missionDataSource.updateSnapShot(missionList: self.missionList) } modalViewController.moveDateClosure = { [weak self] date in @@ -256,7 +256,7 @@ extension HomeViewController { guard let self, let response = response, let data = response.data else { return } self.missionList = data - self.missionDataSource.updateSnapShot(missioList: data) + self.missionDataSource.updateSnapShot(missionList: data) } } @@ -280,7 +280,7 @@ extension HomeViewController { if let index = self.missionList.firstIndex(where: { $0.id == id }) { self.missionList[index] = data self.weeklyLoadData() - self.missionDataSource.updateSnapShot(missioList: self.missionList) + self.missionDataSource.updateSnapShot(missionList: self.missionList) } } } @@ -291,7 +291,7 @@ extension HomeViewController { self.dailyLoadData() self.weeklyLoadData() - self.missionDataSource.updateSnapShot(missioList: self.missionList) + self.missionDataSource.updateSnapShot(missionList: self.missionList) let data = self.missionList[index] AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "home", From 084de48d61d79a43dc0b1f635c8138e2e33867f7 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 17:08:01 +0900 Subject: [PATCH 34/42] =?UTF-8?q?[Fix]=20#198=20-=20CoingKeys=20=EB=B0=8F?= =?UTF-8?q?=20=EB=A6=B0=ED=8A=B8=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/DataModel/Home/DailyMissionResponseDTO.swift | 7 +------ .../Presentation/Common/Calendar/CalendarView.swift | 5 ++++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift index f031d03f..01d2cb30 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift @@ -22,15 +22,10 @@ struct DailyMissionResponseDTO: Codable, Hashable { let completionStatus: CompletionStatus enum CodingKeys: String, CodingKey { - - case id = "id" - case title = "title" - case situationName = "situationName" - case completionStatus = "completionStatus" + case id, title, situationName, completionStatus } static func == (lhs: DailyMissionResponseDTO, rhs: DailyMissionResponseDTO) -> Bool { lhs.uuid == rhs.uuid } - } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift index 5c9a0a57..026036ad 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift @@ -71,11 +71,14 @@ extension CalendarView { config.image = .icBackToday config.title = I18N.todayButton config.imagePadding = 2 - config.contentInsets = NSDirectionalEdgeInsets.init(top: 3, leading: 6, bottom: 2, trailing:7) config.cornerStyle = .capsule config.attributedTitle?.font = .Pretendard(.regular, size: 14) config.baseBackgroundColor = .gray2 config.baseForegroundColor = .gray5 + config.contentInsets = NSDirectionalEdgeInsets.init(top: 3, + leading: 6, + bottom: 2, + trailing: 7) $0.configuration = config $0.addTarget(self, action: #selector(todayBtnTapped), for: .touchUpInside) From ae4d34a607a15c8321c095d005ab0332fa8adff3 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 17:25:52 +0900 Subject: [PATCH 35/42] =?UTF-8?q?[Fix]=20#198=20-=20protocol=20type=20?= =?UTF-8?q?=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Common/Calendar/CalendarView.swift | 4 ++-- .../Presentation/Home/ViewControllers/HomeDataSource.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift index 026036ad..7b7c7daf 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift @@ -11,7 +11,7 @@ import FSCalendar import Then import SnapKit -protocol CalendarViewDelegate { +protocol CalendarViewDelegate: AnyObject { func todayBtnTapped() } @@ -22,7 +22,7 @@ final class CalendarView: UIView { let today = Date() var monthCalendarClosure: ((_ month: String) -> Void)? - var delegate: CalendarViewDelegate? + weak var delegate: CalendarViewDelegate? // MARK: - UI Components diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift index 4060b807..2b68d6ed 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift @@ -7,7 +7,7 @@ import UIKit -protocol HomeModalDelegate { +protocol HomeModalDelegate: AnyObject { func updateMissionStatus(id: Int, status: CompletionStatus) func modifyMission(id: Int, type: MissionType) @@ -37,7 +37,7 @@ final class HomeDataSource { private var missionList: [DailyMissionResponseDTO] var dataSource: DataSource? - var modalDelegate: HomeModalDelegate? + weak var modalDelegate: HomeModalDelegate? // MARK: - UI Components From b829423ed54f022f757bfa9a778e287d6a2951b4 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 17:42:24 +0900 Subject: [PATCH 36/42] =?UTF-8?q?[Fix]=20#199=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AchievementViewController.swift | 7 +------ .../Presentation/Common/MissionCalendarCell.swift | 13 +++++++++---- .../Home/ViewControllers/HomeViewController.swift | 14 +++++--------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index d4499845..26c12c70 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -170,12 +170,7 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell guard let percentage = getPercentage(for: date) else { return cell } - - switch percentage { - case 0.0: cell.configure(.none, .week) - case 1.0: cell.configure(.rateFull, .week) - default: cell.configure(.rateHalf, .week) - } + cell.configure(percent: percentage) return cell } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift index 962ca16b..86f4ae4f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift @@ -49,7 +49,6 @@ final class MissionCalendarCell: FSCalendarCell { // MARK: - UI Components private let iconView = UIImageView() - private let padding = 8 // MARK: - Life Cycle @@ -111,8 +110,14 @@ extension MissionCalendarCell { iconView.image = state.icon } - func configure(_ state: ToDoState, _ mode: FSCalendarScope) { - self.mode = mode - self.state = state + func configure(percent: Float) { + switch percent { + case 0.0: + self.state = .none + case 1.0: + self.state = .rateFull + default: + self.state = .rateHalf + } } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index 8d75533a..0c472523 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -343,16 +343,12 @@ extension HomeViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalend } func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { + let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell - guard let count = self.count else { return cell } - let dateString = Utils.dateFormatterString(format: nil, date: date) - if let percentage = self.calendarDataSource[dateString] { - switch (count, percentage) { - case (_, 1.0): cell.configure(.rateFull, .week) - case (_, 0.0): cell.configure(.none, .week) - case (2, 0.5), (3, 0.0..<1.0), (_, _): cell.configure(.rateHalf, .week) - } - } + +// guard let percentage = getPercentage(for: date) else { return cell } +// cell.configure(percent: percentage) +// return cell } } From 54640d87348d9cf3ec8a6309bd392e72997fd4bd Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 17:48:59 +0900 Subject: [PATCH 37/42] =?UTF-8?q?[Fix]=20#199=20-=20=EB=84=A4=ED=8A=B8?= =?UTF-8?q?=EC=9B=8C=ED=81=AC=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift index b9cd3053..bef5eca0 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift @@ -45,7 +45,7 @@ final class AchieveAPI { case .success(let response): do { self.achieveCalendarData = try response.map(GeneralArrayResponse?.self) - guard let achieveCalendarData = self.achieveCalendarData else { return } + guard let achieveCalendarData = self.achieveCalendarData else { return completion(nil) } completion(achieveCalendarData) } catch let err { print(err.localizedDescription, 500) From fe0b6ffb50f7454eab9f6de9d9e2da751a7b536f Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 17:51:33 +0900 Subject: [PATCH 38/42] =?UTF-8?q?[Fix]=20#197=20-=20=EB=84=A4=ED=8A=B8?= =?UTF-8?q?=EC=9B=8C=ED=81=AC=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift index 28c56828..240ca4e8 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift @@ -61,7 +61,7 @@ final class HomeAPI { case .success(let response): do { self.missionDetailDailyData = try response.map(GeneralResponse?.self) - guard let missionDetailDailyData = self.missionDetailDailyData else { return } + guard let missionDetailDailyData = self.missionDetailDailyData else { return completion(nil) } completion(missionDetailDailyData) } catch let err { print(err.localizedDescription, 500) From 94bb6836545a4f25c4615604bd4da7127bd7bdbb Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 19:35:03 +0900 Subject: [PATCH 39/42] =?UTF-8?q?[Add]=20#197=20-=20=EB=88=84=EB=9D=BD?= =?UTF-8?q?=EB=90=9C=20=EC=95=B0=ED=94=8C=EB=A6=AC=ED=8A=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MissionDetailViewController.swift | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift index 0c0bc305..164e6e7b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift @@ -43,7 +43,7 @@ final class MissionDetailViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - + guard let id = self.userId else { return } requestDailyMissionAPI(id: id) } @@ -207,7 +207,11 @@ extension MissionDetailViewController { func deleteBtnTapped() { guard let data = detailModel else { return } - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "detail", title: data.title, situation: data.situation, goal: data.goal, action: [data.actions[0].name])) + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "detail", + title: data.title, + situation: data.situation, + goal: data.goal, + action: data.actions.map { $0.name })) let modalViewController = HomeDeleteViewController() modalViewController.modalPresentationStyle = .overFullScreen @@ -231,6 +235,11 @@ extension MissionDetailViewController { self.detailModel = data self.setSnapShot() + + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.appearDetailMission(title: data.title, + situation: data.situation, + goal: data.goal, + action: data.actions.map { $0.name })) } } From 2db518aa7169d7cc36e6a6d46c868b55832c9a26 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 19:36:17 +0900 Subject: [PATCH 40/42] =?UTF-8?q?[Fix]=20#197=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Common/StackView/DetailStackView.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/StackView/DetailStackView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/StackView/DetailStackView.swift index cdc7d5b0..e268dad2 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/StackView/DetailStackView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/StackView/DetailStackView.swift @@ -30,7 +30,11 @@ class DetailStackView: UIView { fatalError("init(coder:) has not been implemented") } } + +// MARK: - Methods + extension DetailStackView { + private func setUI(empty: UIImage) { verticalStackView.do { $0.addArrangedSubviews(tagLabel, titleLabel, emptyIcon) @@ -48,21 +52,25 @@ extension DetailStackView { $0.font = .Pretendard(.medium, size: 16) $0.numberOfLines = 0 } + lineView.do { $0.backgroundColor = .gray5 } + emptyIcon.do { $0.contentMode = .scaleAspectFit $0.isHidden = true $0.image = empty } } + private func setLayout(isTop: Bool) { addSubviews(verticalStackView, lineView) verticalStackView.snp.makeConstraints { $0.edges.equalToSuperview() } + lineView.snp.makeConstraints { if isTop { $0.top.equalTo(verticalStackView.snp.top).offset(-25) From 904f325c7cd87ddbbfe438f507870d8865ee91cf Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 19:36:40 +0900 Subject: [PATCH 41/42] =?UTF-8?q?[Fix]=20#197=20-=20mission=20detail=20cel?= =?UTF-8?q?l=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MissionDetailCollectionViewCell.swift | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift index 2b187d29..ca1475b1 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift @@ -13,9 +13,9 @@ import SnapKit final class MissionDetailCollectionViewCell: UICollectionViewCell { // MARK: - Properties - + static let identifier = "MissionDetailCollectionViewCell" - + // MARK: - UI Components private let missionTagLabel = PaddingLabel(padding: UIEdgeInsets(top: 4, left: 12, bottom: 4, right: 12)) @@ -26,9 +26,9 @@ final class MissionDetailCollectionViewCell: UICollectionViewCell { private let verticalStackView = UIStackView() private let action = DetailStackView(tag: I18N.detailAction, isTop: true, empty: .actionEmpty) private let goal = DetailStackView(tag: I18N.detailGoal, isTop: false, empty: .goalEmpty) - + // MARK: - Life Cycle - + override init(frame: CGRect) { super.init(frame: .zero) setUI() @@ -43,6 +43,7 @@ final class MissionDetailCollectionViewCell: UICollectionViewCell { // MARK: - Methods extension MissionDetailCollectionViewCell { + private func setUI() { backgroundColor = .clear @@ -84,7 +85,7 @@ extension MissionDetailCollectionViewCell { $0.axis = .vertical $0.spacing = 22 } - + } private func setLayout() { @@ -130,11 +131,13 @@ extension MissionDetailCollectionViewCell { missionLabel.setLineSpacing(lineSpacing: 6.0) accumulateLabel.text = "\(model.count)회\n달성" - if model.actions.isEmpty || model.actions.contains(where: { $0.name.isEmpty }) { + let nonEmptyActions = model.actions.filter { !$0.name.isEmpty } + + if nonEmptyActions.isEmpty { action.titleLabel.isHidden = true action.emptyIcon.isHidden = false } else { - let actionNames = model.actions.map { $0.name } + let actionNames = nonEmptyActions.map { $0.name } let joinedActionNames = actionNames.joined(separator: "\n") action.titleLabel.text = joinedActionNames action.titleLabel.setLineSpacing(lineSpacing: 7.0) @@ -145,7 +148,7 @@ extension MissionDetailCollectionViewCell { if model.goal.isEmpty { goal.titleLabel.isHidden = true goal.emptyIcon.isHidden = false - + } else { goal.verticalStackView.removeArrangedSubview(action.emptyIcon) goal.emptyIcon.removeFromSuperview() From 50147480c43f8f203ffc7014962aa328caf1d771 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 19:36:56 +0900 Subject: [PATCH 42/42] =?UTF-8?q?[Fix]=20#197=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AddMissionViewController.swift | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift index 574aecd6..295494a4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift @@ -320,14 +320,17 @@ extension AddMissionViewController { private func requestDailyMissionAPI(id: Int) { HomeAPI.shared.getDailyDetailMission(id: id) { [weak self] response in - guard let self else { return } - guard let response = response else { return } - guard let data = response.data else { return } - self.nottodoInfoList[1] = data.title - self.nottodoInfoList[2] = data.situation - self.nottodoInfoList[3] = data.actions.first!.name - self.nottodoInfoList[4] = data.goal - self.addMissionCollectionView.reloadData() + guard let self = self, let response = response else { return } + + if let data = response.data { + self.nottodoInfoList[1] = data.title + self.nottodoInfoList[2] = data.situation + self.nottodoInfoList[3] = data.actions.first?.name ?? "" + self.nottodoInfoList[4] = data.goal + self.addMissionCollectionView.reloadData() + } else { + self.nottodoInfoList = ["", "", "", ""] + } } } @@ -366,7 +369,7 @@ extension AddMissionViewController: UICollectionViewDataSource { } missionMenuCell.setCellData([currentCellInfo]) } - + if let missionDateCell = cell as? DateCollectionViewCell { missionDateCell.setDateList(dateList) }