라이브 방
- 하트 후원 API 연결 - 하트 후원 성공시 하트 애니메이션 호출 - 하트 후원 성공시 채팅으로 알림
This commit is contained in:
		| @@ -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) | ||||
|         } | ||||
|     } | ||||
|      | ||||
|   | ||||
| @@ -120,4 +120,12 @@ final class LiveRepository { | ||||
|     func getAllMenuPreset(creatorId: Int) -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.getAllMenuPreset(creatorId: creatorId)) | ||||
|     } | ||||
|      | ||||
|     func likeHeart(roomId: Int) -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.likeHeart(request: LiveRoomLikeHeartRequest(roomId: roomId))) | ||||
|     } | ||||
|      | ||||
|     func getTotalHeartCount(roomId: Int) -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.getTotalHeartCount(roomId: roomId)) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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: "닉네임")) | ||||
| } | ||||
| @@ -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<GetLiveRoomHeartTotalResponse>.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 { | ||||
|             } | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -0,0 +1,12 @@ | ||||
| // | ||||
| //  GetLiveRoomHeartTotalResponse.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 10/24/24. | ||||
| // | ||||
|  | ||||
| import Foundation | ||||
|  | ||||
| struct GetLiveRoomHeartTotalResponse: Decodable { | ||||
|     let totalHeartCount: Int | ||||
| } | ||||
							
								
								
									
										11
									
								
								SodaLive/Sources/Live/Room/V2/LiveRoomLikeHeartRequest.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								SodaLive/Sources/Live/Room/V2/LiveRoomLikeHeartRequest.swift
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // | ||||
| //  LiveRoomLikeHeartRequest.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 10/24/24. | ||||
| // | ||||
|  | ||||
| struct LiveRoomLikeHeartRequest: Encodable { | ||||
|     let roomId: Int | ||||
|     let container: String = "ios" | ||||
| } | ||||
| @@ -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, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Yu Sung
					Yu Sung