diff --git a/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift b/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift index 70da2d1a..292ddc91 100644 --- a/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift +++ b/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift @@ -16,6 +16,8 @@ final class RNAlertVC: UIViewController { var rightButtonTapAction: (() -> Void)? + var deleteRecordDelegate: deleteRecordDelegate? + // MARK: - UI Components private let containerView = UIView().then { @@ -30,7 +32,7 @@ final class RNAlertVC: UIViewController { } private lazy var yesButton = UIButton(type: .custom).then { - $0.setTitle("네", for: .normal) + $0.setTitle("예", for: .normal) $0.titleLabel?.font = .h5 $0.setTitleColor(.w1, for: .normal) $0.layer.backgroundColor = UIColor.m1.cgColor @@ -78,6 +80,13 @@ extension RNAlertVC { self.noButton.addTarget(self, action: #selector(touchUpNoButton), for: .touchUpInside) self.yesButton.addTarget(self, action: #selector(touchYesButton), for: .touchUpInside) } + + @discardableResult + func setButtonTitle(_ leftButtonTitle: String, _ rightButtonTitle: String) -> Self { + self.yesButton.setTitle(rightButtonTitle, for: .normal) + self.noButton.setTitle(leftButtonTitle, for: .normal) + return self + } } // MARK: - @objc Function @@ -89,6 +98,7 @@ extension RNAlertVC { @objc private func touchYesButton() { self.rightButtonTapAction?() + deleteRecordDelegate?.wantsToDelete() } } diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/RecordRouter.swift b/Runnect-iOS/Runnect-iOS/Network/Router/RecordRouter.swift index c635164c..ed8be11c 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Router/RecordRouter.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Router/RecordRouter.swift @@ -12,6 +12,7 @@ import Moya enum RecordRouter { case recordRunning(param: RunningRecordRequestDto) case getActivityRecordInfo + case deleteRecord(recordIdList: [Int]) } extension RecordRouter: TargetType { @@ -25,7 +26,7 @@ extension RecordRouter: TargetType { var path: String { switch self { - case .recordRunning: + case .recordRunning, .deleteRecord: return "/record" case .getActivityRecordInfo: return "/record/user" @@ -38,6 +39,8 @@ extension RecordRouter: TargetType { return .post case .getActivityRecordInfo: return .get + case .deleteRecord: + return .put } } @@ -51,12 +54,14 @@ extension RecordRouter: TargetType { } case .getActivityRecordInfo: return .requestPlain + case .deleteRecord(let recordIdList): + return .requestParameters(parameters: ["recordIdList": recordIdList], encoding: JSONEncoding.default) } } var headers: [String: String]? { switch self { - case .recordRunning, .getActivityRecordInfo: + case .recordRunning, .getActivityRecordInfo, .deleteRecord: return Config.defaultHeader } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift index 2a6d0294..7f582e02 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift @@ -104,6 +104,7 @@ final class CourseDetailVC: UIViewController { $0.isScrollEnabled = false $0.sizeToFit() } + // MARK: - View Life Cycle override func viewDidLoad() { diff --git a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordDetailVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordDetailVC.swift index 2412cf8b..007d6d88 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordDetailVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordDetailVC.swift @@ -9,19 +9,16 @@ import UIKit import SnapKit import Then +import Moya final class ActivityRecordDetailVC: UIViewController { // MARK: - Properties private let recordProvider = Providers.recordProvider - - private var activityRecordList = [ActivityRecord]() - - private var courseId: Int? - - private var publicCourseId: Int? - + + private var recordId: Int? + // MARK: - UI Components private lazy var navibar = CustomNavigationBar(self, type: .titleWithLeftButton) @@ -44,9 +41,9 @@ final class ActivityRecordDetailVC: UIViewController { $0.font = .h4 } - private let recordDateInfoView = CourseDetailInfoView(title: "날짜", description: "0000.00.00") + private let recordDateInfoView = CourseDetailInfoView(title: "날짜", description: String()) - private let recordDepartureInfoView = CourseDetailInfoView(title: "출발지", description: "서울시 영등포구") + private let recordDepartureInfoView = CourseDetailInfoView(title: "출발지", description: String()) private lazy var recordInfoStackView = UIStackView(arrangedSubviews: [recordDateInfoView, recordDepartureInfoView]).then { $0.axis = .vertical @@ -70,17 +67,11 @@ final class ActivityRecordDetailVC: UIViewController { $0.text = "평균 페이스" } - private lazy var recordDistanceValueLabel = setBlackTitle().then { - $0.text = "5.1km" - } + private lazy var recordDistanceValueLabel = setBlackTitle() - private lazy var recordRunningTimeValueLabel = setBlackTitle().then { - $0.text = "00:28:07" - } + private lazy var recordRunningTimeValueLabel = setBlackTitle() - private lazy var recordAveragePaceValueLabel = setBlackTitle().then { - $0.text = "5’31’’" - } + private lazy var recordAveragePaceValueLabel = setBlackTitle() private lazy var recordDistanceStackView = setDetailInfoStakcView(title: recordDistanceLabel, value: recordDistanceValueLabel) @@ -105,33 +96,101 @@ final class ActivityRecordDetailVC: UIViewController { setNavigationBar() setUI() setLayout() + setAddTarget() + } +} + +// MARK: - @objc Function + +extension ActivityRecordDetailVC { + @objc func moreButtonDidTap() { + let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + let editAction = UIAlertAction(title: "수정하기", style: .default, handler: {(_: UIAlertAction!) in + //self.navigationController?.pushViewController(courseEditVC, animated: false) + }) + let deleteVC = RNAlertVC(description: "러닝 기록을 정말로 삭제하시겠어요?").setButtonTitle("취소", "삭제하기") + deleteVC.modalPresentationStyle = .overFullScreen + let deleteAction = UIAlertAction(title: "삭제하기", style: .destructive, handler: {(_: UIAlertAction!) in + self.present(deleteVC, animated: false, completion: nil)}) + + deleteVC.rightButtonTapAction = { [weak self] in + deleteVC.dismiss(animated: false) + self?.deleteRecord() + } + + [ editAction, deleteAction ].forEach { alertController.addAction($0) } + present(alertController, animated: true, completion: nil) } } // MARK: - Methods extension ActivityRecordDetailVC { - func setCourseId(courseId: Int?, publicCourseId: Int?) { - self.courseId = courseId - self.publicCourseId = publicCourseId + func setRecordId(recordId: Int?) { + self.recordId = recordId + } + + func setData(model: ActivityRecord) { + self.mapImageView.setImage(with: model.image) + self.courseTitleLabel.text = model.title + + let location = "\(model.departure.region) \(model.departure.city)" + self.recordDepartureInfoView.setDescriptionText(description: location) + + // 날짜 바꾸기 + let recordDate = model.createdAt.prefix(10) + let resultDate = RNTimeFormatter.changeDateSplit(date: String(recordDate)) + self.recordDateInfoView.setDescriptionText(description: resultDate) + + // 이동 시간 바꾸기 + let recordRunningTime = model.time.suffix(7) + self.recordRunningTimeValueLabel.text = String(recordRunningTime) + + // 평균 페이스 바꾸기 + let array = spiltRecordAveragePace(model: model) + setUpRecordAveragePaceValueLabel(array: array, label: recordAveragePaceValueLabel) + setUpRecordDistanceValueLabel(model: model, label: recordDistanceValueLabel) + } + + private func setAddTarget() { + self.moreButton.addTarget(self, action: #selector(moreButtonDidTap), for: .touchUpInside) } func setDetailInfoStakcView(title: UIView, value: UIView) -> UIStackView { let stackView = UIStackView(arrangedSubviews: [title, value]) stackView.axis = .vertical stackView.alignment = .center - stackView.spacing = 2 + stackView.spacing = 8 return stackView } - func setBlackTitle() -> UILabel { + private func spiltRecordAveragePace(model: ActivityRecord) -> [String] { + let recordAveragePace = model.pace + let array = recordAveragePace.split(separator: ":").map { String($0) } + return array + } + + private func setUpRecordAveragePaceValueLabel(array: [String], label: UILabel) { + let numberArray = array.compactMap { Int($0) } // 페이스에서 첫번째 인덱스 두번째 값만 가져오기 위해 + let attributedString = NSMutableAttributedString(string: String(numberArray[1]) + "’", attributes: [.font: UIFont.h3, .foregroundColor: UIColor.g1]) + attributedString.append(NSAttributedString(string: String(array[2]) + "”", attributes: [.font: UIFont.h3, .foregroundColor: UIColor.g1])) + label.attributedText = attributedString + } + + private func setUpRecordDistanceValueLabel(model: ActivityRecord, label: UILabel) { + let attributedString = NSMutableAttributedString(string: String(model.distance) + " ", attributes: [.font: UIFont.h3, .foregroundColor: UIColor.g1]) + attributedString.append(NSAttributedString(string: "km", attributes: [.font: UIFont.b4, .foregroundColor: UIColor.g2])) + label.attributedText = attributedString + } + + private func setBlackTitle() -> UILabel { let label = UILabel() label.textColor = .g1 label.font = .h3 return label } - func setGreyTitle() -> UILabel { + private func setGreyTitle() -> UILabel { let label = UILabel() label.textColor = .g2 label.font = .b4 @@ -142,6 +201,7 @@ extension ActivityRecordDetailVC { // MARK: - Layout Helpers extension ActivityRecordDetailVC { + private func setUI() { view.backgroundColor = .w1 middleScorollView.backgroundColor = .w1 @@ -162,7 +222,7 @@ extension ActivityRecordDetailVC { } moreButton.snp.makeConstraints { make in - make.trailing.equalTo(self.view.safeAreaLayoutGuide).inset(16) + make.trailing.equalTo(self.view.safeAreaLayoutGuide) make.centerY.equalTo(navibar) } } @@ -173,7 +233,7 @@ extension ActivityRecordDetailVC { middleScorollView.snp.makeConstraints { make in make.top.equalTo(navibar.snp.bottom) make.leading.trailing.equalTo(view.safeAreaLayoutGuide) - make.bottom.equalToSuperview() + make.bottom.equalTo(view.safeAreaLayoutGuide) } middleScorollView.addSubviews(mapImageView, courseTitleLabel, firstHorizontalDivideLine, recordInfoStackView, secondHorizontalDivideLine) @@ -197,7 +257,7 @@ extension ActivityRecordDetailVC { recordInfoStackView.snp.makeConstraints { make in make.top.equalTo(firstHorizontalDivideLine.snp.bottom).offset(20) - make.leading.trailing.equalToSuperview().inset(16) + make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16) } firstVerticalDivideLine.snp.makeConstraints { make in @@ -239,6 +299,7 @@ extension ActivityRecordDetailVC { recordSubInfoStackView.snp.makeConstraints { make in make.top.equalTo(secondHorizontalDivideLine.snp.bottom).offset(23) make.centerX.equalToSuperview() + make.bottom.equalToSuperview().inset(30) } } } @@ -246,22 +307,21 @@ extension ActivityRecordDetailVC { // MARK: - Network extension ActivityRecordDetailVC { - func getActivityRecordDetailWithPath() { + private func deleteRecord() { + guard let recordId = self.recordId else { return } + print(recordId) LoadingIndicator.showLoading() - recordProvider.request(.getActivityRecordInfo) { [weak self] response in + recordProvider.request(.deleteRecord(recordIdList: [recordId])) { [weak self] response in LoadingIndicator.hideLoading() guard let self = self else { return } switch response { case .success(let result): + print("result:", 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(activityRecordList: data.records) - } catch { - print(error.localizedDescription) - } + print("삭제 성공") + self.navigationController?.popViewController(animated: false) + } if status >= 400 { print("400 error") diff --git a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoTableView/ActivityRecordInfoTVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoTableView/ActivityRecordInfoTVC.swift index 5adb737f..8e9083b9 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoTableView/ActivityRecordInfoTVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoTableView/ActivityRecordInfoTVC.swift @@ -135,7 +135,8 @@ extension ActivityRecordInfoTVC { } private func setUpActivityRecordAveragePaceValueLabel(array: [String], label: UILabel) { - let attributedString = NSMutableAttributedString(string: String(array[1]) + "’", attributes: [.font: UIFont.h5, .foregroundColor: UIColor.g1]) + let numberArray = array.compactMap { Int($0) } /// 페이스에서 첫번째 인덱스 두번째 값만 가져오기 위해 + let attributedString = NSMutableAttributedString(string: String(numberArray[1]) + "’", attributes: [.font: UIFont.h5, .foregroundColor: UIColor.g1]) attributedString.append(NSAttributedString(string: String(array[2]) + "”", attributes: [.font: UIFont.h5, .foregroundColor: UIColor.g1])) label.attributedText = attributedString } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoVC.swift index 0501a3f2..52f18009 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoVC.swift @@ -11,7 +11,11 @@ import SnapKit import Then import Moya -final class ActivityRecordInfoVC: UIViewController { +protocol deleteRecordDelegate: AnyObject { + func wantsToDelete() +} + +final class ActivityRecordInfoVC: UIViewController, deleteRecordDelegate { // MARK: - Properties @@ -19,8 +23,12 @@ final class ActivityRecordInfoVC: UIViewController { private var activityRecordList = [ActivityRecord]() + private var deleteRecordList = [Int]() + + weak var delegate: deleteRecordDelegate? + private var isEditMode: Bool = false - + // MARK: - UI Components private lazy var navibar = CustomNavigationBar(self, type: .titleWithLeftButton).setTitle("러닝 기록") @@ -69,6 +77,11 @@ final class ActivityRecordInfoVC: UIViewController { getActivityRecordInfo() self.hideTabBar(wantsToHide: true) } + +// override func viewWillAppear(_ animated: Bool) { +// super.viewWillAppear(animated) +// self.reloadActivityRecordInfoVC() +// } } // MARK: - Methods @@ -93,6 +106,38 @@ extension ActivityRecordInfoVC { private func setAddTarget() { self.editButton.addTarget(self, action: #selector(editButtonDidTap), for: .touchUpInside) + self.deleteRecordButton.addTarget(self, action: #selector(deleteRecordButtonDidTap), for: .touchUpInside) + } + + func reloadActivityRecordInfoVC() { + self.activityRecordTableView.reloadData() + self.editButton.setTitle("편집", for: .normal) + self.deleteRecordButton.isHidden = true + self.totalNumOfRecordlabel.text = "총 기록 \(self.activityRecordList.count)개" + if let selectedRows = activityRecordTableView.indexPathsForSelectedRows { + for indexPath in selectedRows { + activityRecordTableView.deselectRow(at: indexPath, animated: true) + } + } + self.deleteRecordButton.isEnabled = false + self.deleteRecordButton.setTitle(title: "삭제하기") + self.activityRecordTableView.reloadData() + } +} + +// MARK: - deleteRecordDelegate + +extension ActivityRecordInfoVC { + func wantsToDelete() { + print("삭제 실행") + + guard let selectedRecords = activityRecordTableView.indexPathsForSelectedRows else { return } + + for indexPath in selectedRecords { + self.deleteRecordList.append(activityRecordList[indexPath.row].id) + } + + deleteRecord() } } @@ -121,6 +166,16 @@ extension ActivityRecordInfoVC { isEditMode = true } } + + @objc func deleteRecordButtonDidTap() { + let deleteVC = RNAlertVC(description: "러닝 기록을 정말로 삭제하시겠어요?").setButtonTitle("취소", "삭제하기") + deleteVC.modalPresentationStyle = .overFullScreen + deleteVC.deleteRecordDelegate = self + self.present(deleteVC, animated: false, completion: nil) + deleteVC.rightButtonTapAction = { [weak self] in + deleteVC.dismiss(animated: false) + } + } } // MARK: - Layout Helpers @@ -194,9 +249,8 @@ extension ActivityRecordInfoVC: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard tableView.cellForRow(at: indexPath) is ActivityRecordInfoTVC else { return } guard let selectedRecords = tableView.indexPathsForSelectedRows else { return } - let activityRecordList = activityRecordList[indexPath.item] - //ActivityRecordDetailVC.setCourseId(courseId: <#T##Int?#>, publicCourseId: <#T##Int?#>) - + let activityRecordDetailVC = ActivityRecordDetailVC() + if isEditMode { self.deleteRecordButton.isEnabled = true let countSelectedRows = selectedRecords.count @@ -204,8 +258,11 @@ extension ActivityRecordInfoVC: UITableViewDelegate { } else { tableView.deselectRow(at: indexPath, animated: true) self.deleteRecordButton.setTitle(title: "삭제하기") - // 편집 모드가 아닐 때 상세 페이지로 이동 + activityRecordDetailVC.setData(model: activityRecordList[indexPath.row]) + activityRecordDetailVC.setRecordId(recordId: activityRecordList[indexPath.row].id) + // 편집 모드가 아닐 때 상세 페이지로 이동 + self.navigationController?.pushViewController(activityRecordDetailVC, animated: true) } } @@ -222,7 +279,8 @@ extension ActivityRecordInfoVC: UITableViewDelegate { } else { tableView.deselectRow(at: indexPath, animated: true) self.deleteRecordButton.setTitle(title: "삭제하기") - } } + } + } } // MARK: - UITableViewDataSource @@ -280,6 +338,7 @@ extension ActivityRecordInfoVC { let responseDto = try result.map(BaseResponse.self) guard let data = responseDto.data else { return } self.setData(activityRecordList: data.records) + } catch { print(error.localizedDescription) } @@ -294,4 +353,29 @@ extension ActivityRecordInfoVC { } } } + + func deleteRecord() { + let deleteRecordList = self.deleteRecordList + LoadingIndicator.showLoading() + recordProvider.request(.deleteRecord(recordIdList: deleteRecordList)) { [weak self] response in + LoadingIndicator.hideLoading() + guard let self = self else { return } + switch response { + case .success(let result): + print("result:", result) + let status = result.statusCode + if 200..<300 ~= status { + print("삭제 성공") + self.reloadActivityRecordInfoVC() + } + 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/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift index 283ec60c..6f2498c7 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift @@ -121,12 +121,11 @@ extension UploadedCourseInfoVC: UICollectionViewDelegateFlowLayout { } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - - let courseDetailVC = CourseDetailVC() - let courseModel = uploadedCourseList[indexPath.item] - courseDetailVC.setCourseId(courseId: courseModel.courseId, publicCourseId: courseModel.id) - courseDetailVC.hidesBottomBarWhenPushed = true - self.navigationController?.pushViewController(courseDetailVC, animated: true) + let courseDetailVC = CourseDetailVC() + let courseModel = uploadedCourseList[indexPath.item] + courseDetailVC.setCourseId(courseId: courseModel.courseId, publicCourseId: courseModel.id) + courseDetailVC.hidesBottomBarWhenPushed = true + self.navigationController?.pushViewController(courseDetailVC, animated: true) } }