diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index 3f4fc566..b340e880 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -119,6 +119,7 @@ CEC2A68E2962AF2C00160BF7 /* RNMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2A68D2962AF2C00160BF7 /* RNMarker.swift */; }; CEC2A6902962B06C00160BF7 /* convertLocationObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2A68F2962B06C00160BF7 /* convertLocationObject.swift */; }; CEC2A6922962BE2900160BF7 /* DepartureSearchVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2A6912962BE2900160BF7 /* DepartureSearchVC.swift */; }; + CECA695C296E61D6002AF05C /* PrivateCourseNotUploadedResponseDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECA695B296E61D6002AF05C /* PrivateCourseNotUploadedResponseDto.swift */; }; CECBAD2F296C2F3C00AC8976 /* SignInRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECBAD2E296C2F3C00AC8976 /* SignInRouter.swift */; }; CEEC6B3A2961C4F300D00E1E /* CourseDrawingHomeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEC6B392961C4F300D00E1E /* CourseDrawingHomeVC.swift */; }; CEEC6B3C2961C51A00D00E1E /* CourseStorageVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEC6B3B2961C51A00D00E1E /* CourseStorageVC.swift */; }; @@ -267,6 +268,7 @@ CEC2A68D2962AF2C00160BF7 /* RNMarker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNMarker.swift; sourceTree = ""; }; CEC2A68F2962B06C00160BF7 /* convertLocationObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = convertLocationObject.swift; sourceTree = ""; }; CEC2A6912962BE2900160BF7 /* DepartureSearchVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DepartureSearchVC.swift; sourceTree = ""; }; + CECA695B296E61D6002AF05C /* PrivateCourseNotUploadedResponseDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateCourseNotUploadedResponseDto.swift; sourceTree = ""; }; CECBAD2E296C2F3C00AC8976 /* SignInRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInRouter.swift; sourceTree = ""; }; CEEC6B392961C4F300D00E1E /* CourseDrawingHomeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDrawingHomeVC.swift; sourceTree = ""; }; CEEC6B3B2961C51A00D00E1E /* CourseStorageVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseStorageVC.swift; sourceTree = ""; }; @@ -825,6 +827,7 @@ isa = PBXGroup; children = ( CE591EA0296D5EB5000FCBB3 /* PrivateCourseResponseDto.swift */, + CECA695B296E61D6002AF05C /* PrivateCourseNotUploadedResponseDto.swift */, ); path = ResponseDto; sourceTree = ""; @@ -1364,6 +1367,7 @@ CE591E9E296D5140000FCBB3 /* RunningRouter.swift in Sources */, DA20D84E2966A9B300F1581F /* CourseSearchVC.swift in Sources */, CE1006572968230800FD31FB /* DepartureLocationModel.swift in Sources */, + CECA695C296E61D6002AF05C /* PrivateCourseNotUploadedResponseDto.swift in Sources */, CE6655EC295D88D000C64E12 /* UITableView+.swift in Sources */, CECBAD2F296C2F3C00AC8976 /* SignInRouter.swift in Sources */, CEEC6B3A2961C4F300D00E1E /* CourseDrawingHomeVC.swift in Sources */, diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/PrivateCourseNotUploadedResponseDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/PrivateCourseNotUploadedResponseDto.swift new file mode 100644 index 00000000..898a21f0 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/PrivateCourseNotUploadedResponseDto.swift @@ -0,0 +1,12 @@ +// +// PrivateCourseNotUploadedResponseDto.swift +// Runnect-iOS +// +// Created by sejin on 2023/01/11. +// + +import Foundation + +struct PrivateCourseNotUploadedResponseDto: Codable { + let privateCourses: [Course] +} diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/PrivateCourseResponseDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/PrivateCourseResponseDto.swift index da9621fd..e4a31dd0 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/PrivateCourseResponseDto.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/PrivateCourseResponseDto.swift @@ -18,7 +18,7 @@ struct PrivateCourseResponseDto: Codable { struct PrivateCourse: Codable { let id: Int let image, createdAt: String - let distance: String? + let distance: Float? let path: [[Double]]? let departure: PrivateCourseDeparture } diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/CourseStorageRouter/CourseStorageRouter.swift b/Runnect-iOS/Runnect-iOS/Network/Router/CourseStorageRouter/CourseStorageRouter.swift index ba7ceb45..1eab1feb 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Router/CourseStorageRouter/CourseStorageRouter.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Router/CourseStorageRouter/CourseStorageRouter.swift @@ -11,6 +11,7 @@ import Moya enum CourseStorageRouter { case getAllPrivateCourse + case getPrivateCourseNotUploaded } extension CourseStorageRouter: TargetType { @@ -26,26 +27,28 @@ extension CourseStorageRouter: TargetType { switch self { case .getAllPrivateCourse: return "/course/user" + case .getPrivateCourseNotUploaded: + return "/course/private/user" } } var method: Moya.Method { switch self { - case .getAllPrivateCourse: + case .getAllPrivateCourse, .getPrivateCourseNotUploaded: return .get } } var task: Moya.Task { switch self { - case .getAllPrivateCourse: + case .getAllPrivateCourse, .getPrivateCourseNotUploaded: return .requestPlain } } var headers: [String: String]? { switch self { - case .getAllPrivateCourse: + case .getAllPrivateCourse, .getPrivateCourseNotUploaded: return Config.headerWithDeviceId } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift index 80ef6320..f918e429 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift @@ -11,9 +11,12 @@ import SnapKit import Then class CourseUploadVC: UIViewController { + // MARK: - Properties + private var courseModel: Course? private let courseTitleMaxLength = 20 + // MARK: - UI Components private lazy var navibar = CustomNavigationBar(self, type: .titleWithLeftButton).setTitle("코스 업로드") @@ -75,6 +78,15 @@ class CourseUploadVC: UIViewController { extension CourseUploadVC { + func setData(courseModel: Course) { + self.courseModel = courseModel + self.mapImageView.setImage(with: courseModel.image) + + guard let distance = courseModel.distance else { return } + self.distanceInfoView.setDescriptionText(description: "\(String(distance))km") + self.departureInfoView.setDescriptionText(description: "\(courseModel.departure.region) \(courseModel.departure.city)") + } + private func setAddTarget() { self.uploadButton.addTarget(self, action: #selector(pushToCourseDiscoveryVC), for: .touchUpInside) self.courseTitleTextField.addTarget(self, action: #selector(textFieldTextDidChange), for: .editingChanged) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/MyCourseSelectVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/MyCourseSelectVC.swift index 46bb884c..84559b5b 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/MyCourseSelectVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/MyCourseSelectVC.swift @@ -6,13 +6,27 @@ // import UIKit + import Then +import Moya class MyCourseSelectVC: UIViewController { // MARK: - Properties - private var selectedIndex: Int? + private let courseStorageProvider = MoyaProvider( + plugins: [NetworkLoggerPlugin(verbose: true)] + ) + + private var courseList = [Course]() + + private var selectedIndex: Int? { + didSet { + if selectedIndex == nil { + selectButton.setEnabled(false) + } + } + } // MARK: - UI Components @@ -24,7 +38,7 @@ class MyCourseSelectVC: UIViewController { private lazy var mapCollectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical - + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.backgroundColor = .clear collectionView.translatesAutoresizingMaskIntoConstraints = false @@ -38,7 +52,7 @@ class MyCourseSelectVC: UIViewController { final let collectionViewInset = UIEdgeInsets(top: 28, left: 16, bottom: 28, right: 16) final let itemSpacing: CGFloat = 10 final let lineSpacing: CGFloat = 20 - + // MARK: - View Life Cycle override func viewDidLoad() { @@ -53,11 +67,18 @@ class MyCourseSelectVC: UIViewController { override func viewWillAppear(_ animated: Bool) { self.hideTabBar(wantsToHide: true) + self.getPrivateCourseNotUploaded() } } + // MARK: - Methods extension MyCourseSelectVC { + private func setData(courseList: [Course]) { + self.courseList = courseList + mapCollectionView.reloadData() + } + private func setDelegate() { mapCollectionView.delegate = self mapCollectionView.dataSource = self @@ -65,23 +86,26 @@ extension MyCourseSelectVC { private func register() { mapCollectionView.register(CourseListCVC.self, - forCellWithReuseIdentifier: CourseListCVC.className) + forCellWithReuseIdentifier: CourseListCVC.className) } private func setAddTarget() { self.selectButton.addTarget(self, action: #selector(pushToUploadVC), for: .touchUpInside) } } - // MARK: - @objc Function - extension MyCourseSelectVC { - @objc private func pushToUploadVC() { - let nextVC = CourseUploadVC() - self.navigationController?.pushViewController(nextVC, animated: true) - } +// MARK: - @objc Function + +extension MyCourseSelectVC { + @objc private func pushToUploadVC() { + guard let selectedIndex = self.selectedIndex else { return } + let courseUploadVC = CourseUploadVC() + courseUploadVC.setData(courseModel: courseList[selectedIndex]) + self.navigationController?.pushViewController(courseUploadVC, animated: true) } +} - // MARK: - naviVar Layout +// MARK: - UI & Layout extension MyCourseSelectVC { private func setNavigationBar() { @@ -89,17 +113,14 @@ extension MyCourseSelectVC { navibar.snp.makeConstraints { make in make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) make.height.equalTo(48) + } } -} - // MARK: - setUI private func setUI() { view.backgroundColor = .w1 self.tabBarController?.tabBar.isHidden = true } - - // MARK: - Layout Helpers - + private func setLayout() { view.addSubviews(selectButton, mapCollectionView) self.view.bringSubviewToFront(selectButton) @@ -109,7 +130,7 @@ extension MyCourseSelectVC { make.height.equalTo(44) make.bottom.equalToSuperview().inset(34) } - + mapCollectionView.snp.makeConstraints { make in make.top.equalTo(navibar.snp.bottom) make.leading.trailing.equalTo(view.safeAreaLayoutGuide) @@ -121,14 +142,16 @@ extension MyCourseSelectVC { extension MyCourseSelectVC: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return 15 + return courseList.count } + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let cell = collectionView.cellForItem(at: indexPath) as? CourseListCVC else { return } self.selectedIndex = indexPath.item self.selectButton.setEnabled(true) cell.selectCell(didSelect: true) } + func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { guard let cell = collectionView.cellForItem(at: indexPath) as? CourseListCVC else { return } self.selectedIndex = nil @@ -139,7 +162,7 @@ extension MyCourseSelectVC: UICollectionViewDelegate, UICollectionViewDataSource guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) as? CourseListCVC else { return UICollectionViewCell() } - cell.setCellType(type: .titleWithLocation) + cell.setCellType(type: .title) if let selectedIndex = selectedIndex, selectedIndex == indexPath.item { cell.selectCell(didSelect: true) @@ -147,6 +170,15 @@ extension MyCourseSelectVC: UICollectionViewDelegate, UICollectionViewDataSource cell.selectCell(didSelect: false) } + let model = courseList[indexPath.item] + + var title = "\(model.departure.region) \(model.departure.city)" + if let town = model.departure.town { + title += " \(town)" + } + + cell.setData(imageURL: model.image, title: title, location: nil, didLike: nil) + return cell } } @@ -156,7 +188,7 @@ extension MyCourseSelectVC: UICollectionViewDelegate, UICollectionViewDataSource extension MyCourseSelectVC: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let cellWidth = (UIScreen.main.bounds.width - (self.itemSpacing + 2*self.collectionViewInset.left)) / 2 - let cellHeight = CourseListCVCType.getCellHeight(type: .titleWithLocation, cellWidth: cellWidth) + let cellHeight = CourseListCVCType.getCellHeight(type: .title, cellWidth: cellWidth) return CGSize(width: cellWidth, height: cellHeight) } @@ -172,5 +204,37 @@ extension MyCourseSelectVC: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return self.lineSpacing } - +} + +// MARK: - Network + +extension MyCourseSelectVC { + private func getPrivateCourseNotUploaded() { + self.selectedIndex = nil + LoadingIndicator.showLoading() + courseStorageProvider.request(.getPrivateCourseNotUploaded) { [weak self] response in + LoadingIndicator.hideLoading() + guard let self = self else { return } + 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.setData(courseList: data.privateCourses) + } catch { + print(error.localizedDescription) + } + } + if status >= 400 { + print("400 error") + self.showNetworkFailureToast() + } + case .failure(let error): + print(error.localizedDescription) + self.showNetworkFailureToast() + } + } + } }