라이브 방
- 하트 후원 API 연결 - 하트 후원 성공시 하트 애니메이션 호출 - 하트 후원 성공시 채팅으로 알림
This commit is contained in:
parent
9fa1bf9f64
commit
17b4516b87
|
@ -37,6 +37,8 @@ enum LiveApi {
|
||||||
case deleteDonationMessage(roomId: Int, messageUUID: String)
|
case deleteDonationMessage(roomId: Int, messageUUID: String)
|
||||||
case getUserProfile(roomId: Int, userId: Int)
|
case getUserProfile(roomId: Int, userId: Int)
|
||||||
case getAllMenuPreset(creatorId: Int)
|
case getAllMenuPreset(creatorId: Int)
|
||||||
|
case likeHeart(request: LiveRoomLikeHeartRequest)
|
||||||
|
case getTotalHeartCount(roomId: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LiveApi: TargetType {
|
extension LiveApi: TargetType {
|
||||||
|
@ -129,15 +131,21 @@ extension LiveApi: TargetType {
|
||||||
|
|
||||||
case .getAllMenuPreset:
|
case .getAllMenuPreset:
|
||||||
return "/live/room/menu/all"
|
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 {
|
var method: Moya.Method {
|
||||||
switch self {
|
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
|
return .get
|
||||||
|
|
||||||
case .makeReservation, .enterRoom, .createRoom, .quitRoom, .donation, .refundDonation, .kickOut:
|
case .makeReservation, .enterRoom, .createRoom, .quitRoom, .donation, .refundDonation, .kickOut, .likeHeart:
|
||||||
return .post
|
return .post
|
||||||
|
|
||||||
case .setListener, .setSpeaker, .setManager, .cancelReservation, .startLive, .cancelRoom, .editLiveRoomInfo:
|
case .setListener, .setSpeaker, .setManager, .cancelReservation, .startLive, .cancelRoom, .editLiveRoomInfo:
|
||||||
|
@ -166,7 +174,7 @@ extension LiveApi: TargetType {
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
encoding: URLEncoding.queryString)
|
encoding: URLEncoding.queryString)
|
||||||
|
|
||||||
case .recentVisitRoomUsers, .getTags, .getRecentRoomInfo, .getRoomInfo, .refundDonation, .donationStatus, .donationTotal, .getUserProfile:
|
case .recentVisitRoomUsers, .getTags, .getRecentRoomInfo, .getRoomInfo, .refundDonation, .donationStatus, .donationTotal, .getUserProfile, .getTotalHeartCount:
|
||||||
return .requestPlain
|
return .requestPlain
|
||||||
|
|
||||||
case .getReservations(let isActive):
|
case .getReservations(let isActive):
|
||||||
|
@ -233,6 +241,9 @@ extension LiveApi: TargetType {
|
||||||
|
|
||||||
case .getAllMenuPreset(let creatorId):
|
case .getAllMenuPreset(let creatorId):
|
||||||
return .requestParameters(parameters: ["creatorId" : creatorId], encoding: URLEncoding.queryString)
|
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> {
|
func getAllMenuPreset(creatorId: Int) -> AnyPublisher<Response, MoyaError> {
|
||||||
return api.requestPublisher(.getAllMenuPreset(creatorId: creatorId))
|
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
|
import Foundation
|
||||||
|
|
||||||
enum LiveRoomChatType: String {
|
enum LiveRoomChatType: String {
|
||||||
case CHAT, DONATION, JOIN, ROULETTE_DONATION
|
case CHAT, DONATION, JOIN, ROULETTE_DONATION, HEART
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol LiveRoomChat {
|
protocol LiveRoomChat {
|
||||||
|
@ -48,3 +48,9 @@ struct LiveRoomJoinChat: LiveRoomChat {
|
||||||
|
|
||||||
var type: LiveRoomChatType = .JOIN
|
var type: LiveRoomChatType = .JOIN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LiveRoomHeartDonationChat: LiveRoomChat {
|
||||||
|
let nickname: String
|
||||||
|
|
||||||
|
var type: LiveRoomChatType = .HEART
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import Foundation
|
||||||
|
|
||||||
struct LiveRoomChatRawMessage: Codable {
|
struct LiveRoomChatRawMessage: Codable {
|
||||||
enum LiveRoomChatRawMessageType: String, 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
|
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 totalDonationCan = 0
|
||||||
|
@Published var totalHeartCount = 0
|
||||||
@Published var donationMessageList = [LiveRoomDonationMessage]()
|
@Published var donationMessageList = [LiveRoomDonationMessage]()
|
||||||
@Published var donationMessageCount = 0
|
@Published var donationMessageCount = 0
|
||||||
|
|
||||||
|
@ -361,6 +362,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
getTotalDonationCan()
|
getTotalDonationCan()
|
||||||
|
getTotalHeartCount()
|
||||||
|
|
||||||
if data.isAdult && !UserDefaults.bool(forKey: .auth) {
|
if data.isAdult && !UserDefaults.bool(forKey: .auth) {
|
||||||
changeIsAdult = true
|
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() {
|
func getTotalDonationCan() {
|
||||||
repository.getTotalDoantionCan(roomId: AppState.shared.roomId)
|
repository.getTotalDoantionCan(roomId: AppState.shared.roomId)
|
||||||
.sink { result in
|
.sink { result in
|
||||||
|
@ -1821,11 +1849,61 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
||||||
if isAvailableLikeHeart {
|
if isAvailableLikeHeart {
|
||||||
if !isLoadingLikeHeart {
|
if !isLoadingLikeHeart {
|
||||||
isLoadingLikeHeart = true
|
isLoadingLikeHeart = true
|
||||||
|
|
||||||
|
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()
|
addHeart()
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [unowned self] in
|
self.messageChangeFlag.toggle()
|
||||||
self.isLoadingLikeHeart = false
|
if self.messages.count > 100 {
|
||||||
|
self.messages.remove(at: 0)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
refundDonation()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: { [unowned self] in
|
||||||
|
refundDonation()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
refundDonation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &subscription)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
isShowNoticeLikeHeart = true
|
isShowNoticeLikeHeart = true
|
||||||
|
@ -2114,6 +2192,10 @@ extension LiveRoomViewModel: AgoraRtmChannelDelegate {
|
||||||
self.isActiveRoulette = decoded.isActiveRoulette!
|
self.isActiveRoulette = decoded.isActiveRoulette!
|
||||||
} else if decoded.type == .EDIT_ROOM_INFO || decoded.type == .SET_MANAGER {
|
} else if decoded.type == .EDIT_ROOM_INFO || decoded.type == .SET_MANAGER {
|
||||||
self.getRoomInfo()
|
self.getRoomInfo()
|
||||||
|
} else if decoded.type == .HEART_DONATION {
|
||||||
|
self.messages.append(LiveRoomHeartDonationChat(nickname: nickname))
|
||||||
|
self.totalHeartCount += decoded.can
|
||||||
|
self.addHeart()
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ struct LiveRoomChatView: View {
|
||||||
let chatMessage = messages[index] as! LiveRoomJoinChat
|
let chatMessage = messages[index] as! LiveRoomJoinChat
|
||||||
LiveRoomJoinChatItemView(chatMessage: chatMessage)
|
LiveRoomJoinChatItemView(chatMessage: chatMessage)
|
||||||
|
|
||||||
|
case LiveRoomChatType.HEART:
|
||||||
|
let chatMessage = messages[index] as! LiveRoomHeartDonationChat
|
||||||
|
LiveRoomHeartDonationChatItemView(chatMessage: chatMessage)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
let chatMessage = messages[index] as! LiveRoomNormalChat
|
let chatMessage = messages[index] as! LiveRoomNormalChat
|
||||||
LiveRoomChatItemView(
|
LiveRoomChatItemView(
|
||||||
|
|
|
@ -10,6 +10,7 @@ import SwiftUI
|
||||||
struct LiveRoomInfoGuestView: View {
|
struct LiveRoomInfoGuestView: View {
|
||||||
|
|
||||||
let title: String
|
let title: String
|
||||||
|
let totalHeart: Int
|
||||||
let totalDonationCan: Int
|
let totalDonationCan: Int
|
||||||
|
|
||||||
let isOnBg: Bool
|
let isOnBg: Bool
|
||||||
|
@ -163,7 +164,23 @@ struct LiveRoomInfoGuestView: View {
|
||||||
|
|
||||||
Spacer()
|
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")
|
Image("ic_can")
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 12, height: 12)
|
.frame(width: 12, height: 12)
|
||||||
|
@ -202,6 +219,7 @@ struct LiveRoomInfoGuestView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
LiveRoomInfoGuestView(
|
LiveRoomInfoGuestView(
|
||||||
title: "qwer",
|
title: "qwer",
|
||||||
|
totalHeart: 1234,
|
||||||
totalDonationCan: 123456,
|
totalDonationCan: 123456,
|
||||||
isOnBg: true,
|
isOnBg: true,
|
||||||
isOnNotice: false,
|
isOnNotice: false,
|
||||||
|
|
|
@ -11,6 +11,7 @@ import Kingfisher
|
||||||
struct LiveRoomInfoHostView: View {
|
struct LiveRoomInfoHostView: View {
|
||||||
|
|
||||||
let title: String
|
let title: String
|
||||||
|
let totalHeart: Int
|
||||||
let totalDonationCan: Int
|
let totalDonationCan: Int
|
||||||
let participantsCount: Int
|
let participantsCount: Int
|
||||||
|
|
||||||
|
@ -161,7 +162,23 @@ struct LiveRoomInfoHostView: View {
|
||||||
|
|
||||||
Spacer()
|
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")
|
Image("ic_can")
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 12, height: 12)
|
.frame(width: 12, height: 12)
|
||||||
|
@ -217,6 +234,7 @@ struct LiveRoomInfoHostView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
LiveRoomInfoHostView(
|
LiveRoomInfoHostView(
|
||||||
title: "오늘의 라이브방송은 OOO입니다.",
|
title: "오늘의 라이브방송은 OOO입니다.",
|
||||||
|
totalHeart: 1234,
|
||||||
totalDonationCan: 123456,
|
totalDonationCan: 123456,
|
||||||
participantsCount: 18,
|
participantsCount: 18,
|
||||||
isOnBg: true,
|
isOnBg: true,
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
//
|
||||||
|
// GetLiveRoomHeartTotalResponse.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 10/24/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct GetLiveRoomHeartTotalResponse: Decodable {
|
||||||
|
let totalHeartCount: Int
|
||||||
|
}
|
|
@ -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) {
|
if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) {
|
||||||
LiveRoomInfoHostView(
|
LiveRoomInfoHostView(
|
||||||
title: liveRoomInfo.title,
|
title: liveRoomInfo.title,
|
||||||
|
totalHeart: viewModel.totalHeartCount,
|
||||||
totalDonationCan: viewModel.totalDonationCan,
|
totalDonationCan: viewModel.totalDonationCan,
|
||||||
participantsCount: liveRoomInfo.participantsCount,
|
participantsCount: liveRoomInfo.participantsCount,
|
||||||
isOnBg: viewModel.isBgOn,
|
isOnBg: viewModel.isBgOn,
|
||||||
|
@ -75,6 +76,7 @@ struct LiveRoomViewV2: View {
|
||||||
} else {
|
} else {
|
||||||
LiveRoomInfoGuestView(
|
LiveRoomInfoGuestView(
|
||||||
title: liveRoomInfo.title,
|
title: liveRoomInfo.title,
|
||||||
|
totalHeart: viewModel.totalHeartCount,
|
||||||
totalDonationCan: viewModel.totalDonationCan,
|
totalDonationCan: viewModel.totalDonationCan,
|
||||||
isOnBg: viewModel.isBgOn,
|
isOnBg: viewModel.isBgOn,
|
||||||
isOnNotice: viewModel.isShowNotice,
|
isOnNotice: viewModel.isShowNotice,
|
||||||
|
|
Loading…
Reference in New Issue