From 17b4516b87c926627b8bf0592a4b5e1abfdf8b7f Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Thu, 24 Oct 2024 18:13:56 +0900 Subject: [PATCH] =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=20=EB=B0=A9=20-=20?= =?UTF-8?q?=ED=95=98=ED=8A=B8=20=ED=9B=84=EC=9B=90=20API=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=20-=20=ED=95=98=ED=8A=B8=20=ED=9B=84=EC=9B=90=20?= =?UTF-8?q?=EC=84=B1=EA=B3=B5=EC=8B=9C=20=ED=95=98=ED=8A=B8=20=EC=95=A0?= =?UTF-8?q?=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20=ED=98=B8=EC=B6=9C=20-?= =?UTF-8?q?=20=ED=95=98=ED=8A=B8=20=ED=9B=84=EC=9B=90=20=EC=84=B1=EA=B3=B5?= =?UTF-8?q?=EC=8B=9C=20=EC=B1=84=ED=8C=85=EC=9C=BC=EB=A1=9C=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SodaLive/Sources/Live/LiveApi.swift | 17 +++- SodaLive/Sources/Live/LiveRepository.swift | 8 ++ .../Sources/Live/Room/Chat/LiveRoomChat.swift | 8 +- .../Room/Chat/LiveRoomChatRawMessage.swift | 2 +- .../LiveRoomHeartDonationChatItemView.swift | 38 ++++++++ .../Sources/Live/Room/LiveRoomViewModel.swift | 90 ++++++++++++++++++- .../V2/Component/View/LiveRoomChatView.swift | 4 + .../View/LiveRoomInfoGuestView.swift | 20 ++++- .../Component/View/LiveRoomInfoHostView.swift | 20 ++++- .../V2/GetLiveRoomHeartTotalResponse.swift | 12 +++ .../Room/V2/LiveRoomLikeHeartRequest.swift | 11 +++ .../Sources/Live/Room/V2/LiveRoomViewV2.swift | 2 + 12 files changed, 221 insertions(+), 11 deletions(-) create mode 100644 SodaLive/Sources/Live/Room/Chat/LiveRoomHeartDonationChatItemView.swift create mode 100644 SodaLive/Sources/Live/Room/V2/GetLiveRoomHeartTotalResponse.swift create mode 100644 SodaLive/Sources/Live/Room/V2/LiveRoomLikeHeartRequest.swift diff --git a/SodaLive/Sources/Live/LiveApi.swift b/SodaLive/Sources/Live/LiveApi.swift index a933c3f..b973fe0 100644 --- a/SodaLive/Sources/Live/LiveApi.swift +++ b/SodaLive/Sources/Live/LiveApi.swift @@ -37,6 +37,8 @@ enum LiveApi { case deleteDonationMessage(roomId: Int, messageUUID: String) case getUserProfile(roomId: Int, userId: Int) case getAllMenuPreset(creatorId: Int) + case likeHeart(request: LiveRoomLikeHeartRequest) + case getTotalHeartCount(roomId: Int) } extension LiveApi: TargetType { @@ -129,15 +131,21 @@ extension LiveApi: TargetType { case .getAllMenuPreset: return "/live/room/menu/all" + + case .likeHeart: + return "/live/room/like-heart" + + case .getTotalHeartCount(let roomId): + return "/live/room/\(roomId)/heart-total" } } var method: Moya.Method { switch self { - case .roomList, .recentVisitRoomUsers, .getReservations, .getReservation, .getRoomDetail, .getTags, .getRecentRoomInfo, .getRoomInfo, .donationStatus, .donationTotal, .getDonationMessageList, .getUserProfile, .getAllMenuPreset: + case .roomList, .recentVisitRoomUsers, .getReservations, .getReservation, .getRoomDetail, .getTags, .getRecentRoomInfo, .getRoomInfo, .donationStatus, .donationTotal, .getDonationMessageList, .getUserProfile, .getAllMenuPreset, .getTotalHeartCount: return .get - case .makeReservation, .enterRoom, .createRoom, .quitRoom, .donation, .refundDonation, .kickOut: + case .makeReservation, .enterRoom, .createRoom, .quitRoom, .donation, .refundDonation, .kickOut, .likeHeart: return .post case .setListener, .setSpeaker, .setManager, .cancelReservation, .startLive, .cancelRoom, .editLiveRoomInfo: @@ -166,7 +174,7 @@ extension LiveApi: TargetType { parameters: parameters, encoding: URLEncoding.queryString) - case .recentVisitRoomUsers, .getTags, .getRecentRoomInfo, .getRoomInfo, .refundDonation, .donationStatus, .donationTotal, .getUserProfile: + case .recentVisitRoomUsers, .getTags, .getRecentRoomInfo, .getRoomInfo, .refundDonation, .donationStatus, .donationTotal, .getUserProfile, .getTotalHeartCount: return .requestPlain case .getReservations(let isActive): @@ -233,6 +241,9 @@ extension LiveApi: TargetType { case .getAllMenuPreset(let creatorId): return .requestParameters(parameters: ["creatorId" : creatorId], encoding: URLEncoding.queryString) + + case .likeHeart(let request): + return .requestJSONEncodable(request) } } diff --git a/SodaLive/Sources/Live/LiveRepository.swift b/SodaLive/Sources/Live/LiveRepository.swift index 42f8fc2..9ed713e 100644 --- a/SodaLive/Sources/Live/LiveRepository.swift +++ b/SodaLive/Sources/Live/LiveRepository.swift @@ -120,4 +120,12 @@ final class LiveRepository { func getAllMenuPreset(creatorId: Int) -> AnyPublisher { return api.requestPublisher(.getAllMenuPreset(creatorId: creatorId)) } + + func likeHeart(roomId: Int) -> AnyPublisher { + return api.requestPublisher(.likeHeart(request: LiveRoomLikeHeartRequest(roomId: roomId))) + } + + func getTotalHeartCount(roomId: Int) -> AnyPublisher { + return api.requestPublisher(.getTotalHeartCount(roomId: roomId)) + } } diff --git a/SodaLive/Sources/Live/Room/Chat/LiveRoomChat.swift b/SodaLive/Sources/Live/Room/Chat/LiveRoomChat.swift index 8af0bcc..f775496 100644 --- a/SodaLive/Sources/Live/Room/Chat/LiveRoomChat.swift +++ b/SodaLive/Sources/Live/Room/Chat/LiveRoomChat.swift @@ -8,7 +8,7 @@ import Foundation enum LiveRoomChatType: String { - case CHAT, DONATION, JOIN, ROULETTE_DONATION + case CHAT, DONATION, JOIN, ROULETTE_DONATION, HEART } protocol LiveRoomChat { @@ -48,3 +48,9 @@ struct LiveRoomJoinChat: LiveRoomChat { var type: LiveRoomChatType = .JOIN } + +struct LiveRoomHeartDonationChat: LiveRoomChat { + let nickname: String + + var type: LiveRoomChatType = .HEART +} diff --git a/SodaLive/Sources/Live/Room/Chat/LiveRoomChatRawMessage.swift b/SodaLive/Sources/Live/Room/Chat/LiveRoomChatRawMessage.swift index cb6679a..d7f2e1f 100644 --- a/SodaLive/Sources/Live/Room/Chat/LiveRoomChatRawMessage.swift +++ b/SodaLive/Sources/Live/Room/Chat/LiveRoomChatRawMessage.swift @@ -9,7 +9,7 @@ import Foundation struct LiveRoomChatRawMessage: Codable { enum LiveRoomChatRawMessageType: String, Codable { - case DONATION, SECRET_DONATION, EDIT_ROOM_INFO, SET_MANAGER, TOGGLE_ROULETTE, ROULETTE_DONATION + case DONATION, SECRET_DONATION, EDIT_ROOM_INFO, SET_MANAGER, TOGGLE_ROULETTE, ROULETTE_DONATION, HEART_DONATION } let type: LiveRoomChatRawMessageType diff --git a/SodaLive/Sources/Live/Room/Chat/LiveRoomHeartDonationChatItemView.swift b/SodaLive/Sources/Live/Room/Chat/LiveRoomHeartDonationChatItemView.swift new file mode 100644 index 0000000..0bb3936 --- /dev/null +++ b/SodaLive/Sources/Live/Room/Chat/LiveRoomHeartDonationChatItemView.swift @@ -0,0 +1,38 @@ +// +// LiveRoomHeartDonationChatItemView.swift +// SodaLive +// +// Created by klaus on 10/24/24. +// + +import SwiftUI + +struct LiveRoomHeartDonationChatItemView: View { + + let chatMessage: LiveRoomHeartDonationChat + + var body: some View { + HStack(spacing: 0) { + Text("'") + .font(.system(size: 12)) + .foregroundColor(Color.gray11) + + Text(chatMessage.nickname) + .font(.system(size: 12, weight: .bold)) + .foregroundColor(Color(hex: "ec3aa6")) + + Text("'님이 마음을 전했습니다 : 💕") + .font(.system(size: 12)) + .foregroundColor(Color.gray11) + } + .padding(.vertical, 6.7) + .frame(width: screenSize().width - 86) + .background(Color.white.opacity(0.7)) + .cornerRadius(4.7) + .padding(.leading, 20) + } +} + +#Preview { + LiveRoomHeartDonationChatItemView(chatMessage: LiveRoomHeartDonationChat(nickname: "닉네임")) +} diff --git a/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift b/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift index 1536de5..a2d88d2 100644 --- a/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift +++ b/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift @@ -118,6 +118,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject { } @Published var totalDonationCan = 0 + @Published var totalHeartCount = 0 @Published var donationMessageList = [LiveRoomDonationMessage]() @Published var donationMessageCount = 0 @@ -361,6 +362,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject { ) getTotalDonationCan() + getTotalHeartCount() if data.isAdult && !UserDefaults.bool(forKey: .auth) { changeIsAdult = true @@ -1133,6 +1135,32 @@ final class LiveRoomViewModel: NSObject, ObservableObject { } } + private func getTotalHeartCount() { + repository.getTotalHeartCount(roomId: AppState.shared.roomId) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) + + if let data = decoded.data, decoded.success { + self.totalHeartCount = data.totalHeartCount + } + } catch { + } + } + .store(in: &subscription) + } + func getTotalDonationCan() { repository.getTotalDoantionCan(roomId: AppState.shared.roomId) .sink { result in @@ -1821,11 +1849,61 @@ final class LiveRoomViewModel: NSObject, ObservableObject { if isAvailableLikeHeart { if !isLoadingLikeHeart { isLoadingLikeHeart = true - addHeart() - DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [unowned self] in - self.isLoadingLikeHeart = false - } + repository.likeHeart(roomId: AppState.shared.roomId) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoadingLikeHeart = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData) + + if decoded.success { + UserDefaults.set(UserDefaults.int(forKey: .can) - 1, forKey: .can) + + let donationRawMessage = LiveRoomChatRawMessage( + type: .HEART_DONATION, + message: "", + can: 1, + donationMessage: nil + ) + + agora.sendRawMessageToGroup( + rawMessage: donationRawMessage, + completion: { [unowned self] errorCode in + if errorCode == .errorOk { + let (nickname, _) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId)) + self.messages.append(LiveRoomHeartDonationChat(nickname: nickname)) + + totalHeartCount += 1 + addHeart() + + self.messageChangeFlag.toggle() + if self.messages.count > 100 { + self.messages.remove(at: 0) + } + } else { + refundDonation() + } + }, + fail: { [unowned self] in + refundDonation() + } + ) + } + } catch { + refundDonation() + } + } + .store(in: &subscription) } } else { isShowNoticeLikeHeart = true @@ -2114,6 +2192,10 @@ extension LiveRoomViewModel: AgoraRtmChannelDelegate { self.isActiveRoulette = decoded.isActiveRoulette! } else if decoded.type == .EDIT_ROOM_INFO || decoded.type == .SET_MANAGER { self.getRoomInfo() + } else if decoded.type == .HEART_DONATION { + self.messages.append(LiveRoomHeartDonationChat(nickname: nickname)) + self.totalHeartCount += decoded.can + self.addHeart() } } catch { } diff --git a/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomChatView.swift b/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomChatView.swift index 9b614b6..f70936f 100644 --- a/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomChatView.swift +++ b/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomChatView.swift @@ -28,6 +28,10 @@ struct LiveRoomChatView: View { let chatMessage = messages[index] as! LiveRoomJoinChat LiveRoomJoinChatItemView(chatMessage: chatMessage) + case LiveRoomChatType.HEART: + let chatMessage = messages[index] as! LiveRoomHeartDonationChat + LiveRoomHeartDonationChatItemView(chatMessage: chatMessage) + default: let chatMessage = messages[index] as! LiveRoomNormalChat LiveRoomChatItemView( diff --git a/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoGuestView.swift b/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoGuestView.swift index 3bd6158..af0cbf7 100644 --- a/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoGuestView.swift +++ b/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoGuestView.swift @@ -10,6 +10,7 @@ import SwiftUI struct LiveRoomInfoGuestView: View { let title: String + let totalHeart: Int let totalDonationCan: Int let isOnBg: Bool @@ -163,7 +164,23 @@ struct LiveRoomInfoGuestView: View { Spacer() - HStack(spacing: 2.7) { + HStack(spacing: 6.7) { + Image("ic_heart_pink") + .resizable() + .frame(width: 12, height: 12) + + Text("\(totalHeart)") + .font(.custom(Font.medium.rawValue, size: 12)) + .foregroundColor(.graybb) + } + .padding(.horizontal, 11) + .padding(.vertical, 5.3) + .overlay( + RoundedRectangle(cornerRadius: 5.3) + .stroke(Color.graybb, lineWidth: 1) + ) + + HStack(spacing: 6.7) { Image("ic_can") .resizable() .frame(width: 12, height: 12) @@ -202,6 +219,7 @@ struct LiveRoomInfoGuestView_Previews: PreviewProvider { static var previews: some View { LiveRoomInfoGuestView( title: "qwer", + totalHeart: 1234, totalDonationCan: 123456, isOnBg: true, isOnNotice: false, diff --git a/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoHostView.swift b/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoHostView.swift index ac8846e..55c2cfb 100644 --- a/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoHostView.swift +++ b/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoHostView.swift @@ -11,6 +11,7 @@ import Kingfisher struct LiveRoomInfoHostView: View { let title: String + let totalHeart: Int let totalDonationCan: Int let participantsCount: Int @@ -161,7 +162,23 @@ struct LiveRoomInfoHostView: View { Spacer() - HStack(spacing: 2.7) { + HStack(spacing: 6.7) { + Image("ic_heart_pink") + .resizable() + .frame(width: 12, height: 12) + + Text("\(totalHeart)") + .font(.custom(Font.medium.rawValue, size: 12)) + .foregroundColor(.graybb) + } + .padding(.horizontal, 11) + .padding(.vertical, 5.3) + .overlay( + RoundedRectangle(cornerRadius: 5.3) + .stroke(Color.graybb, lineWidth: 1) + ) + + HStack(spacing: 6.7) { Image("ic_can") .resizable() .frame(width: 12, height: 12) @@ -217,6 +234,7 @@ struct LiveRoomInfoHostView_Previews: PreviewProvider { static var previews: some View { LiveRoomInfoHostView( title: "오늘의 라이브방송은 OOO입니다.", + totalHeart: 1234, totalDonationCan: 123456, participantsCount: 18, isOnBg: true, diff --git a/SodaLive/Sources/Live/Room/V2/GetLiveRoomHeartTotalResponse.swift b/SodaLive/Sources/Live/Room/V2/GetLiveRoomHeartTotalResponse.swift new file mode 100644 index 0000000..02202f1 --- /dev/null +++ b/SodaLive/Sources/Live/Room/V2/GetLiveRoomHeartTotalResponse.swift @@ -0,0 +1,12 @@ +// +// GetLiveRoomHeartTotalResponse.swift +// SodaLive +// +// Created by klaus on 10/24/24. +// + +import Foundation + +struct GetLiveRoomHeartTotalResponse: Decodable { + let totalHeartCount: Int +} diff --git a/SodaLive/Sources/Live/Room/V2/LiveRoomLikeHeartRequest.swift b/SodaLive/Sources/Live/Room/V2/LiveRoomLikeHeartRequest.swift new file mode 100644 index 0000000..df51545 --- /dev/null +++ b/SodaLive/Sources/Live/Room/V2/LiveRoomLikeHeartRequest.swift @@ -0,0 +1,11 @@ +// +// LiveRoomLikeHeartRequest.swift +// SodaLive +// +// Created by klaus on 10/24/24. +// + +struct LiveRoomLikeHeartRequest: Encodable { + let roomId: Int + let container: String = "ios" +} diff --git a/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift b/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift index d4d49ee..2245a56 100644 --- a/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift +++ b/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift @@ -25,6 +25,7 @@ struct LiveRoomViewV2: View { if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) { LiveRoomInfoHostView( title: liveRoomInfo.title, + totalHeart: viewModel.totalHeartCount, totalDonationCan: viewModel.totalDonationCan, participantsCount: liveRoomInfo.participantsCount, isOnBg: viewModel.isBgOn, @@ -75,6 +76,7 @@ struct LiveRoomViewV2: View { } else { LiveRoomInfoGuestView( title: liveRoomInfo.title, + totalHeart: viewModel.totalHeartCount, totalDonationCan: viewModel.totalDonationCan, isOnBg: viewModel.isBgOn, isOnNotice: viewModel.isShowNotice,