라이브 후원

- 비밀 후원 기능 추가
This commit is contained in:
Yu Sung 2024-08-26 20:07:37 +09:00
parent 81846f7f7b
commit 534a6e737e
9 changed files with 150 additions and 43 deletions

View File

@ -111,6 +111,23 @@ final class Agora {
rtmKit?.send(message, toPeer: peerId, completion: completion) rtmKit?.send(message, toPeer: peerId, completion: completion)
} }
func sendRawMessageToPeer(peerId: String, rawMessage: LiveRoomChatRawMessage, completion: AgoraRtmSendPeerMessageBlock? = nil, fail: (() -> Void)? = nil) {
let encoder = JSONEncoder()
let jsonMessageData = try? encoder.encode(rawMessage)
let option = AgoraRtmSendMessageOptions()
option.enableOfflineMessaging = false
option.enableHistoricalMessaging = false
if let jsonMessageData = jsonMessageData {
let message = AgoraRtmRawMessage(rawData: jsonMessageData, description: "")
rtmKit?.send(message, toPeer: peerId, sendMessageOptions: option, completion: completion)
} else {
if let fail = fail {
fail()
}
}
}
func mute(_ isMute: Bool) { func mute(_ isMute: Bool) {
rtcEngine?.muteLocalAudioStream(isMute) rtcEngine?.muteLocalAudioStream(isMute)
} }

View File

@ -323,7 +323,7 @@ struct ContentDetailView: View {
} }
if viewModel.isShowDonationPopup { if viewModel.isShowDonationPopup {
LiveRoomDonationDialogView(isShowing: $viewModel.isShowDonationPopup, isAudioContentDonation: true) { can, comment in LiveRoomDonationDialogView(isShowing: $viewModel.isShowDonationPopup, isAudioContentDonation: true) { can, comment, _ in
viewModel.donation(can: can, comment: comment) viewModel.donation(can: can, comment: comment)
} }
} }

View File

@ -18,10 +18,11 @@ struct LiveRoomDonationDialogView: View {
@State private var donationMessage = "" @State private var donationMessage = ""
@State private var isShowErrorPopup = false @State private var isShowErrorPopup = false
@State private var errorMessage = "" @State private var errorMessage = ""
@State private var isSecret = false
@Binding var isShowing: Bool @Binding var isShowing: Bool
let isAudioContentDonation: Bool let isAudioContentDonation: Bool
let onClickDonation: (Int, String) -> Void let onClickDonation: (Int, String, Bool) -> Void
@StateObject var keyboardHandler = KeyboardHandler() @StateObject var keyboardHandler = KeyboardHandler()
@ -82,6 +83,27 @@ struct LiveRoomDonationDialogView: View {
.foregroundColor(Color.gray90) .foregroundColor(Color.gray90)
.padding(.top, 16) .padding(.top, 16)
if !isAudioContentDonation {
HStack(spacing: 0) {
Spacer()
HStack(spacing: 8) {
Image(isSecret ? "btn_select_checked" : "btn_select_normal")
.resizable()
.frame(width: 20, height: 20)
Text("비밀후원")
.font(.custom(Font.medium.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
}
.onTapGesture {
isSecret.toggle()
}
}
.padding(.horizontal, 20)
.padding(.top, 16)
}
TextField("몇 캔을 후원할까요?", text: $donationCan) TextField("몇 캔을 후원할까요?", text: $donationCan)
.font(.custom(Font.medium.rawValue, size: 13.3)) .font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.grayee) .foregroundColor(Color.grayee)
@ -221,7 +243,7 @@ struct LiveRoomDonationDialogView: View {
.onTapGesture { .onTapGesture {
if !donationCan.trimmingCharacters(in: .whitespaces).isEmpty, if !donationCan.trimmingCharacters(in: .whitespaces).isEmpty,
let can = Int(donationCan) { let can = Int(donationCan) {
onClickDonation(can, donationMessage) onClickDonation(can, donationMessage, isSecret)
isShowing = false isShowing = false
} else { } else {
errorMessage = "1캔 이상 후원하실 수 있습니다." errorMessage = "1캔 이상 후원하실 수 있습니다."

View File

@ -73,8 +73,8 @@ final class LiveRepository {
return api.requestPublisher(.getRoomInfo(roomId: roomId)) return api.requestPublisher(.getRoomInfo(roomId: roomId))
} }
func donation(roomId: Int, can: Int, message: String = "") -> AnyPublisher<Response, MoyaError> { func donation(roomId: Int, can: Int, message: String = "", isSecret: Bool = false) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.donation(request: LiveRoomDonationRequest(roomId: roomId, can: can, message: message))) return api.requestPublisher(.donation(request: LiveRoomDonationRequest(roomId: roomId, can: can, message: message, isSecret: isSecret)))
} }
func refundDonation(roomId: Int) -> AnyPublisher<Response, MoyaError> { func refundDonation(roomId: Int) -> AnyPublisher<Response, MoyaError> {

View File

@ -9,7 +9,7 @@ import Foundation
struct LiveRoomChatRawMessage: Codable { struct LiveRoomChatRawMessage: Codable {
enum LiveRoomChatRawMessageType: String, Codable { enum LiveRoomChatRawMessageType: String, Codable {
case DONATION, EDIT_ROOM_INFO, SET_MANAGER, TOGGLE_ROULETTE, ROULETTE_DONATION case DONATION, SECRET_DONATION, EDIT_ROOM_INFO, SET_MANAGER, TOGGLE_ROULETTE, ROULETTE_DONATION
} }
let type: LiveRoomChatRawMessageType let type: LiveRoomChatRawMessageType

View File

@ -43,7 +43,7 @@ struct LiveRoomDonationChatItemView: View {
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(Color(hex: "fdca2f")) .foregroundColor(Color(hex: "fdca2f"))
Text("을 후원하셨습니다.") Text(chatMessage.chat.contains("비밀") ? "을 비밀후원하셨습니다.💰🪙" : "을 후원하셨습니다.💰🪙")
.font(.system(size: 15)) .font(.system(size: 15))
.foregroundColor(.white) .foregroundColor(.white)
} }
@ -58,6 +58,7 @@ struct LiveRoomDonationChatItemView: View {
.padding(13) .padding(13)
.frame(width: screenSize().width - 86, alignment: .leading) .frame(width: screenSize().width - 86, alignment: .leading)
.background( .background(
chatMessage.chat.contains("비밀") ? Color(hex: "333333").opacity(0.8) :
chatMessage.can >= 10000 ? Color(hex: "c25264").opacity(0.8) : chatMessage.can >= 10000 ? Color(hex: "c25264").opacity(0.8) :
chatMessage.can >= 5000 ? Color(hex: "d85e37").opacity(0.8) : chatMessage.can >= 5000 ? Color(hex: "d85e37").opacity(0.8) :
chatMessage.can >= 1000 ? Color(hex: "d38c38").opacity(0.8) : chatMessage.can >= 1000 ? Color(hex: "d38c38").opacity(0.8) :

View File

@ -11,5 +11,6 @@ struct LiveRoomDonationRequest: Encodable {
let roomId: Int let roomId: Int
let can: Int let can: Int
let message: String let message: String
var isSecret: Bool = false
let container: String = "ios" let container: String = "ios"
} }

View File

@ -380,11 +380,11 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
} }
} }
func donation(can: Int, message: String = "") { func donation(can: Int, message: String = "", isSecret: Bool = false) {
if can > 0 { if can > 0 {
isLoading = true isLoading = true
repository.donation(roomId: AppState.shared.roomId, can: can, message: message) repository.donation(roomId: AppState.shared.roomId, can: can, message: message, isSecret: isSecret)
.sink { result in .sink { result in
switch result { switch result {
case .finished: case .finished:
@ -402,9 +402,16 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
self.isLoading = false self.isLoading = false
if decoded.success { if decoded.success {
let rawMessage = "\(can)캔을 후원하셨습니다." var rawMessage = ""
if isSecret {
rawMessage = "\(can)캔을 비밀후원하셨습니다.💰🪙"
} else {
rawMessage = "\(can)캔을 후원하셨습니다.💰🪙"
}
let donationRawMessage = LiveRoomChatRawMessage( let donationRawMessage = LiveRoomChatRawMessage(
type: .DONATION, type: isSecret ? .SECRET_DONATION : .DONATION,
message: rawMessage, message: rawMessage,
can: can, can: can,
signature: decoded.data, signature: decoded.data,
@ -414,36 +421,68 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
UserDefaults.set(UserDefaults.int(forKey: .can) - can, forKey: .can) UserDefaults.set(UserDefaults.int(forKey: .can) - can, forKey: .can)
agora.sendRawMessageToGroup( if isSecret {
rawMessage: donationRawMessage, agora.sendRawMessageToPeer(
completion: { [unowned self] errorCode in peerId: String(liveRoomInfo!.creatorId), rawMessage: donationRawMessage,
if errorCode == .errorOk { completion: { [unowned self] errorCode in
let (nickname, profileUrl) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId)) if errorCode == .ok {
self.messages.append( let (nickname, profileUrl) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId))
LiveRoomDonationChat( self.messages.append(
profileUrl: profileUrl, LiveRoomDonationChat(
nickname: nickname, profileUrl: profileUrl,
chat: rawMessage, nickname: nickname,
can: can, chat: rawMessage,
donationMessage: message can: can,
donationMessage: message
)
) )
)
addSignature(signature: decoded.data)
totalDonationCan += can
addSignature(signature: decoded.data) self.messageChangeFlag.toggle()
if self.messages.count > 100 {
self.messageChangeFlag.toggle() self.messages.remove(at: 0)
if self.messages.count > 100 { }
self.messages.remove(at: 0) } else {
refundDonation()
} }
} else { },
fail: { [unowned self] in
refundDonation() refundDonation()
} }
}, )
fail: { [unowned self] in } else {
refundDonation() agora.sendRawMessageToGroup(
} rawMessage: donationRawMessage,
) completion: { [unowned self] errorCode in
if errorCode == .errorOk {
let (nickname, profileUrl) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId))
self.messages.append(
LiveRoomDonationChat(
profileUrl: profileUrl,
nickname: nickname,
chat: rawMessage,
can: can,
donationMessage: message
)
)
totalDonationCan += can
addSignature(signature: decoded.data)
self.messageChangeFlag.toggle()
if self.messages.count > 100 {
self.messages.remove(at: 0)
}
} else {
refundDonation()
}
},
fail: { [unowned self] in
refundDonation()
}
)
}
} else { } else {
if let message = decoded.message { if let message = decoded.message {
self.popupContent = message self.popupContent = message
@ -1858,6 +1897,31 @@ extension LiveRoomViewModel: AgoraRtmDelegate {
self.startNoChatting() self.startNoChatting()
} }
} }
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(LiveRoomChatRawMessage.self, from: rawMessage.rawData)
let (nickname, profileUrl) = getUserNicknameAndProfileUrl(accountId: Int(peerId)!)
if decoded.type == .SECRET_DONATION {
self.messages.append(
LiveRoomDonationChat(
profileUrl: profileUrl,
nickname: nickname,
chat: decoded.message,
can: decoded.can,
donationMessage: decoded.donationMessage ?? ""
)
)
if let signature = decoded.signature {
self.addSignature(signature: signature)
} else if let imageUrl = decoded.signatureImageUrl {
self.addSignatureImage(imageUrl: imageUrl)
}
}
} catch {
}
} }
} }
} }

View File

@ -203,10 +203,12 @@ struct LiveRoomViewV2: View {
) )
} }
LiveRoomRightBottomButton( if liveRoomInfo.creatorId != UserDefaults.int(forKey: .userId) {
imageName: "ic_donation", LiveRoomRightBottomButton(
onClick: { viewModel.isShowDonationPopup = true } imageName: "ic_donation",
) onClick: { viewModel.isShowDonationPopup = true }
)
}
LiveRoomRightBottomButton( LiveRoomRightBottomButton(
imageName: "ic_donation_message_list", imageName: "ic_donation_message_list",
@ -390,8 +392,8 @@ struct LiveRoomViewV2: View {
} }
if viewModel.isShowDonationPopup { if viewModel.isShowDonationPopup {
LiveRoomDonationDialogView(isShowing: $viewModel.isShowDonationPopup, isAudioContentDonation: false) { can, message in LiveRoomDonationDialogView(isShowing: $viewModel.isShowDonationPopup, isAudioContentDonation: false) { can, message, isSecret in
viewModel.donation(can: can, message: message) viewModel.donation(can: can, message: message, isSecret: isSecret)
} }
} }