feat: 커뮤니티 댓글

- 유료 커뮤니티 구매시 비밀 댓글 쓰기 기능 추가
This commit is contained in:
Yu Sung
2025-06-13 19:18:37 +09:00
parent 522a177063
commit 24c97dbe51
13 changed files with 90 additions and 23 deletions

View File

@@ -9,4 +9,5 @@ struct CreateCommunityPostCommentRequest: Encodable {
let comment: String let comment: String
let postId: Int let postId: Int
let parentId: Int? let parentId: Int?
let isSecret: Bool
} }

View File

@@ -44,10 +44,22 @@ struct CreatorCommunityCommentItemView: View {
} }
} }
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
Text(commentItem.nickname) HStack(spacing: 6.7) {
.font(.custom(Font.medium.rawValue, size: 12)) Text(commentItem.nickname)
.foregroundColor(Color.gray90) .font(.custom(Font.medium.rawValue, size: 12))
.foregroundColor(Color.gray90)
if commentItem.isSecret {
Text("비밀댓글")
.font(.custom(Font.medium.rawValue, size: 11))
.foregroundColor(Color.grayee)
.padding(.horizontal, 4)
.padding(.vertical, 2)
.background(Color.button.opacity(0.2))
.cornerRadius(3.3)
}
}
Text(commentItem.date) Text(commentItem.date)
.font(.custom(Font.medium.rawValue, size: 10.3)) .font(.custom(Font.medium.rawValue, size: 10.3))

View File

@@ -14,6 +14,7 @@ struct CreatorCommunityCommentListView: View {
let creatorId: Int let creatorId: Int
let postId: Int let postId: Int
let isShowSecret: Bool
@State private var commentId: Int = 0 @State private var commentId: Int = 0
@State private var isShowDeletePopup: Bool = false @State private var isShowDeletePopup: Bool = false
@@ -53,6 +54,28 @@ struct CreatorCommunityCommentListView: View {
.padding(.bottom, 13.3) .padding(.bottom, 13.3)
.padding(.horizontal, 13.3) .padding(.horizontal, 13.3)
if isShowSecret {
HStack(spacing: 8) {
Spacer()
Image(viewModel.isSecret ? "btn_square_select_checked" : "btn_square_select_normal")
.resizable()
.frame(width: 20, height: 20)
.onTapGesture {
viewModel.isSecret.toggle()
}
Text("비밀댓글")
.font(.custom(Font.medium.rawValue, size: 12))
.foregroundColor(viewModel.isSecret ? Color.button : Color.grayee)
.onTapGesture {
viewModel.isSecret.toggle()
}
}
.padding(.bottom, 13.3)
.padding(.horizontal, 13.3)
}
HStack(spacing: 8) { HStack(spacing: 8) {
KFImage(URL(string: UserDefaults.string(forKey: .profileImage))) KFImage(URL(string: UserDefaults.string(forKey: .profileImage)))
.cancelOnDisappear(true) .cancelOnDisappear(true)
@@ -193,7 +216,8 @@ struct CreatorCommunityCommentListView_Previews: PreviewProvider {
CreatorCommunityCommentListView( CreatorCommunityCommentListView(
isPresented: .constant(true), isPresented: .constant(true),
creatorId: 0, creatorId: 0,
postId: 0 postId: 0,
isShowSecret: true
) )
} }
} }

View File

@@ -17,6 +17,7 @@ final class CreatorCommunityCommentListViewModel: ObservableObject {
@Published var isShowPopup = false @Published var isShowPopup = false
@Published var comment = "" @Published var comment = ""
@Published var isSecret = false
@Published var totalCommentCount = 0 @Published var totalCommentCount = 0
@Published var commentList = [GetCommunityPostCommentListItem]() @Published var commentList = [GetCommunityPostCommentListItem]()
@@ -79,7 +80,7 @@ final class CreatorCommunityCommentListViewModel: ObservableObject {
if !isLoading { if !isLoading {
isLoading = true isLoading = true
repository.createCommunityPostComment(comment: comment, postId: postId, parentId: parentId) repository.createCommunityPostComment(comment: comment, postId: postId, parentId: parentId, isSecret: isSecret)
.sink { result in .sink { result in
switch result { switch result {
case .finished: case .finished:
@@ -100,6 +101,7 @@ final class CreatorCommunityCommentListViewModel: ObservableObject {
self.comment = "" self.comment = ""
self.page = 1 self.page = 1
self.isLast = false self.isLast = false
self.isSecret = false
self.getCommentList() self.getCommentList()
} }
} else { } else {

View File

@@ -192,6 +192,7 @@ struct CreatorCommunityCommentReplyView_Previews: PreviewProvider {
nickname: "댓글", nickname: "댓글",
profileUrl: "", profileUrl: "",
comment: "부모 댓글입니다.", comment: "부모 댓글입니다.",
isSecret: false,
date: "1시간전", date: "1시간전",
replyCount: 1 replyCount: 1
) )

View File

@@ -81,7 +81,7 @@ final class CreatorCommunityCommentReplyViewModel: ObservableObject {
if !isLoading { if !isLoading {
isLoading = true isLoading = true
repository.createCommunityPostComment(comment: comment, postId: postId, parentId: parentId) repository.createCommunityPostComment(comment: comment, postId: postId, parentId: parentId, isSecret: false)
.sink { result in .sink { result in
switch result { switch result {
case .finished: case .finished:

View File

@@ -11,10 +11,12 @@ import Kingfisher
struct CreatorCommunityCommentView: View { struct CreatorCommunityCommentView: View {
let commentCount: Int let commentCount: Int
let commentItem: GetCommunityPostCommentListItem? let commentItem: GetCommunityPostCommentListItem?
let isShowSecret: Bool
let onClickWriteComment: (String) -> Void let onClickWriteComment: (String, Bool) -> Void
@State private var comment = "" @State private var comment = ""
@State private var isSecret = false
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 11) { VStack(alignment: .leading, spacing: 11) {
@@ -28,6 +30,22 @@ struct CreatorCommunityCommentView: View {
.foregroundColor(Color(hex: "909090")) .foregroundColor(Color(hex: "909090"))
Spacer() Spacer()
if isShowSecret && commentItem == nil {
Image(isSecret ? "btn_square_select_checked" : "btn_square_select_normal")
.resizable()
.frame(width: 20, height: 20)
.onTapGesture {
isSecret.toggle()
}
Text("비밀댓글")
.font(.custom(Font.medium.rawValue, size: 12))
.foregroundColor(isSecret ? Color.button : Color.grayee)
.onTapGesture {
isSecret.toggle()
}
}
} }
HStack(spacing: 8) { HStack(spacing: 8) {
@@ -80,7 +98,7 @@ struct CreatorCommunityCommentView: View {
.padding(6.7) .padding(6.7)
.onTapGesture { .onTapGesture {
hideKeyboard() hideKeyboard()
onClickWriteComment(comment) onClickWriteComment(comment, isSecret)
comment = "" comment = ""
} }
} }
@@ -110,10 +128,12 @@ struct CreatorCommunityCommentView_Previews: PreviewProvider {
nickname: "닉네임", nickname: "닉네임",
profileUrl: "https://test-cf.sodalive.net/profile/default-profile.png", profileUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
comment: "댓글 테스트", comment: "댓글 테스트",
isSecret: false,
date: "1시간전", date: "1시간전",
replyCount: 0 replyCount: 0
), ),
onClickWriteComment: { _ in } isShowSecret: true,
onClickWriteComment: { _, _ in }
) )
} }
} }

View File

@@ -16,6 +16,7 @@ struct GetCommunityPostCommentListItem: Decodable {
let nickname: String let nickname: String
let profileUrl: String let profileUrl: String
let comment: String let comment: String
let isSecret: Bool
let date: String let date: String
let replyCount: Int let replyCount: Int
} }

View File

@@ -13,7 +13,7 @@ struct CreatorCommunityAllItemView: View {
let item: GetCommunityPostListResponse let item: GetCommunityPostListResponse
let onClickLike: () -> Void let onClickLike: () -> Void
let onClickComment: () -> Void let onClickComment: () -> Void
let onClickWriteComment: (String) -> Void let onClickWriteComment: (String, Bool) -> Void
let onClickShowReportMenu: () -> Void let onClickShowReportMenu: () -> Void
let onClickPurchaseContent: () -> Void let onClickPurchaseContent: () -> Void
@@ -28,7 +28,7 @@ struct CreatorCommunityAllItemView: View {
item: GetCommunityPostListResponse, item: GetCommunityPostListResponse,
onClickLike: @escaping () -> Void, onClickLike: @escaping () -> Void,
onClickComment: @escaping () -> Void, onClickComment: @escaping () -> Void,
onClickWriteComment: @escaping (String) -> Void, onClickWriteComment: @escaping (String, Bool) -> Void,
onClickShowReportMenu: @escaping () -> Void, onClickShowReportMenu: @escaping () -> Void,
onClickPurchaseContent: @escaping () -> Void onClickPurchaseContent: @escaping () -> Void
) { ) {
@@ -137,6 +137,7 @@ struct CreatorCommunityAllItemView: View {
CreatorCommunityCommentView( CreatorCommunityCommentView(
commentCount: item.commentCount, commentCount: item.commentCount,
commentItem: item.firstComment, commentItem: item.firstComment,
isShowSecret: item.existOrdered,
onClickWriteComment: onClickWriteComment onClickWriteComment: onClickWriteComment
) )
.onTapGesture { .onTapGesture {
@@ -189,7 +190,7 @@ struct CreatorCommunityAllItemView_Previews: PreviewProvider {
), ),
onClickLike: {}, onClickLike: {},
onClickComment: {}, onClickComment: {},
onClickWriteComment: { _ in }, onClickWriteComment: { _, _ in },
onClickShowReportMenu: {}, onClickShowReportMenu: {},
onClickPurchaseContent: {} onClickPurchaseContent: {}
) )

View File

@@ -31,12 +31,14 @@ struct CreatorCommunityAllView: View {
}, },
onClickComment: { onClickComment: {
viewModel.postId = item.postId viewModel.postId = item.postId
viewModel.isShowSecret = item.existOrdered
viewModel.isShowCommentListView = true viewModel.isShowCommentListView = true
}, },
onClickWriteComment: { comment in onClickWriteComment: { comment, isSecret in
viewModel.createCommunityPostComment( viewModel.createCommunityPostComment(
comment: comment, comment: comment,
postId: item.postId postId: item.postId,
isSecret: isSecret
) )
}, },
onClickShowReportMenu: { onClickShowReportMenu: {
@@ -65,7 +67,8 @@ struct CreatorCommunityAllView: View {
CreatorCommunityCommentListView( CreatorCommunityCommentListView(
isPresented: $viewModel.isShowCommentListView, isPresented: $viewModel.isShowCommentListView,
creatorId: creatorId, creatorId: creatorId,
postId: viewModel.postId postId: viewModel.postId,
isShowSecret: viewModel.isShowSecret
) )
} }
) )

View File

@@ -23,11 +23,13 @@ class CreatorCommunityAllViewModel: ObservableObject {
@Published var postId = 0 @Published var postId = 0
@Published var postPrice = 0 @Published var postPrice = 0
@Published var postIndex = -1 @Published var postIndex = -1
@Published var isShowSecret = false
@Published var isShowCommentListView = false { @Published var isShowCommentListView = false {
didSet { didSet {
if !isShowCommentListView { if !isShowCommentListView {
postId = 0 postId = 0
isShowSecret = false
} }
} }
} }
@@ -130,10 +132,10 @@ class CreatorCommunityAllViewModel: ObservableObject {
.store(in: &subscription) .store(in: &subscription)
} }
func createCommunityPostComment(comment: String, postId: Int, parentId: Int? = nil) { func createCommunityPostComment(comment: String, postId: Int, parentId: Int? = nil, isSecret: Bool = false) {
isLoading = true isLoading = true
repository.createCommunityPostComment(comment: comment, postId: postId, parentId: parentId) repository.createCommunityPostComment(comment: comment, postId: postId, parentId: parentId, isSecret: isSecret)
.sink { result in .sink { result in
switch result { switch result {
case .finished: case .finished:

View File

@@ -14,7 +14,7 @@ enum CreatorCommunityApi {
case getCommunityPostList(creatorId: Int, page: Int, size: Int) case getCommunityPostList(creatorId: Int, page: Int, size: Int)
case getCommunityPostDetail(postId: Int) case getCommunityPostDetail(postId: Int)
case communityPostLike(postId: Int) case communityPostLike(postId: Int)
case createCommunityPostComment(comment: String, postId: Int, parentId: Int?) case createCommunityPostComment(comment: String, postId: Int, parentId: Int?, isSecret: Bool)
case getCommunityPostCommentList(postId: Int, page: Int, size: Int) case getCommunityPostCommentList(postId: Int, page: Int, size: Int)
case getCommentReplyList(commentId: Int, page: Int, size: Int) case getCommentReplyList(commentId: Int, page: Int, size: Int)
case modifyComment(request: ModifyCommunityPostCommentRequest) case modifyComment(request: ModifyCommunityPostCommentRequest)
@@ -89,8 +89,8 @@ extension CreatorCommunityApi: TargetType {
let request = PostCommunityPostLikeRequest(postId: postId) let request = PostCommunityPostLikeRequest(postId: postId)
return .requestJSONEncodable(request) return .requestJSONEncodable(request)
case .createCommunityPostComment(let comment, let postId, let parentId): case .createCommunityPostComment(let comment, let postId, let parentId, let isSecret):
let request = CreateCommunityPostCommentRequest(comment: comment, postId: postId, parentId: parentId) let request = CreateCommunityPostCommentRequest(comment: comment, postId: postId, parentId: parentId, isSecret: isSecret)
return .requestJSONEncodable(request) return .requestJSONEncodable(request)
case .getCommunityPostCommentList(_, let page, let size): case .getCommunityPostCommentList(_, let page, let size):

View File

@@ -33,8 +33,8 @@ class CreatorCommunityRepository {
return api.requestPublisher(.communityPostLike(postId: postId)) return api.requestPublisher(.communityPostLike(postId: postId))
} }
func createCommunityPostComment(comment: String, postId: Int, parentId: Int?) -> AnyPublisher<Response, MoyaError> { func createCommunityPostComment(comment: String, postId: Int, parentId: Int?, isSecret: Bool) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.createCommunityPostComment(comment: comment, postId: postId, parentId: parentId)) return api.requestPublisher(.createCommunityPostComment(comment: comment, postId: postId, parentId: parentId, isSecret: isSecret))
} }
func getCommunityPostCommentList(postId: Int, page: Int, size: Int) -> AnyPublisher<Response, MoyaError> { func getCommunityPostCommentList(postId: Int, page: Int, size: Int) -> AnyPublisher<Response, MoyaError> {