diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index b340e880..45f38ad3 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ A3BC2F432966A93100198261 /* CourseDetailVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3BC2F422966A93100198261 /* CourseDetailVC.swift */; }; 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 */; }; 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 */; }; @@ -156,6 +157,7 @@ A3BC2F422966A93100198261 /* CourseDetailVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDetailVC.swift; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; @@ -411,6 +413,7 @@ children = ( CE10063D29680C8100FD31FB /* .gitkeep */, A3F67AE1296D33AC001598A2 /* MyPageDto.swift */, + A3F67AE9296E4936001598A2 /* ActivityRecordInfoDto.swift */, ); path = MyPageDto; sourceTree = ""; @@ -1325,6 +1328,7 @@ CE6655C8295D849F00C64E12 /* StringLiterals.swift in Sources */, DAD5A3E4296D526D00C8166B /* CourseDiscoveryRouter.swift in Sources */, CEEC6B3E2961C53700D00E1E /* CourseDiscoveryVC.swift in Sources */, + A3F67AEA296E4936001598A2 /* ActivityRecordInfoDto.swift in Sources */, CE6655E0295D87D200C64E12 /* UIDevice+.swift in Sources */, DA97A02F296DC3300086760E /* CourseSearchingRouter.swift in Sources */, CE40BB242968068E0030ABCA /* Config.swift in Sources */, diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/RNTimeFormatter.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/RNTimeFormatter.swift index 30eb9e92..2caa823f 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/RNTimeFormatter.swift +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/RNTimeFormatter.swift @@ -30,4 +30,15 @@ class RNTimeFormatter { return formatter.string(from: date) } + + static func changeDateSplit(date: String) -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd" + let convertDate = dateFormatter.date(from: date) + + let resultDateFormatter = DateFormatter() + resultDateFormatter.dateFormat = "yyyy.MM.dd" + + return resultDateFormatter.string(from: convertDate!) + } } diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/MyPageDto/ActivityRecordInfoDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/MyPageDto/ActivityRecordInfoDto.swift new file mode 100644 index 00000000..71b8f553 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/MyPageDto/ActivityRecordInfoDto.swift @@ -0,0 +1,33 @@ +// +// ActivityRecordInfoDto.swift +// Runnect-iOS +// +// Created by 몽이 누나 on 2023/01/11. +// + +import Foundation + +// MARK: - ActivityRecordInfoDto + +struct ActivityRecordInfoDto: Codable { + let records: [ActivityRecord] +} + +// MARK: - Record + +struct ActivityRecord: Codable { + let id, courseId: Int + let publicCourseId: Int? + let machineId, title: String + let image: String + let createdAt: String + let distance: Double + let time, pace: String + let departure: ActivityRecordDeparture +} + +// MARK: - Departure + +struct ActivityRecordDeparture: Codable { + let region, city: String +} diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/MyPageRouter/MyPageRouter.swift b/Runnect-iOS/Runnect-iOS/Network/Router/MyPageRouter/MyPageRouter.swift index 54728c92..01a65f93 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Router/MyPageRouter/MyPageRouter.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Router/MyPageRouter/MyPageRouter.swift @@ -12,6 +12,7 @@ import Moya enum MyPageRouter { case getMyPageInfo case getUploadedCourseInfo + case getActivityRecordInfo } extension MyPageRouter: TargetType { @@ -29,6 +30,8 @@ extension MyPageRouter: TargetType { return "/user" case .getUploadedCourseInfo: return "/public-course/user" + case .getActivityRecordInfo: + return "/record/user" } } @@ -38,6 +41,8 @@ extension MyPageRouter: TargetType { return .get case .getUploadedCourseInfo: return .get + case .getActivityRecordInfo: + return .get } } @@ -47,6 +52,8 @@ extension MyPageRouter: TargetType { return .requestPlain case .getUploadedCourseInfo: return .requestPlain + case .getActivityRecordInfo: + return .requestPlain } } @@ -58,6 +65,9 @@ extension MyPageRouter: TargetType { case .getUploadedCourseInfo: return ["Content-Type": "application/json", "machineId": "1"] + case .getActivityRecordInfo: + return ["Content-Type": "application/json", + "machineId": "1"] } } } 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 8d1c979e..6f5a5ca9 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 @@ -6,8 +6,10 @@ // import UIKit + import SnapKit import Then +import Kingfisher final class ActivityRecordInfoTVC: UITableViewCell { @@ -23,7 +25,7 @@ final class ActivityRecordInfoTVC: UITableViewCell { private let firstVerticalDivideLine = UIView() private let secondVerticalDivideLine = UIView() - private let activityRecordMapImage = UIView().then { + private let activityRecordMapImage = UIImageView().then { $0.layer.cornerRadius = 10 } @@ -71,7 +73,7 @@ final class ActivityRecordInfoTVC: UITableViewCell { private lazy var activityRecordSubInfoStackView = UIStackView(arrangedSubviews: [activityRecordTotalDistanceStackView, firstVerticalDivideLine, activityRecordRunningTimeStackView, secondVerticalDivideLine, activityRecordAveragePaceStackView]).then { $0.axis = .horizontal - $0.distribution = .fillProportionally + $0.distribution = .fill } // MARK: - Life Cycles @@ -87,6 +89,57 @@ final class ActivityRecordInfoTVC: UITableViewCell { } } +// MARK: - Methods + +extension ActivityRecordInfoTVC { + func setData(model: ActivityRecord) { + guard let imageURL = URL(string: model.image) else { return } + + // 날짜 바꾸기 + let activityRecordDate = model.createdAt.prefix(10) + let resultDate = RNTimeFormatter.changeDateSplit(date: String(activityRecordDate)) + + // 이동 시간 바꾸기 + let activityRecordRunningTime = model.time.suffix(7) + + // 평균 페이스 바꾸기 + let activityRecordAveragePace = model.pace + let array = spiltActivityRecordAveragePace(model: model) + + activityRecordTitleLabel.text = model.title + setUpActivityRecordPlaceLabel(model: model, label: activityRecordPlaceLabel) + activityRecordDateLabel.text = resultDate + setUpactivityRecordTotalDistanceValueLabel(model: model, label: activityRecordTotalDistanceValueLabel) + activityRecordRunningTimeValueLabel.text = String(activityRecordRunningTime) + setUpActivityRecordAveragePaceValueLabel(array: array, label: activityRecordAveragePaceValueLabel) + self.activityRecordMapImage.kf.setImage(with: imageURL) + } + + private func setUpActivityRecordPlaceLabel(model: ActivityRecord, label: UILabel) { + let attributedString = NSMutableAttributedString(string: String(model.departure.region) + " ", attributes: [.font: UIFont.b8, .foregroundColor: UIColor.g2]) + attributedString.append(NSAttributedString(string: String(model.departure.city), attributes: [.font: UIFont.b8, .foregroundColor: UIColor.g2])) + label.attributedText = attributedString + } + + private func setUpactivityRecordTotalDistanceValueLabel(model: ActivityRecord, label: UILabel) { + let attributedString = NSMutableAttributedString(string: String(model.distance) + " ", attributes: [.font: UIFont.h5, .foregroundColor: UIColor.g1]) + attributedString.append(NSAttributedString(string: "km", attributes: [.font: UIFont.b4, .foregroundColor: UIColor.g1])) + label.attributedText = attributedString + } + + private func setUpActivityRecordAveragePaceValueLabel(array: [String], label: UILabel) { + let attributedString = NSMutableAttributedString(string: String(array[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 + } + + private func spiltActivityRecordAveragePace(model: ActivityRecord) -> [String] { + let activityRecordAveragePace = model.pace + let array = activityRecordAveragePace.split(separator: ":").map { String($0) } + return array + } +} + extension ActivityRecordInfoTVC { // MARK: - Method @@ -167,9 +220,28 @@ extension ActivityRecordInfoTVC { make.width.equalTo(1) } + setActivityRecordSubInfoStackView() + } + + private func setActivityRecordSubInfoStackView() { + let screenWidth = UIScreen.main.bounds.width + let containerViewWidth = screenWidth - 32 + let stackViewWidth = Int(containerViewWidth - 2) / 3 + + activityRecordTotalDistanceStackView.snp.makeConstraints { make in + make.width.equalTo(stackViewWidth) + } + + activityRecordRunningTimeStackView.snp.makeConstraints { make in + make.width.equalTo(stackViewWidth) + } + + activityRecordAveragePaceStackView.snp.makeConstraints { make in + make.width.equalTo(stackViewWidth) + } + activityRecordSubInfoStackView.snp.makeConstraints { make in make.top.equalTo(horizontalDivideLine.snp.bottom).offset(15) - make.width.equalToSuperview() make.centerX.equalToSuperview() } } @@ -178,15 +250,4 @@ extension ActivityRecordInfoTVC { super.layoutSubviews() contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: 16, right: 0)) } - - // MARK: - General Helpers - - func dataBind(model: ActivityRecordInfoModel) { - activityRecordTitleLabel.text = model.title - activityRecordPlaceLabel.text = model.place - activityRecordDateLabel.text = model.date - activityRecordTotalDistanceValueLabel.text = model.distance - activityRecordRunningTimeValueLabel.text = model.runningTime - activityRecordAveragePaceValueLabel.text = model.averagePace - } } 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 85a7547e..b9281a4d 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoVC.swift @@ -6,21 +6,20 @@ // import UIKit + import SnapKit import Then +import Moya final class ActivityRecordInfoVC: UIViewController { // MARK: - Properties - var activityRecordList: [ActivityRecordInfoModel] = [ - ActivityRecordInfoModel(title: "석촌 호수 한 바퀴", place: "서울시 강동구", date: "2022.12.28", distance: "4.01 km", runningTime: "0:27:36", averagePace: "6'45\""), - ActivityRecordInfoModel(title: "석촌 호수 한 바퀴", place: "서울시 강동구", date: "2022.12.29", distance: "4.01 km", runningTime: "0:27:36", averagePace: "6'45\""), - ActivityRecordInfoModel(title: "석촌 호수 한 바퀴", place: "서울시 강동구", date: "2022.12.30", distance: "4.01 km", runningTime: "0:27:36", averagePace: "6'45\""), - ActivityRecordInfoModel(title: "석촌 호수 한 바퀴", place: "서울시 강동구", date: "2022.12.31", distance: "4.01 km", runningTime: "0:27:36", averagePace: "6'45\""), - ActivityRecordInfoModel(title: "석촌 호수 한 바퀴", place: "서울시 강동구", date: "2022.12.28", distance: "4.01 km", runningTime: "0:27:36", averagePace: "6'45\""), - ActivityRecordInfoModel(title: "석촌 호수 한 바퀴", place: "서울시 강동구", date: "2022.12.28", distance: "4.01 km", runningTime: "0:27:36", averagePace: "6'45\"") - ] + private var activityRecordProvider = MoyaProvider( + plugins: [NetworkLoggerPlugin(verbose: true)] + ) + + private var activityRecordList = [ActivityRecord]() // MARK: - UI Components @@ -29,8 +28,6 @@ final class ActivityRecordInfoVC: UIViewController { private lazy var activityRecordTableView = UITableView().then { $0.showsVerticalScrollIndicator = false $0.separatorStyle = .none - $0.delegate = self - $0.dataSource = self } // MARK: - View Life Cycle @@ -40,7 +37,27 @@ final class ActivityRecordInfoVC: UIViewController { setNavigationBar() setUI() setLayout() + setDelegate() register() + getActivityRecordInfo() + } +} + +// MARK: - Methods + +extension ActivityRecordInfoVC { + private func setData(activityRecordList: [ActivityRecord]) { + self.activityRecordList = activityRecordList + activityRecordTableView.reloadData() + } + + private func setDelegate() { + self.activityRecordTableView.delegate = self + self.activityRecordTableView.dataSource = self + } + + private func register() { + self.activityRecordTableView.register(ActivityRecordInfoTVC.self, forCellReuseIdentifier: ActivityRecordInfoTVC.className) } } @@ -71,12 +88,6 @@ extension ActivityRecordInfoVC { make.bottom.equalToSuperview() } } - - // MARK: - General Helpers - - private func register() { - activityRecordTableView.register(ActivityRecordInfoTVC.self, forCellReuseIdentifier: ActivityRecordInfoTVC.className) - } } // MARK: - UITableViewDelegate @@ -97,7 +108,39 @@ extension ActivityRecordInfoVC: UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let activityRecordCell = tableView.dequeueReusableCell(withIdentifier: ActivityRecordInfoTVC.className, for: indexPath) as? ActivityRecordInfoTVC else { return UITableViewCell()} activityRecordCell.selectionStyle = .none - activityRecordCell.dataBind(model: activityRecordList[indexPath.item]) + activityRecordCell.setData(model: activityRecordList[indexPath.item]) return activityRecordCell } } + +// MARK: - Network + +extension ActivityRecordInfoVC { + func getActivityRecordInfo() { + LoadingIndicator.showLoading() + activityRecordProvider.request(.getActivityRecordInfo) { [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(activityRecordList: data.records) + } 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/MyPage/VC/InfoVC/UploadedCourseInfoCollectionView/UploadedCourseInfoCVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoCollectionView/UploadedCourseInfoCVC.swift index 8ac48bfd..c239483b 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoCollectionView/UploadedCourseInfoCVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoCollectionView/UploadedCourseInfoCVC.swift @@ -84,12 +84,4 @@ extension UploadedCourseInfoCVC { make.leading.equalToSuperview() } } - - // MARK: - General Helpers - - func dataBind(model: UploadedCourseInfoModel) { - uploadedCourseTitleLabel.text = model.title - uploadedCoursePlaceLabel.text = model.place - } - }