diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index 23f283d7..b7ccc67c 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -18,7 +18,6 @@ 71288ED32B26ED2500D6C921 /* UserUploadedLabelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71288ED22B26ED2500D6C921 /* UserUploadedLabelCell.swift */; }; 712F661D2A7B7BAB00D9539B /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712F661C2A7B7BAB00D9539B /* Config.swift */; }; 7136BF8A2AF921A900679364 /* CustomBottomSheetVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */; }; - 713BA40B2B218AF8009091A8 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 713BA40A2B218AF8009091A8 /* GoogleService-Info.plist */; }; 717916DA2B13613B009CEF97 /* MarathonListResponseDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 717916D92B13613B009CEF97 /* MarathonListResponseDto.swift */; }; 717916DE2B137DC3009CEF97 /* TotalPageCountDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 717916DD2B137DC3009CEF97 /* TotalPageCountDto.swift */; }; 71BAD06A2B24CECC0061E31D /* UserProfileDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BAD0692B24CECC0061E31D /* UserProfileDto.swift */; }; @@ -190,7 +189,6 @@ 71288ED22B26ED2500D6C921 /* UserUploadedLabelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserUploadedLabelCell.swift; sourceTree = ""; }; 712F661C2A7B7BAB00D9539B /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBottomSheetVC.swift; sourceTree = ""; }; - 713BA40A2B218AF8009091A8 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 717916D92B13613B009CEF97 /* MarathonListResponseDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonListResponseDto.swift; sourceTree = ""; }; 717916DD2B137DC3009CEF97 /* TotalPageCountDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TotalPageCountDto.swift; sourceTree = ""; }; 71BAD0692B24CECC0061E31D /* UserProfileDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileDto.swift; sourceTree = ""; }; @@ -198,6 +196,7 @@ 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonTitleCollectionViewCell.swift; sourceTree = ""; }; 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonMapCollectionViewCell.swift; sourceTree = ""; }; 71F7BF062B0CDFE300B752B3 /* MarathonCourseListCVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonCourseListCVC.swift; sourceTree = ""; }; + A3305A96296EF58C000B1A10 /* GoalRewardInfoDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalRewardInfoDto.swift; sourceTree = ""; }; A3BC2F2A2962C3D500198261 /* GoalRewardInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalRewardInfoVC.swift; sourceTree = ""; }; A3BC2F2C2962C3F200198261 /* ActivityRecordInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityRecordInfoVC.swift; sourceTree = ""; }; diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/ScrapCourseResponseDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/ScrapCourseResponseDto.swift index df79d112..5e1c9712 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/ScrapCourseResponseDto.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseStorageDto/ResponseDto/ScrapCourseResponseDto.swift @@ -11,10 +11,6 @@ import Foundation struct ScrapCourseResponseDto: Codable { let scraps: [ScrapCourse] - - enum CodingKeys: String, CodingKey { - case scraps = "Scraps" - } } // MARK: - ScrapCourse diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift index 6aa3809d..d7f81065 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift @@ -101,10 +101,12 @@ extension CourseStorageVC { privateCourseListView.cellDidTapped.sink { [weak self] index in guard let self = self else { return } + guard let title = self.privateCourseList[index].departure.name else {return} let runningWaitingVC = RunningWaitingVC() - runningWaitingVC.setData(courseId: self.privateCourseList[index].id, publicCourseId: nil) - runningWaitingVC.hidesBottomBarWhenPushed = true + runningWaitingVC.setData(courseId: self.privateCourseList[index].id, publicCourseId: nil, courseTitle: title) + /// 코스 이름을 여기서 가져오는 로직 + runningWaitingVC.hidesBottomBarWhenPushed = true self.navigationController?.pushViewController(runningWaitingVC, animated: true) }.store(in: cancelBag) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift index 7106bb88..ee97f6dc 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift @@ -192,18 +192,17 @@ extension PrivateCourseListView: UICollectionViewDelegate, UICollectionViewDataS } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) - as? CourseListCVC else { return UICollectionViewCell() } - cell.setCellType(type: .title) - if let selectedCells = collectionView.indexPathsForSelectedItems, selectedCells.contains(indexPath) { - cell.selectCell(didSelect: true) - } else { - cell.selectCell(didSelect: false) + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) as? CourseListCVC else { + return UICollectionViewCell() } let model = courseList[indexPath.item] - let cellTitle = "\(model.departure.region) \(model.departure.city)" + let cellTitle = model.departure.name ?? " " + + cell.setCellType(type: .title) + cell.selectCell(didSelect: collectionView.indexPathsForSelectedItems?.contains(indexPath) ?? false) cell.setData(imageURL: model.image, title: cellTitle, location: nil, didLike: nil, isEditMode: isEditMode) + return cell } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningWaitingVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningWaitingVC.swift index c5f95be7..50a0e3a0 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningWaitingVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningWaitingVC.swift @@ -9,6 +9,7 @@ import UIKit import NMapsMap import Moya +import DropDown final class RunningWaitingVC: UIViewController { @@ -17,14 +18,20 @@ final class RunningWaitingVC: UIViewController { private var courseId: Int? private var publicCourseId: Int? private var courseModel: Course? + private var courseTitle: String? private let courseProvider = Providers.courseProvider private let recordProvider = Providers.recordProvider // MARK: - UI Components - - private lazy var naviBar = CustomNavigationBar(self, type: .titleWithLeftButton) + + private lazy var naviBar = CustomNavigationBar(self, type: .titleWithLeftButton).setTitle(courseTitle ?? "Test Code") + + private let moreButton = UIButton(type: .system).then { + $0.setImage(ImageLiterals.icMore, for: .normal) + $0.tintColor = .g1 + } private let mapView = RNMapView() @@ -72,9 +79,10 @@ final class RunningWaitingVC: UIViewController { // MARK: - Methods extension RunningWaitingVC { - func setData(courseId: Int, publicCourseId: Int?) { + func setData(courseId: Int, publicCourseId: Int?, courseTitle: String) { self.courseId = courseId self.publicCourseId = publicCourseId + self.courseTitle = courseTitle getCourseDetail(courseId: courseId) } @@ -88,12 +96,13 @@ extension RunningWaitingVC { self.distanceLabel.text = String(distance) } - func makePath(locations: [NMGLatLng]) { + private func makePath(locations: [NMGLatLng]) { self.mapView.makeMarkersWithStartMarker(at: locations, moveCameraToStartMarker: true) } private func setAddTarget() { self.startButton.addTarget(self, action: #selector(startButtonDidTap), for: .touchUpInside) + moreButton.addTarget(self, action: #selector(moreButtonDidTap), for: .touchUpInside) } } @@ -115,6 +124,38 @@ extension RunningWaitingVC { countDownVC.setData(runningModel: runningModel) self.navigationController?.pushViewController(countDownVC, animated: true) } + + @objc private func moreButtonDidTap() { + guard let courseModel = self.courseModel else {return} + + let items = ["수정하기", "삭제하기"] + let imageArray: [UIImage] = [ImageLiterals.icModify, ImageLiterals.icRemove] + + let menu = DropDown().then { + $0.anchorView = moreButton + $0.backgroundColor = .w1 + $0.bottomOffset = CGPoint(x: -136, y: moreButton.bounds.height - 10) + $0.width = 170 + $0.cellHeight = 40 + $0.cornerRadius = 12 + $0.dismissMode = .onTap + $0.separatorColor = UIColor(hex: "#EBEBEB") + $0.dataSource = items + $0.textFont = .b3 + } + + menu.customCellConfiguration = { (index: Index, _: String, cell: DropDownCell) -> Void in + let lastDividerLineRemove = UIView(frame: CGRect(origin: CGPoint(x: 0, y: (items.count * 40) - 1), size: CGSize(width: 170, height: 10))) + lastDividerLineRemove.backgroundColor = .white + cell.separatorInset = .zero + cell.dropDownImage.image = imageArray[index] + cell.addSubview(lastDividerLineRemove) + } + + dropDownTouchAction(menu: menu, courseModel: courseModel) + + menu.show() + } } // MARK: - UI & Layout @@ -123,11 +164,11 @@ extension RunningWaitingVC { private func setUI() { self.view.backgroundColor = .w1 self.distanceContainerView.layer.applyShadow(alpha: 0.2, x: 2, y: 4, blur: 9) - self.naviBar.backgroundColor = .clear } private func setLayout() { view.addSubviews(naviBar, + moreButton, mapView, distanceContainerView, startButton) @@ -139,8 +180,16 @@ extension RunningWaitingVC { view.bringSubviewToFront(naviBar) + moreButton.snp.makeConstraints { make in + make.trailing.equalTo(self.view.safeAreaLayoutGuide) + make.centerY.equalTo(naviBar) + } + view.bringSubviewToFront(moreButton) + mapView.snp.makeConstraints { make in - make.edges.equalToSuperview() + make.leading.trailing.equalTo(self.view.safeAreaLayoutGuide) + make.top.equalTo(naviBar.snp.bottom) + make.bottom.equalToSuperview() } distanceContainerView.snp.makeConstraints { make in @@ -155,11 +204,11 @@ extension RunningWaitingVC { distanceStackView.snp.makeConstraints { make in make.center.equalToSuperview() } - + startButton.snp.makeConstraints { make in make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16) make.height.equalTo(44) - make.top.equalTo(view.snp.bottom).offset(34) + make.top.equalTo(view.snp.bottom).offset(24) } } @@ -207,3 +256,76 @@ extension RunningWaitingVC { } } } +// MARK: - Network + +extension RunningWaitingVC { + private func deleteCourse() { + guard let courseId = self.courseId else { return } + LoadingIndicator.showLoading() + courseProvider.request(.deleteCourse(courseIdList: [courseId])) { [weak self] response in + LoadingIndicator.hideLoading() + guard let self = self else { return } + switch response { + case .success(let result): + print("리절트", result) + let status = result.statusCode + if 200..<300 ~= status { + print("삭제 성공") + } + if status >= 400 { + print("400 error") + self.showNetworkFailureToast() + } + case .failure(let error): + print(error.localizedDescription) + self.showNetworkFailureToast() + } + } + } +} + +// MARK: - DropDown + +extension RunningWaitingVC { + private func dropDownTouchAction(menu: DropDown, courseModel: Course) { + DropDown.appearance().textColor = .g1 + DropDown.appearance().selectionBackgroundColor = .w1 + DropDown.appearance().shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.2) + DropDown.appearance().shadowOpacity = 1 + DropDown.appearance().shadowRadius = 10 + + menu.selectionAction = { [unowned self] (_, item) in + menu.clearSelection() + + switch item { + case "수정하기": + // 현재 코스 모델의 이름 변경 + ModifyCourseTitle() + case "삭제하기": + let deleteAlertVC = RNAlertVC(description: "러닝 기록을 정말로 삭제하시겠어요?").setButtonTitle("취소", "삭제하기") + deleteAlertVC.modalPresentationStyle = .overFullScreen + deleteAlertVC.rightButtonTapAction = { + deleteAlertVC.dismiss(animated: false) + self.deleteCourse() + self.navigationController?.popViewController(animated: true) + } + self.present(deleteAlertVC, animated: false) + default: + self.showToast(message: "없는 명령어 입니다.") + } + } + } + + private func ModifyCourseTitle() { + let bottomSheetVC = CustomBottomSheetVC(type: .textField) + bottomSheetVC.modalPresentationStyle = .overFullScreen + bottomSheetVC.completeButtonTapAction = { [weak self] text in + guard let self = self else { return } + guard handleVisitor() else { return } + self.courseTitle = text + /// 여기에 id 랑 text post 작업 필요 할 듯? + self.dismiss(animated: false) + } + self.present(bottomSheetVC, animated: false) + } +}