diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index 45f38ad3..7abed865 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ A3F67AE2296D33AC001598A2 /* MyPageDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3F67AE1296D33AC001598A2 /* MyPageDto.swift */; }; A3F67AE4296D33E0001598A2 /* MyPageRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3F67AE3296D33E0001598A2 /* MyPageRouter.swift */; }; A3F67AEA296E4936001598A2 /* ActivityRecordInfoDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3F67AE9296E4936001598A2 /* ActivityRecordInfoDto.swift */; }; + CE09037D296E9ED900BEA710 /* ScrapCourseResponseDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE09037C296E9ED900BEA710 /* ScrapCourseResponseDto.swift */; }; CE0C23742966D62A00B45063 /* PagedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE0C23732966D62A00B45063 /* PagedView.swift */; }; CE0C23772966D64D00B45063 /* PageCVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE0C23762966D64D00B45063 /* PageCVC.swift */; }; CE0C23792966D6AF00B45063 /* ViewPager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE0C23782966D6AF00B45063 /* ViewPager.swift */; }; @@ -158,6 +159,7 @@ A3F67AE1296D33AC001598A2 /* MyPageDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageDto.swift; sourceTree = ""; }; A3F67AE3296D33E0001598A2 /* MyPageRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageRouter.swift; sourceTree = ""; }; A3F67AE9296E4936001598A2 /* ActivityRecordInfoDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityRecordInfoDto.swift; sourceTree = ""; }; + CE09037C296E9ED900BEA710 /* ScrapCourseResponseDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrapCourseResponseDto.swift; sourceTree = ""; }; CE0C23732966D62A00B45063 /* PagedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagedView.swift; sourceTree = ""; }; CE0C23762966D64D00B45063 /* PageCVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageCVC.swift; sourceTree = ""; }; CE0C23782966D6AF00B45063 /* ViewPager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewPager.swift; sourceTree = ""; }; @@ -831,6 +833,7 @@ children = ( CE591EA0296D5EB5000FCBB3 /* PrivateCourseResponseDto.swift */, CECA695B296E61D6002AF05C /* PrivateCourseNotUploadedResponseDto.swift */, + CE09037C296E9ED900BEA710 /* ScrapCourseResponseDto.swift */, ); path = ResponseDto; sourceTree = ""; @@ -1400,6 +1403,7 @@ CE3A53C5296C6017003D518C /* KeychainManager.swift in Sources */, CE14677A2965A80700DCEA1B /* CustomBottomSheetVC.swift in Sources */, CEEC6B4B2961D89700D00E1E /* CustomNavigationBar.swift in Sources */, + CE09037D296E9ED900BEA710 /* ScrapCourseResponseDto.swift in Sources */, CE40BB2D296808B00030ABCA /* DepartureSearchingRouter.swift in Sources */, CE15F5A4296C932E0023827C /* RunningModel.swift in Sources */, CE17F02D2961BBA100E1DED0 /* ColorLiterals.swift in Sources */, diff --git a/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomButton.swift b/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomButton.swift index 9422bfa9..c6a49a72 100644 --- a/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomButton.swift +++ b/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomButton.swift @@ -38,6 +38,18 @@ extension CustomButton { return self } + @discardableResult + public func setTitle(title: String) -> Self { + self.setAttributedTitle( + NSAttributedString( + string: title, + attributes: [.font: UIFont.h5, .foregroundColor: UIColor.white] + ), + for: .normal + ) + return self + } + /// 버튼의 backgroundColor, textColor 변경 @discardableResult public func setColor(bgColor: UIColor, disableColor: UIColor, textColor: UIColor = .white) -> Self { diff --git a/Runnect-iOS/Runnect-iOS/Global/UIComponents/ListEmptyView.swift b/Runnect-iOS/Runnect-iOS/Global/UIComponents/ListEmptyView.swift index b8d60788..d6a039d5 100644 --- a/Runnect-iOS/Runnect-iOS/Global/UIComponents/ListEmptyView.swift +++ b/Runnect-iOS/Runnect-iOS/Global/UIComponents/ListEmptyView.swift @@ -78,7 +78,7 @@ extension ListEmptyView { self.backgroundColor = .clear self.descriptionLabel.text = description - self.bottomButton.titleLabel?.text = buttonTitle + self.bottomButton.setTitle(title: buttonTitle) } private func setLayout() { diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/ScrapCourseResponseDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/ScrapCourseResponseDto.swift new file mode 100644 index 00000000..df79d112 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/ScrapCourseResponseDto.swift @@ -0,0 +1,33 @@ +// +// ScrapCourseResponseDto.swift +// Runnect-iOS +// +// Created by sejin on 2023/01/11. +// + +import Foundation + +// MARK: - ScrapCourseResponseDto + +struct ScrapCourseResponseDto: Codable { + let scraps: [ScrapCourse] + + enum CodingKeys: String, CodingKey { + case scraps = "Scraps" + } +} + +// MARK: - ScrapCourse + +struct ScrapCourse: Codable { + let id, publicCourseId, courseId: Int + let title: String + let image: String + let departure: ScrapCourseDeparture +} + +// MARK: - ScrapCourseDeparture + +struct ScrapCourseDeparture: Codable { + let region, city: String +} diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/CourseStorageRouter/CourseStorageRouter.swift b/Runnect-iOS/Runnect-iOS/Network/Router/CourseStorageRouter/CourseStorageRouter.swift index 1eab1feb..b447f21c 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Router/CourseStorageRouter/CourseStorageRouter.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Router/CourseStorageRouter/CourseStorageRouter.swift @@ -12,6 +12,7 @@ import Moya enum CourseStorageRouter { case getAllPrivateCourse case getPrivateCourseNotUploaded + case getScrapCourse } extension CourseStorageRouter: TargetType { @@ -29,26 +30,28 @@ extension CourseStorageRouter: TargetType { return "/course/user" case .getPrivateCourseNotUploaded: return "/course/private/user" + case .getScrapCourse: + return "/scrap/user" } } var method: Moya.Method { switch self { - case .getAllPrivateCourse, .getPrivateCourseNotUploaded: + case .getAllPrivateCourse, .getPrivateCourseNotUploaded, .getScrapCourse: return .get } } var task: Moya.Task { switch self { - case .getAllPrivateCourse, .getPrivateCourseNotUploaded: + case .getAllPrivateCourse, .getPrivateCourseNotUploaded, .getScrapCourse: return .requestPlain } } var headers: [String: String]? { switch self { - case .getAllPrivateCourse, .getPrivateCourseNotUploaded: + case .getAllPrivateCourse, .getPrivateCourseNotUploaded, .getScrapCourse: return Config.headerWithDeviceId } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift index 7c528f91..1754df7d 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift @@ -11,6 +11,11 @@ import Then final class CourseDetailVC: UIViewController { + // MARK: - Properties + + private var courseId: Int? + private var publicCourseId: Int? + // MARK: - UI Components private lazy var navibar = CustomNavigationBar(self, type: .titleWithLeftButton) private lazy var middleScorollView = UIScrollView().then { @@ -112,6 +117,11 @@ extension CourseDetailVC { // MARK: - Method extension CourseDetailVC { + func setCourseId(courseId: Int?, publicCourseId: Int?) { + self.courseId = courseId + self.publicCourseId = publicCourseId + } + private func setAddTarget() { likeButton.addTarget(self, action: #selector(likeButtonDidTap), for: .touchUpInside) } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift index 58516be4..4f1c1904 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift @@ -22,6 +22,8 @@ final class CourseStorageVC: UIViewController { private var privateCourseList = [PrivateCourse]() + private var scrapCourseList = [ScrapCourse]() + // MARK: - UI Components private lazy var naviBar = CustomNavigationBar(self, type: .title).setTitle("보관함") @@ -45,6 +47,7 @@ final class CourseStorageVC: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.getPrivateCourseList() + self.getScrapCourseList() } } @@ -56,6 +59,11 @@ extension CourseStorageVC { self.privateCourseListView.setData(courseList: courseList) } + private func setScrapCourseData(courseList: [ScrapCourse]) { + self.scrapCourseList = courseList + self.scrapCourseListView.setData(courseList: courseList) + } + private func bindUI() { privateCourseListView.courseDrawButtonTapped.sink { [weak self] in guard let self = self else { return } @@ -78,6 +86,8 @@ extension CourseStorageVC { scrapCourseListView.cellDidTapped.sink { [weak self] index in guard let self = self else { return } let courseDetailVC = CourseDetailVC() + let model = self.scrapCourseList[index] + courseDetailVC.setCourseId(courseId: model.courseId, publicCourseId: model.publicCourseId) courseDetailVC.hidesBottomBarWhenPushed = true self.navigationController?.pushViewController(courseDetailVC, animated: true) }.store(in: cancelBag) @@ -136,4 +146,32 @@ extension CourseStorageVC { } } } + + private func getScrapCourseList() { + LoadingIndicator.showLoading() + courseStorageProvider.request(.getScrapCourse) { [weak self] response in + guard let self = self else { return } + LoadingIndicator.hideLoading() + switch response { + case .success(let result): + let status = result.statusCode + if 200..<300 ~= status { + do { + let responseDto = try result.map(BaseResponse.self) + guard let data = responseDto.data else { return } + self.setScrapCourseData(courseList: data.scraps) + } catch { + print(error.localizedDescription) + } + } + if status >= 400 { + print("400 error") + self.showNetworkFailureToast() + } + case .failure(let error): + print(error.localizedDescription) + self.showNetworkFailureToast() + } + } + } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/ScrapCourseListView.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/ScrapCourseListView.swift index 5f44b7dc..4c1bb029 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/ScrapCourseListView.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/ScrapCourseListView.swift @@ -15,6 +15,8 @@ final class ScrapCourseListView: UIView { var scrapButtonTapped = PassthroughSubject() var cellDidTapped = PassthroughSubject() + private var courseList = [ScrapCourse]() + final let collectionViewInset = UIEdgeInsets(top: 28, left: 16, bottom: 28, right: 16) final let itemSpacing: CGFloat = 10 final let lineSpacing: CGFloat = 20 @@ -53,6 +55,12 @@ final class ScrapCourseListView: UIView { // MARK: - Methods extension ScrapCourseListView { + func setData(courseList: [ScrapCourse]) { + self.courseList = courseList + self.courseListCollectionView.reloadData() + self.emptyView.isHidden = !courseList.isEmpty + } + private func setDelegate() { courseListCollectionView.delegate = self courseListCollectionView.dataSource = self @@ -94,7 +102,7 @@ extension ScrapCourseListView { extension ScrapCourseListView: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return 15 + return courseList.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { @@ -102,6 +110,12 @@ extension ScrapCourseListView: UICollectionViewDelegate, UICollectionViewDataSou for: indexPath) as? CourseListCVC else { return UICollectionViewCell() } cell.setCellType(type: .all) + + let model = courseList[indexPath.item] + + let location = "\(model.departure.region) \(model.departure.city)" + + cell.setData(imageURL: model.image, title: model.title, location: location, didLike: true) return cell } }