From 02764f5fe835c6c538a2814a2e2879a8dcda5ec3 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Wed, 20 Dec 2023 14:28:20 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BB=A4=EB=AE=A4=EB=8B=88=ED=8B=B0=20?= =?UTF-8?q?=EA=B2=8C=EC=8B=9C=EB=AC=BC=20=EC=A0=84=EC=B2=B4=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0,=20=EC=A2=8B=EC=95=84=EC=9A=94,=20=EB=8C=93=EA=B8=80?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20API=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CustomView/IconAndTitleToggleButton.swift | 5 +- .../CreateCommunityPostCommentRequest.swift | 12 ++ .../Comment/CreatorCommunityCommentView.swift | 40 ++++-- .../All/CreatorCommunityAllItemView.swift | 85 +++++++++--- .../All/CreatorCommunityAllView.swift | 30 ++++- .../All/CreatorCommunityAllViewModel.swift | 127 ++++++++++++++++++ .../CreatorCommunityApi.swift | 33 ++++- .../CreatorCommunityItemView.swift | 1 + .../CreatorCommunityRepository.swift | 12 ++ .../GetCommunityPostListResponse.swift | 1 + .../Like/PostCommunityPostLikeRequest.swift | 10 ++ 11 files changed, 313 insertions(+), 43 deletions(-) create mode 100644 SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreateCommunityPostCommentRequest.swift create mode 100644 SodaLive/Sources/Explorer/Profile/CreatorCommunity/Like/PostCommunityPostLikeRequest.swift diff --git a/SodaLive/Sources/CustomView/IconAndTitleToggleButton.swift b/SodaLive/Sources/CustomView/IconAndTitleToggleButton.swift index 2872f5e..e657ef0 100644 --- a/SodaLive/Sources/CustomView/IconAndTitleToggleButton.swift +++ b/SodaLive/Sources/CustomView/IconAndTitleToggleButton.swift @@ -9,8 +9,7 @@ import SwiftUI struct IconAndTitleToggleButton: View { - @Binding var isChecked: Bool - + let isChecked: Bool let title: String let normalIconName: String let checkedIconName: String @@ -36,7 +35,7 @@ struct IconAndTitleToggleButton: View { struct IconAndTitleToggleButton_Previews: PreviewProvider { static var previews: some View { IconAndTitleToggleButton( - isChecked: .constant(true), + isChecked: true, title: "100", normalIconName: "ic_audio_content_heart_normal", checkedIconName: "ic_audio_content_heart_pressed" diff --git a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreateCommunityPostCommentRequest.swift b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreateCommunityPostCommentRequest.swift new file mode 100644 index 0000000..afc36c5 --- /dev/null +++ b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreateCommunityPostCommentRequest.swift @@ -0,0 +1,12 @@ +// +// CreateCommunityPostCommentRequest.swift +// SodaLive +// +// Created by klaus on 2023/12/20. +// + +struct CreateCommunityPostCommentRequest: Encodable { + let comment: String + let postId: Int + let parentId: Int? +} diff --git a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentView.swift b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentView.swift index d866592..c71ae80 100644 --- a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentView.swift +++ b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentView.swift @@ -6,12 +6,13 @@ // import SwiftUI +import Kingfisher struct CreatorCommunityCommentView: View { let commentCount: Int - let commentList: [String] + let commentItem: GetCommunityPostCommentListItem? - let registerComment: (String) -> Void + let onClickWriteComment: (String) -> Void @State private var comment = "" @@ -30,18 +31,23 @@ struct CreatorCommunityCommentView: View { } HStack(spacing: 8) { - Image("ic_place_holder") - .resizable() - .frame(width: 33.3, height: 33.3) - .clipShape(Circle()) + if let comment = commentItem { + KFImage(URL(string: comment.profileUrl)) + .resizable() + .frame(width: 33.3, height: 33.3) + .clipShape(Circle()) - if commentCount > 0 { - Text(commentList[0]) + Text(comment.comment) .font(.custom(Font.medium.rawValue, size: 12)) .foregroundColor(Color(hex: "bbbbbb")) .lineLimit(1) .padding(.leading, 3) } else { + KFImage(URL(string: UserDefaults.string(forKey: .profileImage))) + .resizable() + .frame(width: 33.3, height: 33.3) + .clipShape(Circle()) + HStack(spacing: 0) { TextField("댓글을 입력해 보세요.", text: $comment) .autocapitalization(.none) @@ -60,7 +66,8 @@ struct CreatorCommunityCommentView: View { .padding(6.7) .onTapGesture { hideKeyboard() - registerComment(comment) + onClickWriteComment(comment) + comment = "" } } .background(Color(hex: "232323")) @@ -83,11 +90,16 @@ struct CreatorCommunityCommentView_Previews: PreviewProvider { static var previews: some View { CreatorCommunityCommentView( commentCount: 0, - commentList: [ - "내용 읽어보니까 결혼해도 될것 같은데", - "너무 조하유 앞으로도 좋은 라이브 많이 들려주세요" - ], - registerComment: { _ in } + commentItem: GetCommunityPostCommentListItem( + id: 1, + writerId: 1, + nickname: "닉네임", + profileUrl: "https://test-cf.sodalive.net/profile/default-profile.png", + comment: "댓글 테스트", + date: "1시간전", + replyCount: 0 + ), + onClickWriteComment: { _ in } ) } } diff --git a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllItemView.swift b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllItemView.swift index 9cc1b2c..58dea0c 100644 --- a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllItemView.swift +++ b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllItemView.swift @@ -6,25 +6,45 @@ // import SwiftUI +import Kingfisher struct CreatorCommunityAllItemView: View { - @State var isExpandContent = true + let item: GetCommunityPostListResponse + let onClickLike: () -> Void + let onClickWriteComment: (String) -> Void + + @State var isExpandContent = false + @State var isLike = false + @State var likeCount = 0 + + init( + item: GetCommunityPostListResponse, + onClickLike: @escaping () -> Void, + onClickWriteComment: @escaping (String) -> Void + ) { + self.item = item + self.onClickLike = onClickLike + self.onClickWriteComment = onClickWriteComment + + self._isLike = State(initialValue: item.isLike) + self._likeCount = State(initialValue: item.likeCount) + } var body: some View { VStack(spacing: 13.3) { HStack(spacing: 0) { - Image("ic_place_holder") + KFImage(URL(string: item.creatorProfileUrl)) .resizable() .frame(width: 40, height: 40) .clipShape(Circle()) VStack(alignment: .leading, spacing: 3) { - Text("민하나") + Text(item.creatorNickname) .font(.custom(Font.medium.rawValue, size: 13.3)) .foregroundColor(Color(hex: "eeeeee")) - Text("1개월 전(수정됨)") + Text(item.date) .font(.custom(Font.light.rawValue, size: 13.3)) .foregroundColor(Color(hex: "777777")) } @@ -37,7 +57,7 @@ struct CreatorCommunityAllItemView: View { .onTapGesture {} } - Text("너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!") + Text(item.content) .font(.custom(Font.medium.rawValue, size: 13.3)) .foregroundColor(Color(hex: "bbbbbb")) .fixedSize(horizontal: false, vertical: true) @@ -45,36 +65,61 @@ struct CreatorCommunityAllItemView: View { .lineLimit(isExpandContent ? Int.max : 3) .onTapGesture { isExpandContent.toggle() } - Image("img_sample") - .resizable() - .frame(maxWidth: .infinity) - .scaledToFit() + if let imageUrl = item.imageUrl { + KFImage(URL(string: imageUrl)) + .resizable() + .frame(maxWidth: .infinity) + .scaledToFit() + } HStack(spacing: 8) { IconAndTitleToggleButton( - isChecked: .constant(true), - title: "252", + isChecked: isLike, + title: "\(likeCount)", normalIconName: "ic_audio_content_heart_normal", checkedIconName: "ic_audio_content_heart_pressed" - ) {} - IconAndTitleButton(iconName: "ic_audio_content_share", title: "공유") {} + ) { + if isLike { + isLike = false + likeCount -= 1 + } else { + isLike = true + likeCount += 1 + } + onClickLike() + } } .frame(maxWidth: .infinity, alignment: .leading) CreatorCommunityCommentView( - commentCount: 2, - commentList: [ - "내용 읽어보니까 결혼해도 될것 같은데", - "너무 조하유 앞으로도 좋은 라이브 많이 들려주세요" - ], - registerComment: { _ in } + commentCount: item.commentCount, + commentItem: item.firstComment, + onClickWriteComment: onClickWriteComment ) } + .padding(6.7) + .background(Color(hex: "222222")) + .cornerRadius(5.3) } } struct CreatorCommunityAllItemView_Previews: PreviewProvider { static var previews: some View { - CreatorCommunityAllItemView() + CreatorCommunityAllItemView( + item: GetCommunityPostListResponse( + postId: 1, + creatorNickname: "민하나", + creatorProfileUrl: "https://test-cf.sodalive.net/profile/default-profile.png", + imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", + content: "너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!너무 조하유 앞으로도 좋은 라이브 많이 들려주세요!", + date: "3일전", + isLike: true, + likeCount: 10, + commentCount: 0, + firstComment: nil + ), + onClickLike: {}, + onClickWriteComment: { _ in } + ) } } diff --git a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift index 9121c07..637ab05 100644 --- a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift +++ b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift @@ -12,17 +12,35 @@ struct CreatorCommunityAllView: View { let creatorId: Int @State var isShowingReportView = false + @StateObject var viewModel = CreatorCommunityAllViewModel() var body: some View { - BaseView { + BaseView(isLoading: $viewModel.isLoading) { VStack(spacing: 0) { DetailNavigationBar(title: "커뮤니티") ScrollView(.vertical, showsIndicators: false) { LazyVStack(spacing: 26.7) { - CreatorCommunityAllItemView() - CreatorCommunityAllItemView() - CreatorCommunityAllItemView() + ForEach(0..() + @Published var isLoading = false + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published private(set) var communityPostList = [GetCommunityPostListResponse]() + + var creatorId = 0 + + var page = 1 + var isLast = false + private var pageSize = 10 + + func getCommunityPostList() { + if !isLoading && !isLast { + isLoading = true + + repository + .getCommunityPostList(creatorId: creatorId, page: page, size: pageSize) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse<[GetCommunityPostListResponse]>.self, from: responseData) + + if let data = decoded.data, decoded.success { + if data.count > 0 { + if page == 1 { + self.communityPostList.removeAll() + } + + self.communityPostList.append(contentsOf: data) + self.page += 1 + self.pageSize = 10 + } else { + isLast = true + } + } else { + if let message = decoded.message { + self.errorMessage = message + } else { + self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + } + + self.isShowPopup = true + } + } catch { + self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.isShowPopup = true + } + + self.isLoading = false + } + .store(in: &subscription) + } + } + + func communityPostLike(postId: Int) { + repository.communityPostLike(postId: postId) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { _ in + } + .store(in: &subscription) + } + + func createCommunityPostComment(comment: String, postId: Int, parentId: Int? = nil) { + if !isLoading && !isLast { + isLoading = true + + repository.createCommunityPostComment(comment: comment, postId: postId, parentId: parentId) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData) + + if decoded.success { + DispatchQueue.main.async { + self.pageSize *= self.page + self.page = 1 + self.isLast = false + self.getCommunityPostList() + } + } else { + if let message = decoded.message { + self.errorMessage = message + } else { + self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + } + + self.isShowPopup = true + } + } catch { + self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.isShowPopup = true + } + + self.isLoading = false + } + .store(in: &subscription) + } + } } diff --git a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityApi.swift b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityApi.swift index 936e78c..290578e 100644 --- a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityApi.swift +++ b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityApi.swift @@ -10,6 +10,9 @@ import Moya enum CreatorCommunityApi { case createCommunityPost(parameters: [MultipartFormData]) + case getCommunityPostList(creatorId: Int, page: Int, size: Int) + case communityPostLike(postId: Int) + case createCommunityPostComment(comment: String, postId: Int, parentId: Int?) } extension CreatorCommunityApi: TargetType { @@ -19,15 +22,24 @@ extension CreatorCommunityApi: TargetType { var path: String { switch self { - case .createCommunityPost: + case .createCommunityPost, .getCommunityPostList: return "/creator-community" + + case .communityPostLike: + return "/creator-community/like" + + case .createCommunityPostComment: + return "/creator-community/comment" } } var method: Moya.Method { switch self { - case .createCommunityPost: + case .createCommunityPost, .communityPostLike, .createCommunityPostComment: return .post + + case .getCommunityPostList: + return .get } } @@ -35,6 +47,23 @@ extension CreatorCommunityApi: TargetType { switch self { case .createCommunityPost(let parameters): return .uploadMultipart(parameters) + + case .getCommunityPostList(let creatorId, let page, let size): + let parameters = [ + "creatorId": creatorId, + "page": page - 1, + "size": size, + "timezone": TimeZone.current.identifier + ] as [String: Any] + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) + + case .communityPostLike(let postId): + let request = PostCommunityPostLikeRequest(postId: postId) + return .requestJSONEncodable(request) + + case .createCommunityPostComment(let comment, let postId, let parentId): + let request = CreateCommunityPostCommentRequest(comment: comment, postId: postId, parentId: parentId) + return .requestJSONEncodable(request) } } diff --git a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityItemView.swift b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityItemView.swift index 4ce9532..f92b866 100644 --- a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityItemView.swift +++ b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityItemView.swift @@ -85,6 +85,7 @@ struct CreatorCommunityItemView_Previews: PreviewProvider { static var previews: some View { CreatorCommunityItemView( item: GetCommunityPostListResponse( + postId: 1, creatorNickname: "민하나", creatorProfileUrl: "https://test-cf.sodalive.net/profile/default-profile.png", imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", diff --git a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityRepository.swift b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityRepository.swift index ee992c0..1a50656 100644 --- a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityRepository.swift +++ b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityRepository.swift @@ -16,4 +16,16 @@ class CreatorCommunityRepository { func createCommunityPost(parameters: [MultipartFormData]) -> AnyPublisher { return api.requestPublisher(.createCommunityPost(parameters: parameters)) } + + func getCommunityPostList(creatorId: Int, page: Int, size: Int) -> AnyPublisher { + return api.requestPublisher(.getCommunityPostList(creatorId: creatorId, page: page, size: size)) + } + + func communityPostLike(postId: Int) -> AnyPublisher { + return api.requestPublisher(.communityPostLike(postId: postId)) + } + + func createCommunityPostComment(comment: String, postId: Int, parentId: Int?) -> AnyPublisher { + return api.requestPublisher(.createCommunityPostComment(comment: comment, postId: postId, parentId: parentId)) + } } diff --git a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/GetCommunityPostListResponse.swift b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/GetCommunityPostListResponse.swift index 1658ba3..552f3b8 100644 --- a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/GetCommunityPostListResponse.swift +++ b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/GetCommunityPostListResponse.swift @@ -6,6 +6,7 @@ // struct GetCommunityPostListResponse: Decodable { + let postId: Int let creatorNickname: String let creatorProfileUrl: String let imageUrl: String? diff --git a/SodaLive/Sources/Explorer/Profile/CreatorCommunity/Like/PostCommunityPostLikeRequest.swift b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/Like/PostCommunityPostLikeRequest.swift new file mode 100644 index 0000000..3caedff --- /dev/null +++ b/SodaLive/Sources/Explorer/Profile/CreatorCommunity/Like/PostCommunityPostLikeRequest.swift @@ -0,0 +1,10 @@ +// +// PostCommunityPostLikeRequest.swift +// SodaLive +// +// Created by klaus on 2023/12/20. +// + +struct PostCommunityPostLikeRequest: Encodable { + let postId: Int +}