From 527c76aec7e372a489da8b5bb2f0e5ee6d42f7d4 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:22:40 +0900 Subject: [PATCH 01/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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/56] =?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 110cbf83279b9d862c2644c381ded06d99bdcff2 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Thu, 23 Nov 2023 17:42:54 +0900 Subject: [PATCH 25/56] =?UTF-8?q?[Add]=20#206=20-=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO.xcodeproj/project.pbxproj | 20 +++++++++++++++ .../Global/Extensions/Design/UIImage+.swift | 2 ++ .../NotificationDialogViewController.swift | 19 +++++++++++++++ .../IC/ic_bell.imageset/Contents.json | 23 ++++++++++++++++++ .../IC/ic_bell.imageset/ic_bell.png | Bin 0 -> 1695 bytes .../IC/ic_bell.imageset/ic_bell@2x.png | Bin 0 -> 3197 bytes .../IC/ic_bell.imageset/ic_bell@3x.png | Bin 0 -> 4747 bytes .../NotificationDialog.imageset/Contents.json | 23 ++++++++++++++++++ .../NotificationDialog.png | Bin 0 -> 3255 bytes .../NotificationDialog@2x.png | Bin 0 -> 8712 bytes .../NotificationDialog@3x.png | Bin 0 -> 15333 bytes 11 files changed, 87 insertions(+) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/ic_bell.imageset/Contents.json create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/ic_bell.imageset/ic_bell.png create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/ic_bell.imageset/ic_bell@2x.png create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/ic_bell.imageset/ic_bell@3x.png create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/modal/NotificationDialog.imageset/Contents.json create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/modal/NotificationDialog.imageset/NotificationDialog.png create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/modal/NotificationDialog.imageset/NotificationDialog@2x.png create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/modal/NotificationDialog.imageset/NotificationDialog@3x.png diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 92673262..8fda81c6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -72,6 +72,7 @@ 3B027AA2299C355800BEB65C /* AchievementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B027AA1299C355800BEB65C /* AchievementViewController.swift */; }; 3B027AA4299C357000BEB65C /* MyInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B027AA3299C357000BEB65C /* MyInfoViewController.swift */; }; 3B027AAC299C35E500BEB65C /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3B027AAB299C35E500BEB65C /* Colors.xcassets */; }; + 3B03D0D62B0F15AA00302872 /* NotificationDialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B03D0D52B0F15AA00302872 /* NotificationDialogViewController.swift */; }; 3B0CBA222A45FC170004F2DB /* UpdateMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B0CBA212A45FC170004F2DB /* UpdateMissionResponseDTO.swift */; }; 3B0CBA242A461B1C0004F2DB /* RecentMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B0CBA232A461B1C0004F2DB /* RecentMissionResponseDTO.swift */; }; 3B11740D2A4B574B0033DDF3 /* CALayer+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B11740C2A4B574B0033DDF3 /* CALayer+.swift */; }; @@ -231,6 +232,7 @@ 3B027AA1299C355800BEB65C /* AchievementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchievementViewController.swift; sourceTree = ""; }; 3B027AA3299C357000BEB65C /* MyInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoViewController.swift; sourceTree = ""; }; 3B027AAB299C35E500BEB65C /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; + 3B03D0D52B0F15AA00302872 /* NotificationDialogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationDialogViewController.swift; sourceTree = ""; }; 3B0CBA212A45FC170004F2DB /* UpdateMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateMissionResponseDTO.swift; sourceTree = ""; }; 3B0CBA232A461B1C0004F2DB /* RecentMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentMissionResponseDTO.swift; sourceTree = ""; }; 3B11740C2A4B574B0033DDF3 /* CALayer+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+.swift"; sourceTree = ""; }; @@ -680,6 +682,7 @@ 3B027A97299C343D00BEB65C /* Presentation */ = { isa = PBXGroup; children = ( + 3B03D0D32B0F157700302872 /* NotificatoinDialog */, 09F6717E29CAD68100708725 /* Onboarding */, 3B027A9B299C348800BEB65C /* Achievement */, 3B027A9C299C349A00BEB65C /* AddMission */, @@ -803,6 +806,22 @@ path = Assets; sourceTree = ""; }; + 3B03D0D32B0F157700302872 /* NotificatoinDialog */ = { + isa = PBXGroup; + children = ( + 3B03D0D42B0F159100302872 /* ViewControllers */, + ); + path = NotificatoinDialog; + sourceTree = ""; + }; + 3B03D0D42B0F159100302872 /* ViewControllers */ = { + isa = PBXGroup; + children = ( + 3B03D0D52B0F15AA00302872 /* NotificationDialogViewController.swift */, + ); + path = ViewControllers; + sourceTree = ""; + }; 3B3C89DB29C0EF6A00B1D56D /* Models */ = { isa = PBXGroup; children = ( @@ -1238,6 +1257,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3B03D0D62B0F15AA00302872 /* NotificationDialogViewController.swift in Sources */, 09F6719729CC81B500708725 /* DetailAchievementCollectionViewCell.swift in Sources */, 0960C0D62A38BC8100A3D8DB /* DefaultKeys.swift in Sources */, 3B14A14129A6FDA900F92897 /* UILabel+.swift in Sources */, diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIImage+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIImage+.swift index 05e437f7..dad99f3a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIImage+.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIImage+.swift @@ -56,6 +56,7 @@ extension UIImage { static var icSNS: UIImage { UIImage(named: "ic_snsmessage")! } static var icTrashbin: UIImage { UIImage(named: "ic_trashbin")! } static var icToastError: UIImage { UIImage(named: "ic_toast_error")! } + static var icBell: UIImage { UIImage(named: "ic_bell")! } // image @@ -110,4 +111,5 @@ extension UIImage { static var quit1: UIImage { UIImage(named: "img_quit1")! } static var quit2: UIImage { UIImage(named: "img_quit2")! } + static var notificationDialog: UIImage { UIImage(named: "NotificationDialog")! } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift new file mode 100644 index 00000000..350b3563 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift @@ -0,0 +1,19 @@ +// +// NotificationDialogViewController.swift +// iOS-NOTTODO +// +// Created by 강윤서 on 11/23/23. +// + +import UIKit + +import SnapKit +import Then + +final class NotificationDialogViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + } + +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/ic_bell.imageset/Contents.json b/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/ic_bell.imageset/Contents.json new file mode 100644 index 00000000..b6f326b3 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/ic_bell.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "ic_bell.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "ic_bell@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "ic_bell@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/ic_bell.imageset/ic_bell.png b/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/ic_bell.imageset/ic_bell.png new file mode 100644 index 0000000000000000000000000000000000000000..61a85597f09a294f7bb94f52e8e43f21f4ea008c GIT binary patch literal 1695 zcmV;Q24MM#P)?IEDNg`AL6WOVfMoInrBX$57xxKb zo`6jgO4XaDm$i52^o)s9L`{tG?3vxk`6RG0@*4KvbLO8jJ2L=^q9}@@D2k#eilQir zq9}@@D2k#eisGEs?^}nhqn$%gW}*gSP;&S6cJr6dSG(Yd_Hifa=Yu9fd;^f?`70p; z`#6t)>B+}pN}b?%a(r~B9M??MHLz!6{nw$r|IwEb7>czgZq@Akf0dCMpHiG6Mm+b{+8 zcvff*o(-2mSvc65tXRjkr>!?z%TRKhrNJ2Kt+6MQPwJhcouuB6*Q`jR-lzvX#98Isz>o=S<|D_H24iiS6(xZxXnoT(CJ(hFJ9&!A4VsO z4qm`CGPL5-2)V*}n|1s6$0VZo#Yiy9VeQ!$-|b!Gy~DO?E`P*V`ZD+?f{p!!)lDc7 zg%}Kb(*LPvkGTO~vOizMRGbKvz2K(}C<`SM%$dPBjcu!*U0+@1RJO@i!`qzjgT|ng zLRmN!k26fZ5V7u);0ni2oHnO&iINbSwP&Aw{}?>uM6A1iY(3nt>#p~&t$y(MZ8jr)Q>#eLklk%RzInPkB8{7V7STnDn;3b&ZvQlEIbCwqI-D32$vyRnp&Ce5S)f+~6c^`r~L^h6?ho z80`fwV)dc6bkb?A9=4unhkC#RUc{NDT38}aD!kOrx=m5QlAx&^|Biq(Fz)o?w4=no1zvIY@0Asps!A9oTEL{uy?m_R@v z<)WhmqGDl0Hy|L;KobP<)6WV8GzkbIn+ENTDM5jvj!+|OHbhN^iZvsWCPUU>n8@3} zT~SDy3|X-dQV0x5hbt-TAvUnKQSCet+&j4Y9H5o|j zMNf70QyUD>WSpAn+G!V*61|8OqRAM+!$8Y@lfU37b8b33Ro#O$8K)RV*~>e}J4@gY zFJgsiGR{JVc3no8w^<~1&5UqvOZyP+=5_fSd<@cLjDwQFa0P*z=Nr+v@;Z@9SO<9B) zhjj;aeY4$;UeDyoJ)jBA8HaQloNDs-$1Kb^M8z5|AbbvG5N5dU;U|LxGi-8==msGV)e{`- zU%Ow5N$X-_+2vSA%deMG7@$TUvIY$Bo&BLp-`yc<4;GW#p0`eg9rIiC<=lLZ5|*+@2~GHNqBg^EhwjF*WSA8}>CaT%5Jl{y}}OtT)FfilQir pq9}@@D2k#eilQirq9{t$_!o~i(%7i6hMWKZ002ovPDHLkV1i5AAj$v$ literal 0 HcmV?d00001 diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/ic_bell.imageset/ic_bell@2x.png b/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/ic_bell.imageset/ic_bell@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5ee09c8c8c0b6550331136d4dce466877edac52a GIT binary patch literal 3197 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 Pp8+gQZB1Sn5$^pD_3`>i literal 0 HcmV?d00001 diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/modal/NotificationDialog.imageset/NotificationDialog@2x.png b/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/modal/NotificationDialog.imageset/NotificationDialog@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f0362a5ceed6c0cf0b53272f72560e14aa7498d7 GIT binary patch literal 8712 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 literal 0 HcmV?d00001 diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/modal/NotificationDialog.imageset/NotificationDialog@3x.png b/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/modal/NotificationDialog.imageset/NotificationDialog@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..98c13a39bda5be7db0ec8a3b62885cde876085c3 GIT binary patch literal 15333 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 literal 0 HcmV?d00001 From 44f48613077e401486ff28acf1057787bd2332da Mon Sep 17 00:00:00 2001 From: hyesuuou <68391767+hyesuuou@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:18:08 +0900 Subject: [PATCH 26/56] =?UTF-8?q?[Feat]=20#194=20-=20=ED=86=A0=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=86=8C=EB=A9=B8=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=8B=9C=EA=B0=84=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 --- .../Global/Extensions/UIViewController+.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIViewController+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIViewController+.swift index 4aacea07..708b738d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIViewController+.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIViewController+.swift @@ -54,10 +54,14 @@ extension UIViewController { $0.height.equalTo(61) } + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(deleteToast(_:))) + toastView.addGestureRecognizer(tapGestureRecognizer) + toastView.isUserInteractionEnabled = true + UIView.animate(withDuration: 0.4, delay: 0.0, options: .curveEaseIn) { toastView.alpha = 1.0 } completion: { _ in - UIView.animate(withDuration: 0.4, delay: 1.0, options: .curveEaseOut) { + UIView.animate(withDuration: 0.4, delay: 1.5, options: .curveEaseOut) { toastView.alpha = 0.0 } completion: { _ in toastView.removeFromSuperview() @@ -65,6 +69,11 @@ extension UIViewController { } } + @objc + private func deleteToast(_ sender: UITapGestureRecognizer) { + sender.view?.removeFromSuperview() + } + /// html을 string으로 변환하는 메소드 func htmlToString(_ targetString: String) -> NSAttributedString? { let text = targetString From 56c276fb9f5a0aad1d3c47ef442390a6d73be4b7 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Thu, 23 Nov 2023 19:31:32 +0900 Subject: [PATCH 27/56] =?UTF-8?q?[Design]=20#206=20-=20notificationDialog?= =?UTF-8?q?=20=EB=B7=B0=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 + .../iOS-NOTTODO/Application/AppDelegate.swift | 2 +- .../Global/Extensions/CGSize+.swift | 29 ++++ .../Global/Extensions/Design/UIColor+.swift | 4 + .../Global/Extensions/Design/UIImage+.swift | 1 + .../iOS-NOTTODO/Global/Literals/Strings.swift | 17 +- .../NotificationDialogViewController.swift | 164 ++++++++++++++++++ .../IC/acceptCircle.imageset/Contents.json | 23 +++ .../IC/acceptCircle.imageset/acceptCircle.png | Bin 0 -> 1946 bytes .../acceptCircle.imageset/acceptCircle@2x.png | Bin 0 -> 3879 bytes .../acceptCircle.imageset/acceptCircle@3x.png | Bin 0 -> 5704 bytes .../NotiBlack.colorset/Contents.json | 38 ++++ .../notiBg.colorset/Contents.json | 38 ++++ .../notiBlue.colorset/Contents.json | 38 ++++ .../notiGreen.colorset/Contents.json | 38 ++++ 15 files changed, 394 insertions(+), 2 deletions(-) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/CGSize+.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/acceptCircle.imageset/Contents.json create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/acceptCircle.imageset/acceptCircle.png create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/acceptCircle.imageset/acceptCircle@2x.png create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/acceptCircle.imageset/acceptCircle@3x.png create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/NotiBlack.colorset/Contents.json create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/notiBg.colorset/Contents.json create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/notiBlue.colorset/Contents.json create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/notiGreen.colorset/Contents.json diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 8fda81c6..e2cf9cc2 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -73,6 +73,7 @@ 3B027AA4299C357000BEB65C /* MyInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B027AA3299C357000BEB65C /* MyInfoViewController.swift */; }; 3B027AAC299C35E500BEB65C /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3B027AAB299C35E500BEB65C /* Colors.xcassets */; }; 3B03D0D62B0F15AA00302872 /* NotificationDialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B03D0D52B0F15AA00302872 /* NotificationDialogViewController.swift */; }; + 3B03D0D82B0F5EF300302872 /* CGSize+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B03D0D72B0F5EF300302872 /* CGSize+.swift */; }; 3B0CBA222A45FC170004F2DB /* UpdateMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B0CBA212A45FC170004F2DB /* UpdateMissionResponseDTO.swift */; }; 3B0CBA242A461B1C0004F2DB /* RecentMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B0CBA232A461B1C0004F2DB /* RecentMissionResponseDTO.swift */; }; 3B11740D2A4B574B0033DDF3 /* CALayer+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B11740C2A4B574B0033DDF3 /* CALayer+.swift */; }; @@ -233,6 +234,7 @@ 3B027AA3299C357000BEB65C /* MyInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoViewController.swift; sourceTree = ""; }; 3B027AAB299C35E500BEB65C /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; 3B03D0D52B0F15AA00302872 /* NotificationDialogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationDialogViewController.swift; sourceTree = ""; }; + 3B03D0D72B0F5EF300302872 /* CGSize+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGSize+.swift"; sourceTree = ""; }; 3B0CBA212A45FC170004F2DB /* UpdateMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateMissionResponseDTO.swift; sourceTree = ""; }; 3B0CBA232A461B1C0004F2DB /* RecentMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentMissionResponseDTO.swift; sourceTree = ""; }; 3B11740C2A4B574B0033DDF3 /* CALayer+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+.swift"; sourceTree = ""; }; @@ -654,6 +656,7 @@ 097568352A2FEF3F0001EC46 /* String+.swift */, 3B11740C2A4B574B0033DDF3 /* CALayer+.swift */, 0943A9F82A53239200614761 /* Bundle+.swift */, + 3B03D0D72B0F5EF300302872 /* CGSize+.swift */, ); path = Extensions; sourceTree = ""; @@ -1313,6 +1316,7 @@ 098BFD5F29B7AECF008E80F9 /* MyInfoHeaderCollectionReusableView.swift in Sources */, 3B482FA7299EB8FD00BCF424 /* UIViewController+.swift in Sources */, 0960C0D42A38BC6500A3D8DB /* KeychainUtil.swift in Sources */, + 3B03D0D82B0F5EF300302872 /* CGSize+.swift in Sources */, 6CA208302A1925EE001C4247 /* RecommendActionResponseDTO.swift in Sources */, 09F6718629CB26E400708725 /* OnboardingHeaderView.swift in Sources */, 3B11740D2A4B574B0033DDF3 /* CALayer+.swift in Sources */, diff --git a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift index 8e4a9897..9e6685bf 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift @@ -70,7 +70,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { DispatchQueue.main.async { if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let window = windowScene.windows.first { - let tabBarController = TabBarController() + let tabBarController = NotificationDialogViewController() let navigationController = UINavigationController(rootViewController: tabBarController) navigationController.isNavigationBarHidden = true window.rootViewController = navigationController diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/CGSize+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/CGSize+.swift new file mode 100644 index 00000000..2e66925e --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/CGSize+.swift @@ -0,0 +1,29 @@ +// +// CGSize+.swift +// iOS-NOTTODO +// +// Created by 강윤서 on 11/23/23. +// + +import UIKit + +extension CGSize { + + func getDeviceWidth() -> CGFloat { + return UIScreen.main.bounds.width + } + + func getDeviceHeight() -> CGFloat { + return UIScreen.main.bounds.height + } + + /// 아이폰 13 미니(width 375)를 기준으로 레이아웃을 잡고, 기기의 width 사이즈를 곱해 대응 값을 구할 때 사용 + func convertByWidthRatio(_ convert: CGFloat) -> CGFloat { + return (convert / 375) * getDeviceWidth() + } + + /// 아이폰 13 미니(height 812)를 기준으로 레이아웃을 잡고, 기기의 height 사이즈를 곱해 대응 값을 구할 때 사용 + func convertByHeightRatio(_ convert: CGFloat) -> CGFloat { + return (convert / 812) * getDeviceHeight() + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIColor+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIColor+.swift index 1dcc1e5f..b2a316ea 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIColor+.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIColor+.swift @@ -24,4 +24,8 @@ extension UIColor { static let green2 = UIColor(named: "green2") static let systemBlack = UIColor(named: "systemBlack") static let kakaoYellow = UIColor(named: "yellow") + static let notiBlack = UIColor(named: "notiBlack") + static let notiGreen = UIColor(named: "notiGreen") + static let notiBlue = UIColor(named: "notiBlue") + static let notiBg = UIColor(named: "notiBg") } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIImage+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIImage+.swift index dad99f3a..a225eaee 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIImage+.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIImage+.swift @@ -57,6 +57,7 @@ extension UIImage { static var icTrashbin: UIImage { UIImage(named: "ic_trashbin")! } static var icToastError: UIImage { UIImage(named: "ic_toast_error")! } static var icBell: UIImage { UIImage(named: "ic_bell")! } + static var icCircle: UIImage { UIImage(named: "acceptCircle")! } // image diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift index 04be6159..934c13fe 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift @@ -162,7 +162,22 @@ struct I18N { static let delete = "삭제하기" static let deleteModalTitle = "삭제하시겠습니까?" static let deleteModalSubtitle = "한 번 삭제하면 되돌릴 수 없어요." - + static let notiDialogTitle = """ + 알림을 허용하면 + 낫투두가 더 잘 도울 수 있어요! + """ + static let notiAllowTitle = """ + '낫투두'에서 알림을 + 보내고자 합니다. + """ + static let notiAllowSubTitle = """ + 알림 기능이 꺼져있으면 + 알람이 울릴 때까지 확인하기 어려워요 + """ + static let notAllow = "허용 안 함" + static let allow = "허용" + static let notiDialogButton = "네, 알겠어요 :)" + /// home static let subText = "*달성 가능한 계획을 위해 다가올 일주일만 선택할 수 있어요" } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift index 350b3563..6bd1f0c4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift @@ -12,8 +12,172 @@ import Then final class NotificationDialogViewController: UIViewController { + // MARK: - UI Components + + private let bellImage = UIImageView() + private let titleLabel = UILabel() + private let alertView = UIView() + private let bottomButton = UIButton() + private let alertTitleMessageLabel = UILabel() + private let alertSubTitleMessageLabel = UILabel() + private let notAllowButton = UIButton() + private let allowButton = UIButton() + private let buttonStackView = UIStackView() + private let circleImage = UIImageView() + private let backgroundImage = UIImageView() + private let verticalView = UIView() + private let horizontalView = UIView() + + // MARK: - Life Cycle + override func viewDidLoad() { super.viewDidLoad() + setUI() + setLayout() } } + +extension NotificationDialogViewController { + private func setUI() { + view.backgroundColor = .ntdBlack + + bellImage.image = .icBell + circleImage.image = .icCircle + backgroundImage.image = .notificationDialog + + titleLabel.do { + $0.text = I18N.notiDialogTitle + $0.textColor = .white + $0.font = .Pretendard(.semiBold, size: 22) + $0.numberOfLines = 0 + $0.textAlignment = .center + } + + alertTitleMessageLabel.do { + $0.text = I18N.notiAllowTitle + $0.textColor = .notiBlack + $0.font = .Pretendard(.semiBold, size: 18) + $0.numberOfLines = 0 + $0.textAlignment = .center + } + + alertSubTitleMessageLabel.do { + $0.text = I18N.notiAllowSubTitle + $0.textColor = .gray3 + $0.font = .Pretendard(.medium, size: 13) + $0.numberOfLines = 0 + $0.textAlignment = .center + } + + buttonStackView.do { + $0.layer.cornerRadius = 17 + $0.backgroundColor = .none + } + + allowButton.do { + $0.setTitle(I18N.allow, for: .normal) + $0.setTitleColor(.notiBlue, for: .normal) + $0.titleLabel?.font = .Pretendard(.semiBold, size: 18) + } + + notAllowButton.do { + $0.setTitle(I18N.notAllow, for: .normal) + $0.setTitleColor(.gray4, for: .normal) + $0.titleLabel?.font = .Pretendard(.medium, size: 15) + } + + bottomButton.do { + $0.backgroundColor = .white + $0.layer.cornerRadius = 25 + $0.titleLabel?.font = .Pretendard(.semiBold, size: 16) + $0.setTitleColor(.black, for: .normal) + $0.setTitle(I18N.notiDialogButton, for: .normal) + $0.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) + } + + alertView.do { + $0.backgroundColor = .none + $0.layer.cornerRadius = 17 + } + + verticalView.backgroundColor = .notiGreen + horizontalView.backgroundColor = .notiGreen + } + + private func setLayout() { + view.addSubviews(bellImage, titleLabel, alertView, bottomButton, circleImage) + alertView.addSubviews(backgroundImage, alertTitleMessageLabel, alertSubTitleMessageLabel, horizontalView, buttonStackView) + buttonStackView.addArrangedSubviews(notAllowButton, verticalView, allowButton) + + bellImage.snp.makeConstraints { + $0.top.equalTo(view.safeAreaLayoutGuide).inset(convertByHeightRatio(63)) + $0.centerX.equalToSuperview() + $0.height.equalTo(convertByHeightRatio(107)) + } + + titleLabel.snp.makeConstraints { + $0.top.equalTo(bellImage.snp.bottom).offset(convertByHeightRatio(2)) + $0.directionalHorizontalEdges.equalToSuperview().inset(convertByHeightRatio(57)) + $0.height.equalTo(convertByHeightRatio(60)) + } + + bottomButton.snp.makeConstraints { + $0.bottom.equalTo(view.safeAreaLayoutGuide).inset(convertByHeightRatio(44)) + $0.directionalHorizontalEdges.equalToSuperview().inset(convertByHeightRatio(15)) + $0.height.equalTo(convertByHeightRatio(50)) + } + + alertView.snp.makeConstraints { + $0.top.equalTo(titleLabel.snp.bottom).offset(convertByHeightRatio(36)) + $0.directionalHorizontalEdges.equalToSuperview().inset(convertByHeightRatio(46)) + $0.bottom.equalTo(bottomButton.snp.top).inset(convertByHeightRatio(-186)) + } + + backgroundImage.snp.makeConstraints { + $0.center.equalToSuperview() + $0.size.equalToSuperview() + } + + alertTitleMessageLabel.snp.makeConstraints { + $0.top.equalToSuperview().inset(convertByHeightRatio(26)) + $0.centerX.equalToSuperview() + $0.height.equalTo(convertByHeightRatio(48)) + } + + alertSubTitleMessageLabel.snp.makeConstraints { + $0.top.equalTo(alertTitleMessageLabel.snp.bottom).offset(convertByHeightRatio(7)) + $0.directionalHorizontalEdges.equalToSuperview() + $0.height.equalTo(convertByHeightRatio(36)) + } + + buttonStackView.snp.makeConstraints { + $0.directionalHorizontalEdges.bottom.equalToSuperview() + $0.height.equalTo(convertByHeightRatio(48)) + } + + horizontalView.snp.makeConstraints { + $0.directionalHorizontalEdges.equalToSuperview() + $0.height.equalTo(convertByHeightRatio(1)) + $0.bottom.equalTo(buttonStackView.snp.top) + } + + verticalView.snp.makeConstraints { + $0.width.equalTo(convertByHeightRatio(1)) + } + + circleImage.snp.makeConstraints { + $0.center.equalTo(allowButton.snp.center) + $0.size.equalTo(convertByHeightRatio(77)) + } + + allowButton.snp.makeConstraints { + $0.width.equalToSuperview().dividedBy(2) + } + } + + @objc + private func buttonTapped() { + print("버튼 클릭") + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/acceptCircle.imageset/Contents.json b/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/acceptCircle.imageset/Contents.json new file mode 100644 index 00000000..21904c7c --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/acceptCircle.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "acceptCircle.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "acceptCircle@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "acceptCircle@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/acceptCircle.imageset/acceptCircle.png b/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/acceptCircle.imageset/acceptCircle.png new file mode 100644 index 0000000000000000000000000000000000000000..b675dcaccb8356062baf54ce0d7a1aa7babe6ed7 GIT binary patch literal 1946 zcmV;L2W9w)P)+mN81C=hi4YC!?i0sjY3 z^+a_a%YCfV(f`AR!0&Ix%PSZI1dIrW>a_kJTsLbVo+3#NhL|BBI!I(Y0C5J;ZLf+y zeHZ}>B`qdOf_#P~HK1^aHcIQ!s(9Ig0#OKQMAi-bzotN~=Oa;>;cEiB+eJuUuB1Hx zvN-H02zK)%FE`Tm)yW$E*>WdKkWoiN9(+ZTuL7uDN$7UwVZO60UUcGG^h11y@6@J; zYF<~Nn#(8neIdgSIN^!4XQxmpq8Ffe1tiY+{Grq*SF5cl(`qy#qsHXwnYU@dq5K z)(14*K|{QY>bBFXZbgG6AIaNtAa7?yyxM>vkeM`X>~GFhXj81sw&viMjZu=7gbR6i z4Q*-gBCEP)B2C*bc@B2_K{jK#NHxp~ZTBSF+=1_$$<9gFis^)@-8q05J!hUJVbc2(mr4fdPb>lvUbyPS|pV!g#_+Yv06S*!X#Yxi8KRN}LFf#VR(j*W@ zp!e*8Sd4tcBs{X!F72d}7LAge(f7LmOAk#Q)9@ZwM(Djkua@+I`niw&O>1MIdTKvR zF;XNt^x*enTAIlNf2os&8o)W{W2TuKD`}CZf5|mUB0kB4#fU~$P9@3gEZRm?t9t6j z_+Oz6gb~QJP=A<9bv-rY!IHM8PMW|PeziL(M&A{>Kb$+F8Z_E+_ZXyUwN#r-T^y3< zwxEQF`sF9f(LLFu8BnrZngx<3;hxz*usqIbQIVj2QU91rxGZ&JNL$}P5IM2`nD>g- zig?L2S}N?R;}$bl?V!{9xL08MBn^!prrT~?vKZQSg-h~%P~kSDMo5}o z7BN*5rR)eQ2wIwFN*l3?Mv_)*F!%bCHW|+gQNR6Ud90!``gmsEs?MOopq+ZAtU-Te z@0b1xoQPHlLE9`7Ps7iTxI_|wx=dW{;7-l&P;ubW*uTj|3!FJTmMBr4SzVN36(a{Q zJk%Oeu?}tU6{Y}=2LZu?#JT1pqbi6tOhLXGBWdzCsZSsF1KBL)YDQMnSoKs3IC^q6 z&Gb>Kh%R(F;i;3$;U!DyxD!larY0(+i%EQ8gkIpR^q}8A{B`w5^XO;{}p3R%9wK`aCQP03sWH;yznsFc57hCcB~Z2_ zK`gzUpr|}@Erf+I%AW8}yjo0a!)dd47G~KKNpjmm?Et`XxS5cB#{}3@}CIE^`G* zm!30oQzH$sVPtx;YDZ?$V(hEWG~HSoTx;~?x#w_GyDQ@LP_}qz(g->^h}?wKJZ40m zI9e5dw4or78sd)lGki`nEz1`9VPnZ^6~n>K^#UDc_!59T%Y#5o6g)+n)O34(nDTIW ztohS=f#fwsD0NQcL7b@%eJ1kpE|5t4KI6}$?`LM}gL(~a>vYB>O7TkeG()Do`%70} zYmg(f)QR0}p9 zT8t<^8KtSgo$LHEDnpo2SzQ!ORNqr!OrVv98Bm@+1126tIlDbD)Hx)kBf|VY6zCRw gQ+n`dkIQ-f0U#n&-9S#?q5uE@07*qoM6N<$f-$C<4*&oF literal 0 HcmV?d00001 diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/acceptCircle.imageset/acceptCircle@2x.png b/iOS-NOTTODO/iOS-NOTTODO/Resource/Assets/Assets.xcassets/IC/acceptCircle.imageset/acceptCircle@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e1005e7fac99abe1c9b4d3e522ac0f77e4aea717 GIT binary patch literal 3879 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 Date: Thu, 23 Nov 2023 20:40:38 +0900 Subject: [PATCH 28/56] =?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 29/56] =?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 30/56] =?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 456c3222ceb43faab6215866a556267e1a8b1e98 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Thu, 23 Nov 2023 21:10:01 +0900 Subject: [PATCH 31/56] =?UTF-8?q?[Del]=20#206=20-=20=ED=91=B8=EC=8B=9C?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EA=B6=8C=ED=95=9C=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=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/iOS-NOTTODO/Application/AppDelegate.swift | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift index 9e6685bf..b61d4a3d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift @@ -41,12 +41,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // FCM 다시 사용 설정 Messaging.messaging().isAutoInitEnabled = true - - // 푸시 알림 권한 설정 및 푸시 알림에 앱 등록 - UNUserNotificationCenter.current().delegate = self - let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] - UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: { _, _ in }) - + // device token 요청. application.registerForRemoteNotifications() @@ -70,7 +65,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { DispatchQueue.main.async { if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let window = windowScene.windows.first { - let tabBarController = NotificationDialogViewController() + let tabBarController = TabBarController() let navigationController = UINavigationController(rootViewController: tabBarController) navigationController.isNavigationBarHidden = true window.rootViewController = navigationController From c6e3dad23e9d6b5dfdb4d43a94d7c5281b0a11a2 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Thu, 23 Nov 2023 21:10:46 +0900 Subject: [PATCH 32/56] =?UTF-8?q?[Feat]=20#206=20-=20=ED=95=98=EB=8B=A8=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EB=88=84=EB=A5=BC=20=EB=95=8C=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=EB=90=98=EB=8A=94=20=ED=81=B4=EB=A1=9C=EC=A0=80=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 --- .../NotificationDialogViewController.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift index 6bd1f0c4..7934db82 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift @@ -12,6 +12,10 @@ import Then final class NotificationDialogViewController: UIViewController { + // MARK: - Properties + + var buttonHandler: (() -> Void)? + // MARK: - UI Components private let bellImage = UIImageView() @@ -79,12 +83,14 @@ extension NotificationDialogViewController { $0.setTitle(I18N.allow, for: .normal) $0.setTitleColor(.notiBlue, for: .normal) $0.titleLabel?.font = .Pretendard(.semiBold, size: 18) + $0.isEnabled = false } notAllowButton.do { $0.setTitle(I18N.notAllow, for: .normal) $0.setTitleColor(.gray4, for: .normal) $0.titleLabel?.font = .Pretendard(.medium, size: 15) + $0.isEnabled = false } bottomButton.do { @@ -178,6 +184,6 @@ extension NotificationDialogViewController { @objc private func buttonTapped() { - print("버튼 클릭") + buttonHandler?() } } From 04d816e95647cf3eae184f546a200871778ec1e7 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Thu, 23 Nov 2023 21:11:34 +0900 Subject: [PATCH 33/56] =?UTF-8?q?[Feat]=20#206=20-=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=EC=9A=94=EC=B2=AD=20=ED=9B=84=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Auth/AuthViewController.swift | 54 ++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift index 8ecd3eb4..be12b16d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift @@ -241,7 +241,7 @@ extension AuthViewController { guard let userId = result?.data?.userId else { return } KeychainUtil.setAccessToken(accessToken) Amplitude.instance().setUserId(userId) - self?.presentToHomeViewController() + self?.checkNotificationSettings() } } @@ -249,13 +249,53 @@ extension AuthViewController { } func presentToHomeViewController() { - if let window = view.window?.windowScene?.keyWindow { - let tabBarController = TabBarController() - let navigationController = UINavigationController(rootViewController: tabBarController) - navigationController.isNavigationBarHidden = true - window.rootViewController = navigationController + DispatchQueue.main.async { + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let window = windowScene.windows.first { + let tabBarController = TabBarController() + let navigationController = UINavigationController(rootViewController: tabBarController) + navigationController.isNavigationBarHidden = true + window.rootViewController = navigationController + window.makeKeyAndVisible() + } } } + + func checkNotificationSettings() { + UNUserNotificationCenter.current().getNotificationSettings { settings in + switch settings.authorizationStatus { + case .notDetermined: + self.showNotiDialogView() + default: + break + } + } + } + + func showNotiDialogView() { + DispatchQueue.main.async { + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let window = windowScene.windows.first { + let notiDialogViewController = NotificationDialogViewController() + notiDialogViewController.buttonHandler = { + self.requestNotification() + } + let navigationController = UINavigationController(rootViewController: notiDialogViewController) + navigationController.isNavigationBarHidden = true + window.rootViewController = navigationController + window.makeKeyAndVisible() + } + } + } + + func requestNotification() { +// UNUserNotificationCenter.current().delegate = self + let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] + UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: { _, _ in + self.presentToHomeViewController() + + }) + } } // MARK: - AppleSignIn @@ -298,7 +338,7 @@ extension AuthViewController: ASAuthorizationControllerDelegate, ASAuthorization guard let userId = result?.data?.userId else { return } KeychainUtil.setAccessToken(accessToken) Amplitude.instance().setUserId(userId) - self?.presentToHomeViewController() + self?.checkNotificationSettings() } default: break From 6e722e4884664f52f8dd1c285f4f9aa0d802c058 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Thu, 23 Nov 2023 23:57:40 +0900 Subject: [PATCH 34/56] =?UTF-8?q?[Feat]=20#205=20-=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20=ED=86=A0=ED=81=B0=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=20=ED=8C=90=EB=8B=A8=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO/Application/AppDelegate.swift | 141 ++++++++++++++---- 1 file changed, 114 insertions(+), 27 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift index 8e4a9897..f5dc3f41 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift @@ -28,13 +28,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { KakaoSDK.initSDK(appKey: Bundle.main.kakaoAPIKey) FirebaseApp.configure() - if KeychainUtil.getAccessToken() != "" { - self.skipAuthView() - print("토큰 유효") - } else { - // self.showAuthView() - // 토큰이 유효하지 않을 경우 일단은 온보딩->로그인->홈 이렇게만 가도록 - } + checkForUpdate() // 메시지 대리자 설정 Messaging.messaging().delegate = self @@ -43,9 +37,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Messaging.messaging().isAutoInitEnabled = true // 푸시 알림 권한 설정 및 푸시 알림에 앱 등록 - UNUserNotificationCenter.current().delegate = self - let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] - UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: { _, _ in }) + // UNUserNotificationCenter.current().delegate = self + // let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] + // UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: { _, _ in }) // device token 요청. application.registerForRemoteNotifications() @@ -53,17 +47,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } - func showAuthView() { - DispatchQueue.main.async { - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.first { - let authViewController = AuthViewController() - let navigationController = UINavigationController(rootViewController: authViewController) - window.rootViewController = navigationController - window.makeKeyAndVisible() - } - } - } +// func showAuthView() { +// DispatchQueue.main.async { +// if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, +// let window = windowScene.windows.first { +// let authViewController = AuthViewController() +// let navigationController = UINavigationController(rootViewController: authViewController) +// window.rootViewController = navigationController +// window.makeKeyAndVisible() +// } +// } +// } func skipAuthView() { // 홈 화면으로 바로 이동 @@ -103,22 +97,115 @@ func application(_ application: UIApplication, didDiscardSceneSessions sceneSess extension AppDelegate: MessagingDelegate { /// 현재 등록 토큰 가져오기. - func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { - if let fcmToken = fcmToken { - KeychainUtil.setFcmToken(fcmToken) - } + func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { + if let fcmToken = fcmToken { + KeychainUtil.setFcmToken(fcmToken) } + } } extension AppDelegate: UNUserNotificationCenterDelegate { - + /// foreground에서 러닝 중에 앱에 도착하는 알림을 다루는 메서드 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.list, .sound, .badge, .banner]) } - + /// 도착한 notification에 대한 유저의 반응을 다루는 메서드 func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { completionHandler() } } + +extension AppDelegate { + func checkForUpdate() { + // 앱스토어 버전 + guard let appstoreVersion = getAppstoreVersion() else { return } + + // 현재 설치된 앱의 버전 + guard let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { return } + + if compareVersion(userVersion: appVersion, appstoreVersion: appstoreVersion) { + DispatchQueue.main.async { + self.showUpdateAlert() + } + } else { + if KeychainUtil.getAccessToken() != "" { + self.skipAuthView() + print("토큰 유효") + } + } + } + + /// 버전 비교하는 메서드 + func compareVersion(userVersion: String, appstoreVersion:String) -> Bool { + let userMajor = userVersion.split(separator: ".").map {Int($0)!}[0] + let appstoreMajor = appstoreVersion.split(separator: ".").map {Int($0)!}[0] + + if userMajor < appstoreMajor { + return true + } + + let userMinor = userVersion.split(separator: ".").map {Int($0)!}[1] + let appstoreMinor = appstoreVersion.split(separator: ".").map {Int($0)!}[1] + + if userMinor < appstoreMinor { + return true + } + + let userPatch = userVersion.split(separator: ".").map {Int($0)!}[2] + let appstorePatch = appstoreVersion.split(separator: ".").map {Int($0)!}[2] + + if userPatch < appstorePatch { + return true + } + + return false + } + + /// 앱스토어에 배포된 버전 가져오는 메서드 + func getAppstoreVersion() -> String? { + let appleID = Bundle.main.appleId + guard let url = URL(string: "https://itunes.apple.com/lookup?id=\(appleID)"), + let data = try? Data(contentsOf: url), + let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any], + let results = json["results"] as? [[String: Any]], + let appStoreVersion = results[0]["version"] as? String else { + return nil + } + return appStoreVersion + } + + /// 선택 업데이트 경고창 + func showUpdateAlert() { + let alertController = UIAlertController( + title: I18N.update, + message: I18N.updateAlert, + preferredStyle: .alert + ) + + let updateAction = UIAlertAction(title: I18N.update, style: .default) { _ in + // App Store로 이동 + if let appStoreURL = URL(string: "https://itunes.apple.com/app/\(Bundle.main.appleId)") { + UIApplication.shared.open(appStoreURL, options: [:], completionHandler: {_ in + if KeychainUtil.getAccessToken() != "" { + self.skipAuthView() + print("토큰 유효") + } + }) + } + } + + let cancelAction = UIAlertAction(title: I18N.later, style: .default) + + alertController.addAction(updateAction) + alertController.addAction(cancelAction) + + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { + if let keyWindow = windowScene.windows.first, + let rootViewController = keyWindow.rootViewController { + rootViewController.present(alertController, animated: true, completion: nil) + } + } + } +} From 264da30afe445738ce3971564e1539555e0eb945 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Thu, 23 Nov 2023 23:58:43 +0900 Subject: [PATCH 35/56] =?UTF-8?q?[Add]=20#205=20-=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20appId=EC=99=80=20=EB=AC=B8=EC=9E=90=EC=97=B4=20?= =?UTF-8?q?=EB=A6=AC=ED=84=B0=EB=9F=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj | 4 ++-- .../iOS-NOTTODO/Global/Extensions/Bundle+.swift | 12 ++++++++++++ .../iOS-NOTTODO/Global/Literals/Strings.swift | 6 ++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 92673262..90f50f11 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -1519,7 +1519,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1557,7 +1557,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift index 89f96b1e..d752fe1d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift @@ -44,4 +44,16 @@ extension Bundle { } return value } + + var appleId: String { + guard let filePath = Bundle.main.path(forResource: "API_KEY", ofType: "plist") else { + fatalError("Could't find file 'API_KEY.plist'.") + } + let plist = NSDictionary(contentsOfFile: filePath) + + guard let value = plist?.object(forKey: "APPLE_ID") as? String else { + fatalError("Couldn't find key 'APPLE_ID' in 'API_KEY.plist'.") + } + return value + } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift index 04be6159..dd7d65c9 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift @@ -162,6 +162,12 @@ struct I18N { static let delete = "삭제하기" static let deleteModalTitle = "삭제하시겠습니까?" static let deleteModalSubtitle = "한 번 삭제하면 되돌릴 수 없어요." + static let update = "업데이트" + static let updateAlert = """ + 최신 업데이트가 있습니다. + 업데이트하시겠습니까? + """ + static let later = "나중에" /// home static let subText = "*달성 가능한 계획을 위해 다가올 일주일만 선택할 수 있어요" From 3c28afd1df236e616c7e4d41b8332bf55ec39e6f Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Fri, 24 Nov 2023 01:00:42 +0900 Subject: [PATCH 36/56] =?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 37/56] =?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 38/56] =?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 39/56] =?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 40/56] =?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 41/56] =?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 42/56] =?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 43/56] =?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 44/56] =?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 45/56] =?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 46/56] =?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 47/56] =?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 48/56] =?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 49/56] =?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 50/56] =?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) } From 25f866ac77ae6ee90548c0f3db27221946185b65 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Thu, 30 Nov 2023 20:01:29 +0900 Subject: [PATCH 51/56] =?UTF-8?q?[Chore]=20#205=20-=20lint=20=EC=98=A4?= =?UTF-8?q?=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/iOS-NOTTODO/Application/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift index f5dc3f41..6a1ae761 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift @@ -138,7 +138,7 @@ extension AppDelegate { } /// 버전 비교하는 메서드 - func compareVersion(userVersion: String, appstoreVersion:String) -> Bool { + func compareVersion(userVersion: String, appstoreVersion: String) -> Bool { let userMajor = userVersion.split(separator: ".").map {Int($0)!}[0] let appstoreMajor = appstoreVersion.split(separator: ".").map {Int($0)!}[0] From b2f53c71e807ae24430881b277826ca3f9015729 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Thu, 30 Nov 2023 20:03:40 +0900 Subject: [PATCH 52/56] =?UTF-8?q?[Fix]=20#205=20-=20version=20=EC=B5=9C?= =?UTF-8?q?=EC=8B=A0=EC=9C=BC=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 --- iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 90f50f11..92673262 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -1519,7 +1519,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 1.0.1; PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1557,7 +1557,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 1.0.1; PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From ade4d87bc0115f1aa98ba31d26c717ab821b776a Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Thu, 30 Nov 2023 20:25:08 +0900 Subject: [PATCH 53/56] =?UTF-8?q?[Fix]=20#206=20-=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20-=20=ED=95=98=EB=8B=A8=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=EC=9D=84=20=EB=88=8C=EB=A0=80=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=20=EC=95=8C=EB=9E=8C=EA=B6=8C=ED=95=9C=EC=9D=84=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20-=20=EC=BD=94=EB=93=9C=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NotificationDialogViewController.swift | 105 +----------------- 1 file changed, 1 insertion(+), 104 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift index 7934db82..8e49b69a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift @@ -20,17 +20,7 @@ final class NotificationDialogViewController: UIViewController { private let bellImage = UIImageView() private let titleLabel = UILabel() - private let alertView = UIView() private let bottomButton = UIButton() - private let alertTitleMessageLabel = UILabel() - private let alertSubTitleMessageLabel = UILabel() - private let notAllowButton = UIButton() - private let allowButton = UIButton() - private let buttonStackView = UIStackView() - private let circleImage = UIImageView() - private let backgroundImage = UIImageView() - private let verticalView = UIView() - private let horizontalView = UIView() // MARK: - Life Cycle @@ -47,8 +37,6 @@ extension NotificationDialogViewController { view.backgroundColor = .ntdBlack bellImage.image = .icBell - circleImage.image = .icCircle - backgroundImage.image = .notificationDialog titleLabel.do { $0.text = I18N.notiDialogTitle @@ -58,41 +46,6 @@ extension NotificationDialogViewController { $0.textAlignment = .center } - alertTitleMessageLabel.do { - $0.text = I18N.notiAllowTitle - $0.textColor = .notiBlack - $0.font = .Pretendard(.semiBold, size: 18) - $0.numberOfLines = 0 - $0.textAlignment = .center - } - - alertSubTitleMessageLabel.do { - $0.text = I18N.notiAllowSubTitle - $0.textColor = .gray3 - $0.font = .Pretendard(.medium, size: 13) - $0.numberOfLines = 0 - $0.textAlignment = .center - } - - buttonStackView.do { - $0.layer.cornerRadius = 17 - $0.backgroundColor = .none - } - - allowButton.do { - $0.setTitle(I18N.allow, for: .normal) - $0.setTitleColor(.notiBlue, for: .normal) - $0.titleLabel?.font = .Pretendard(.semiBold, size: 18) - $0.isEnabled = false - } - - notAllowButton.do { - $0.setTitle(I18N.notAllow, for: .normal) - $0.setTitleColor(.gray4, for: .normal) - $0.titleLabel?.font = .Pretendard(.medium, size: 15) - $0.isEnabled = false - } - bottomButton.do { $0.backgroundColor = .white $0.layer.cornerRadius = 25 @@ -102,19 +55,10 @@ extension NotificationDialogViewController { $0.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) } - alertView.do { - $0.backgroundColor = .none - $0.layer.cornerRadius = 17 - } - - verticalView.backgroundColor = .notiGreen - horizontalView.backgroundColor = .notiGreen } private func setLayout() { - view.addSubviews(bellImage, titleLabel, alertView, bottomButton, circleImage) - alertView.addSubviews(backgroundImage, alertTitleMessageLabel, alertSubTitleMessageLabel, horizontalView, buttonStackView) - buttonStackView.addArrangedSubviews(notAllowButton, verticalView, allowButton) + view.addSubviews(bellImage, titleLabel, bottomButton) bellImage.snp.makeConstraints { $0.top.equalTo(view.safeAreaLayoutGuide).inset(convertByHeightRatio(63)) @@ -133,53 +77,6 @@ extension NotificationDialogViewController { $0.directionalHorizontalEdges.equalToSuperview().inset(convertByHeightRatio(15)) $0.height.equalTo(convertByHeightRatio(50)) } - - alertView.snp.makeConstraints { - $0.top.equalTo(titleLabel.snp.bottom).offset(convertByHeightRatio(36)) - $0.directionalHorizontalEdges.equalToSuperview().inset(convertByHeightRatio(46)) - $0.bottom.equalTo(bottomButton.snp.top).inset(convertByHeightRatio(-186)) - } - - backgroundImage.snp.makeConstraints { - $0.center.equalToSuperview() - $0.size.equalToSuperview() - } - - alertTitleMessageLabel.snp.makeConstraints { - $0.top.equalToSuperview().inset(convertByHeightRatio(26)) - $0.centerX.equalToSuperview() - $0.height.equalTo(convertByHeightRatio(48)) - } - - alertSubTitleMessageLabel.snp.makeConstraints { - $0.top.equalTo(alertTitleMessageLabel.snp.bottom).offset(convertByHeightRatio(7)) - $0.directionalHorizontalEdges.equalToSuperview() - $0.height.equalTo(convertByHeightRatio(36)) - } - - buttonStackView.snp.makeConstraints { - $0.directionalHorizontalEdges.bottom.equalToSuperview() - $0.height.equalTo(convertByHeightRatio(48)) - } - - horizontalView.snp.makeConstraints { - $0.directionalHorizontalEdges.equalToSuperview() - $0.height.equalTo(convertByHeightRatio(1)) - $0.bottom.equalTo(buttonStackView.snp.top) - } - - verticalView.snp.makeConstraints { - $0.width.equalTo(convertByHeightRatio(1)) - } - - circleImage.snp.makeConstraints { - $0.center.equalTo(allowButton.snp.center) - $0.size.equalTo(convertByHeightRatio(77)) - } - - allowButton.snp.makeConstraints { - $0.width.equalToSuperview().dividedBy(2) - } } @objc From cac89eda7e45b5b30bcc66198430003cc6e1c2d4 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Wed, 3 Jan 2024 21:49:26 +0900 Subject: [PATCH 54/56] =?UTF-8?q?[Fix]=20#213=20-=20=EB=AF=B8=EC=85=98=20?= =?UTF-8?q?=EC=B2=B4=ED=81=AC=20=ED=95=B4=EC=A0=9C=20=EC=8B=9C=20complete?= =?UTF-8?q?=5Funcheck=5Fmission=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Home/ViewControllers/HomeViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index 8d75533a..fad9734b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -133,7 +133,7 @@ extension HomeViewController { 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)) + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.completeUncheckMission(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)) From 7f32e54f286b30223cd090fccb082c90172e2044 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Mon, 8 Jan 2024 14:24:06 +0900 Subject: [PATCH 55/56] =?UTF-8?q?[Fix]=20#206=20-=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81=20-=20changeRootViewCon?= =?UTF-8?q?trollerTo=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20-=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO/Application/AppDelegate.swift | 15 ---------- .../Auth/AuthViewController.swift | 28 ++++--------------- .../MyInfoAccountViewController.swift | 9 ++---- .../FifthOnboardingViewController.swift | 5 +--- 4 files changed, 9 insertions(+), 48 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift index b61d4a3d..897387f2 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift @@ -31,9 +31,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { if KeychainUtil.getAccessToken() != "" { self.skipAuthView() print("토큰 유효") - } else { - // self.showAuthView() - // 토큰이 유효하지 않을 경우 일단은 온보딩->로그인->홈 이렇게만 가도록 } // 메시지 대리자 설정 @@ -48,18 +45,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } - func showAuthView() { - DispatchQueue.main.async { - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.first { - let authViewController = AuthViewController() - let navigationController = UINavigationController(rootViewController: authViewController) - window.rootViewController = navigationController - window.makeKeyAndVisible() - } - } - } - func skipAuthView() { // 홈 화면으로 바로 이동 DispatchQueue.main.async { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift index be12b16d..33af0b2b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift @@ -242,7 +242,6 @@ extension AuthViewController { KeychainUtil.setAccessToken(accessToken) Amplitude.instance().setUserId(userId) self?.checkNotificationSettings() - } } } @@ -250,16 +249,9 @@ extension AuthViewController { func presentToHomeViewController() { DispatchQueue.main.async { - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.first { - let tabBarController = TabBarController() - let navigationController = UINavigationController(rootViewController: tabBarController) - navigationController.isNavigationBarHidden = true - window.rootViewController = navigationController - window.makeKeyAndVisible() - } + SceneDelegate.shared?.changeRootViewControllerTo(TabBarController()) } - } + } func checkNotificationSettings() { UNUserNotificationCenter.current().getNotificationSettings { settings in @@ -274,26 +266,18 @@ extension AuthViewController { func showNotiDialogView() { DispatchQueue.main.async { - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.first { - let notiDialogViewController = NotificationDialogViewController() - notiDialogViewController.buttonHandler = { - self.requestNotification() - } - let navigationController = UINavigationController(rootViewController: notiDialogViewController) - navigationController.isNavigationBarHidden = true - window.rootViewController = navigationController - window.makeKeyAndVisible() + let notiDialogViewController = NotificationDialogViewController() + notiDialogViewController.buttonHandler = { + self.requestNotification() } + SceneDelegate.shared?.changeRootViewControllerTo(notiDialogViewController) } } func requestNotification() { -// UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: { _, _ in self.presentToHomeViewController() - }) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift index bc26d58f..d2c1af64 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift @@ -180,16 +180,11 @@ extension MyInfoAccountViewController { if !UserDefaults.standard.bool(forKey: DefaultKeys.isAppleLogin) { kakaoLogout() } - AuthAPI.shared.deleteAuth { [weak self] _ in + AuthAPI.shared.deleteAuth { _ in UserDefaults.standard.removeObject(forKey: DefaultKeys.accessToken) UserDefaults.standard.removeObject(forKey: DefaultKeys.socialToken) AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.AccountInfo.completeLogout) - let authViewController = AuthViewController() - if let window = self?.view.window?.windowScene?.keyWindow { - let navigationController = UINavigationController(rootViewController: authViewController) - navigationController.isNavigationBarHidden = true - window.rootViewController = navigationController - } + SceneDelegate.shared?.changeRootViewControllerTo(AuthViewController()) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift index 8c75c522..01bfe566 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift @@ -184,10 +184,7 @@ extension FifthOnboardingViewController { if let window = view.window?.windowScene?.keyWindow { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { UIView.animate(withDuration: 0.01) { - let TabBarController = AuthViewController() - let navigationController = UINavigationController(rootViewController: TabBarController) - navigationController.isNavigationBarHidden = true - window.rootViewController = navigationController + SceneDelegate.shared?.changeRootViewControllerTo(AuthViewController()) } } } From be177e826ec4976bb16ee8cf8080fff035f6dcc1 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Mon, 8 Jan 2024 15:23:54 +0900 Subject: [PATCH 56/56] =?UTF-8?q?[FIX]=20#213=20-=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EB=90=9C=20=EB=A1=9C=EC=A7=81=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EC=95=B0=ED=94=8C=EB=A6=AC=ED=8A=9C=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Home/ViewControllers/HomeDataSource.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift index 2b68d6ed..a6e23db0 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift @@ -90,7 +90,12 @@ final class HomeDataSource { 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)) + switch status { + case .CHECKED: + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.completeCheckMission(title: missionItem.title, situation: missionItem.situationName)) + case .UNCHECKED: + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.completeUncheckMission(title: missionItem.title, situation: missionItem.situationName)) + } } } }