diff --git a/ttubeokAR/ttubeokAR.xcodeproj/project.pbxproj b/ttubeokAR/ttubeokAR.xcodeproj/project.pbxproj index 99dde57..7beddf9 100644 --- a/ttubeokAR/ttubeokAR.xcodeproj/project.pbxproj +++ b/ttubeokAR/ttubeokAR.xcodeproj/project.pbxproj @@ -59,6 +59,7 @@ 483CB9602B732DC200938224 /* GameSettingSubTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 483CB95F2B732DC200938224 /* GameSettingSubTitle.swift */; }; 483CB9622B732F4A00938224 /* GameBenefitsTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 483CB9612B732F4A00938224 /* GameBenefitsTextField.swift */; }; 483CB9642B73329700938224 /* GameBenefitsText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 483CB9632B73329700938224 /* GameBenefitsText.swift */; }; + 484272B42BEBFD3800D099F1 /* GuestBookAPITarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484272B32BEBFD3800D099F1 /* GuestBookAPITarget.swift */; }; 4844E13F2B5A243700E6619C /* Icon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4844E13E2B5A243700E6619C /* Icon.swift */; }; 484C16AD2B7ED4CE00257229 /* AuthPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484C16AC2B7ED4CE00257229 /* AuthPlugin.swift */; }; 484C16AF2B7ED7EA00257229 /* RefreshService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484C16AE2B7ED7EA00257229 /* RefreshService.swift */; }; @@ -257,6 +258,7 @@ 483CB95F2B732DC200938224 /* GameSettingSubTitle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSettingSubTitle.swift; sourceTree = ""; }; 483CB9612B732F4A00938224 /* GameBenefitsTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameBenefitsTextField.swift; sourceTree = ""; }; 483CB9632B73329700938224 /* GameBenefitsText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameBenefitsText.swift; sourceTree = ""; }; + 484272B32BEBFD3800D099F1 /* GuestBookAPITarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestBookAPITarget.swift; sourceTree = ""; }; 4844E13E2B5A243700E6619C /* Icon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Icon.swift; sourceTree = ""; }; 484C16AC2B7ED4CE00257229 /* AuthPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthPlugin.swift; sourceTree = ""; }; 484C16AE2B7ED7EA00257229 /* RefreshService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshService.swift; sourceTree = ""; }; @@ -1240,6 +1242,7 @@ CA3DFFF22B6E42B30007D2E6 /* APITarget */ = { isa = PBXGroup; children = ( + 484272B32BEBFD3800D099F1 /* GuestBookAPITarget.swift */, CA3DFFEC2B6CE7C90007D2E6 /* ExploreAPITarget.swift */, 4831BEBF2B742866006E184C /* SearchAPITarget.swift */, CA3DFFF02B6E42AE0007D2E6 /* DetailExploreAPITarget.swift */, @@ -1534,6 +1537,7 @@ CAFAE8332B75771D00C20502 /* CommentAPITarget.swift in Sources */, 483820E42B4BE80700C3FB14 /* SettingsCommand.swift in Sources */, CA2513092B62918A006D2EDF /* GuestBookModel.swift in Sources */, + 484272B42BEBFD3800D099F1 /* GuestBookAPITarget.swift in Sources */, 48A12C222B5CF84100E51200 /* NicknameRedundancyService.swift in Sources */, 48A6C6B62B5F800900CFF763 /* KeyChainManager.swift in Sources */, 4861FF242B54E35C00DC6E2D /* CustomSlider.swift in Sources */, diff --git a/ttubeokAR/ttubeokAR/ExploreView/ExploreAPI/APITarget/GuestBookAPITarget.swift b/ttubeokAR/ttubeokAR/ExploreView/ExploreAPI/APITarget/GuestBookAPITarget.swift new file mode 100644 index 0000000..3d0474f --- /dev/null +++ b/ttubeokAR/ttubeokAR/ExploreView/ExploreAPI/APITarget/GuestBookAPITarget.swift @@ -0,0 +1,64 @@ +// +// GuestBookAPITarget.swift +// ttubeokAR +// +// Created by 정의찬 on 5/9/24. +// + +import Foundation +import Moya + +enum GuestBookAPITarget { + case getImage(guestBookId: Int, token: String) + case getSpotGuestBookData(spotId: Int, page: Int, token: String) + case getStoreGuestBookData(storeId: Int, page: Int, token: String) +} + +extension GuestBookAPITarget: TargetType { + var baseURL: URL { return URL(string: "http://43.201.79.99:8080")!} + + var path: String { + switch self { + case .getImage: + return "/api/v1/image/guestbook" + case .getSpotGuestBookData: + return "/api/v1/guestbook" + case .getStoreGuestBookData: + return "/api/v1/guestbook" + } + } + + var method: Moya.Method { + switch self { + case .getImage: + return .get + case .getSpotGuestBookData: + return .get + case .getStoreGuestBookData: + return .get + } + } + + var task: Task { + switch self { + case .getImage(let id, _): + return .requestParameters(parameters: ["guestBookId": id], encoding: URLEncoding.queryString) + case .getSpotGuestBookData(let id, let page, _): + return .requestParameters(parameters: ["spotId": id, "pageNum": page], encoding: URLEncoding.queryString) + case .getStoreGuestBookData(let id, let page, _): + return .requestParameters(parameters: ["storeId": id, "pageNum": page] , encoding: URLEncoding.queryString) + } + } + + var headers: [String : String]? { + let token: String + switch self { + case .getSpotGuestBookData(_, _, let tokenValue), .getStoreGuestBookData(_, _, let tokenValue) ,.getImage(_, let tokenValue): + token = tokenValue + } + return [ + "Content-Type": "application/json", + "Authorization": "Bearer \(token)" + ] + } +} diff --git a/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/DetailView.swift b/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/DetailView.swift index 628ac1c..2386dcf 100644 --- a/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/DetailView.swift +++ b/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/DetailView.swift @@ -58,7 +58,7 @@ struct DetailViewControl: View { } } .padding([.leading, .trailing], 20) - // guestBookGrid + guestBookGrid } } @@ -400,19 +400,34 @@ struct DetailViewControl: View { } - // //MARK: - 스크롤 방명록 보기 - // private var guestBookGrid: some View { - // ScrollView(.vertical, showsIndicators: false) { - // LazyVGrid(columns: [GridItem(.flexible(minimum: 150), spacing: 10), GridItem(.flexible(minimum: 150), spacing: 100)], spacing: 13) { - // ForEach(viewModel.guestBookModel?.information ?? [], id: \.self) { information in - // GuestBookCard(guestBookModelInfor: information) - // .frame(minWidth: 0, maxWidth: .infinity, minHeight: 200) - // } - // .frame(maxWidth: 300, maxHeight: .infinity, alignment: .center) - // } + //MARK: - 스크롤 방명록 보기 + // private var guestBookGrid: some View { + // ScrollView(.vertical, showsIndicators: false) { + // LazyVGrid(columns: [GridItem(.flexible(minimum: 150), spacing: 10), GridItem(.flexible(minimum: 150), spacing: 100)], spacing: 13) { + // ForEach(viewModel.guestBookModel?.information ?? [], id: \.self) { information in + // GuestBookCard(guestBookModelInfor: information) + // .frame(minWidth: 0, maxWidth: .infinity, minHeight: 200) // } + // .frame(maxWidth: 300, maxHeight: .infinity, alignment: .center) // } - // + // } + // } + + private var guestBookGrid: some View { + ScrollView(.vertical, showsIndicators: false) { + LazyVGrid(columns: [ + GridItem(.flexible(maximum: 200)), + GridItem(.flexible(maximum: 200)) + ], spacing: 20) { + ForEach(0..<12, id: \.self) { information in + GuestBookCard() + .frame(minWidth: 0, maxWidth: .infinity, minHeight: 200) + } + } + } + .frame(maxWidth: 360, maxHeight: .infinity, alignment: .center) + } + } diff --git a/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/DetailViewModel.swift b/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/DetailViewModel.swift index 9fa2ee0..b3f0e47 100644 --- a/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/DetailViewModel.swift +++ b/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/DetailViewModel.swift @@ -16,20 +16,21 @@ class DetailViewModel: NSObject, ObservableObject,CLLocationManagerDelegate { private let authPlugin: AuthPlugin private let provider: MoyaProvider -// private let guestProvider: MoyaProvider + private let guestProvider: MoyaProvider private let likeProvider: MoyaProvider override init() { self.authPlugin = AuthPlugin(provider: MoyaProvider()) self.provider = MoyaProvider(plugins: [authPlugin]) self.likeProvider = MoyaProvider(plugins: [authPlugin]) + self.guestProvider = MoyaProvider(plugins: [authPlugin]) } //MARK: - Model @Published var walkwayDetailDataModel: WalkwayDetailDataModel? @Published var storeDetailDataModel: StoreDetailDataModel? @Published var walkwayImageModel: WalkImageModel? - @Published var storeImageModel : StoreImageModel? + @Published var storeImageModel: StoreImageModel? @Published var guestBookModel: GuestBookModel? // MARK: - Property @@ -39,34 +40,73 @@ class DetailViewModel: NSObject, ObservableObject,CLLocationManagerDelegate { @Published var currentImageIndex = 0 @Published var placeType: PlaceTypeValue = .spot + private var placeId: Int? = nil + var locationManager = CLLocationManager() var currentLocation: CLLocation? { locationManager.location } + // MARK: - ScrollProperty + var currentPage: Int = 0 + var isLoading: Bool = false + //MARK: - API Fetch 함수 /// 장소 타입에 맞춰 API 호출 /// - Parameter place: 전달 받은 장소 타입 public func fetchDetails(for place: ExploreDetailInfor) { + self.placeType = place.placeType.spot ? .spot : .store + self.placeId = place.placeId + + guard let placeId = self.placeId else { + print("placeId nil 저장됨") + return + } + + if place.placeType.spot { - self.placeType = .spot - walkWayGet(get: place) + walkWayGet(get: placeId) } else if place.placeType.store { - self.placeType = .store - storeGet(get: place) + storeGet(get: placeId) + } + } + + //MARK: - 방명록 API Fetch 함수 + + /// 방명록 카드 스크롤 API 호출 + /// - Parameter page: 페이징을 값 입력 + private func fetchScrollData(page: Int) { + if placeType == .spot { + getSpotData(page: page, placeId: placeId ?? 0) + } else if placeType == .store { + getStoreData(page: page, placeId: placeId ?? 0) } } + /// 방명록 스크롤 다음 페이지 로드 + public func nextPageLoading() { + guard !isLoading else { return } + isLoading = true + currentPage += 1 + fetchScrollData(page: currentPage) + } + + /// 초기 방명록 스크롤 페이지 로드 + /// - Parameter page: 다음 페이지 로드 + public func initialFetchScroll(page: Int) { + fetchScrollData(page: page) + } + //MARK: - 산책로 상세 조회 처리 /// 선택한 산책로 데이터를 가져온다. /// - Parameter place: 선택한 산책로 정보 - private func walkWayGet(get place: ExploreDetailInfor) { + private func walkWayGet(get placeId: Int) { guard let accessToken = KeyChainManager.standard.getAccessToken(for: "userSession") else { return } - provider.request(.fetchWalkWayDetail(spotId: place.placeId, token: accessToken)) { [weak self] result in + provider.request(.fetchWalkWayDetail(spotId: placeId, token: accessToken)) { [weak self] result in switch result { case .success(let response): do { @@ -114,11 +154,11 @@ class DetailViewModel: NSObject, ObservableObject,CLLocationManagerDelegate { /// 매장 상세 데이터 조회 /// - Parameter place: 장소 타입 - private func storeGet(get place: ExploreDetailInfor) { + private func storeGet(get placeId: Int) { guard let accessToken = KeyChainManager.standard.getAccessToken(for: "userSession") else { return } - provider.request(.fetchStoreDetail(storeId: place.placeId, token: accessToken)) { [weak self] result in + provider.request(.fetchStoreDetail(storeId: placeId, token: accessToken)) { [weak self] result in switch result { case .success(let response): do { @@ -161,9 +201,86 @@ class DetailViewModel: NSObject, ObservableObject,CLLocationManagerDelegate { } } } + // MARK: - 방명록 카드 조회 + /// 페이지 초기화 + public func resetPage() { + currentPage = 0 + } + + /// 방명록 SpotData 받아오는 API 호출 함수 + /// - Parameter page: 받고자 하는 페이지 값 + /// - Parameter placeId: 받고자하는 placeId 입력 + public func getSpotData(page: Int, placeId: Int) { + guard let accessToken = KeyChainManager.standard.getAccessToken(for: "userSession") else { + print("상세 장소 Spot 방명록 조회 액세스 가져오기 오류") + return + } + + guestProvider.request(.getSpotGuestBookData(spotId: self.placeId ?? 0, + page: page, + token: accessToken)) { + [weak self] result in + + switch result { + case .success(let response): + do { + let decodedData = try JSONDecoder().decode(GuestBookModel.self, from: response.data) + DispatchQueue.main.async { + if page == 0 { + self?.guestBookModel = decodedData + print("방명록 데이터 Spot 조회 1페이지 디코드 완료") + } else { + self?.guestBookModel?.information.append(contentsOf: decodedData.information) + print("방명록 데이터 Spot 조회 추가 페이지 디코드 완료") + } + self?.currentPage = page + } + } catch { + print("방명록 데이터 Spot 조회 오류: \(error)") + } + case .failure(let error): + print("방명록 데이터 Spot 조회 네트워크 오류: \(error)") + } + } + } - //MARK: - 방명록 불러오기 API + /// 방명록 StoreData 받아오는 API 호출 함수 + /// - Parameters: + /// - page: 받고자 하는 페이지 값 + /// - placeId: 받고자하는 placeId 입력 + public func getStoreData(page: Int, placeId: Int) { + guard let accessToken = KeyChainManager.standard.getAccessToken(for: "userSession") else { + print("상세 장소 Store 방명록 조회 액세스 가져오기 오류") + return + } + + guestProvider.request(.getStoreGuestBookData(storeId: placeId, + page: page, + token: accessToken)) { + [weak self] result in + + switch result { + case .success(let response): + do { + let decodedData = try JSONDecoder().decode(GuestBookModel.self, from: response.data) + DispatchQueue.main.async { + if page == 0 { + self?.guestBookModel = decodedData + print("방명록 데이터 Store 조회 1페이지 디코더 완료") + } else { + self?.guestBookModel?.information.append(contentsOf: decodedData.information) + } + self?.currentPage = page + } + } catch { + print("방명록 데이터 Store 조회 오류: \(error)") + } + case .failure(let error): + print("방명록 데이터 Store 조회 네트워크 오류: \(error)") + } + } + } // MARK: - 방문하기 버튼 diff --git a/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/GuestBookCard/GuestBookCard.swift b/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/GuestBookCard/GuestBookCard.swift index fb201a9..c853542 100644 --- a/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/GuestBookCard/GuestBookCard.swift +++ b/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/GuestBookCard/GuestBookCard.swift @@ -36,7 +36,7 @@ struct GuestBookCard : View { /// 방명록 카드 배경화면 private var backGround: some View { RoundedRectangle(cornerRadius: 24) - .frame(maxWidth: 135, maxHeight: 180) + .frame(maxWidth: 160, maxHeight: 200) .foregroundStyle(Color(red: 160 / 255.0, green: 158 / 255.0, blue: 241 / 255.0, opacity: 0.30)) .overlay( RoundedRectangle(cornerRadius: 24) diff --git a/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/GuestBookCard/GuestBookModel.swift b/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/GuestBookCard/GuestBookModel.swift index 0e9e382..88c2714 100644 --- a/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/GuestBookCard/GuestBookModel.swift +++ b/ttubeokAR/ttubeokAR/ExploreView/ExploreDetail/GuestBookCard/GuestBookModel.swift @@ -8,7 +8,7 @@ import Foundation -/// 방명록 받아오는 데이터 +// MARK: - GuestBookData struct GuestBookModel: Codable, Hashable { var check: Bool var information: [GuestBookModelInfor] @@ -25,7 +25,7 @@ struct GuestBookModelInfor: Codable, Hashable { var star : Float } - +// MARK: - GuestBookImage struct GuestBookImage: Codable { var check: Bool var information: GuestBookImageData