sodalive-ios/SodaLive/Sources/Live/LiveViewModel.swift

689 lines
29 KiB
Swift

//
// LiveViewModel.swift
// SodaLive
//
// Created by klaus on 2023/08/09.
//
import Foundation
import Combine
final class LiveViewModel: ObservableObject {
private let repository = LiveRepository()
private let eventRepository = EventRepository()
private let liveRecommendRepository = LiveRecommendRepository()
private let creatorCommunityRepository = CreatorCommunityRepository()
private var subscription = Set<AnyCancellable>()
@Published private(set) var eventBannerItems = [EventItem]()
@Published private(set) var liveNowItems = [GetRoomListResponse]()
@Published private(set) var liveReservationItems = [GetRoomListResponse]()
@Published private(set) var recommendLiveItems: [GetRecommendLiveResponse] = []
@Published private(set) var recommendChannelItems: [GetRecommendChannelResponse] = []
@Published private(set) var followedChannelItems: [GetRecommendChannelResponse] = []
@Published private(set) var communityPostItems: [GetCommunityPostListResponse] = []
@Published var errorMessage = ""
@Published var isShowPopup = false
@Published var isRefresh = false
@Published var isLoading = false
@Published var isRecommendLiveLoading = false
@Published var isRecommendChannelLoading = false
@Published var isFollowedChannelLoading = false
@Published var paymentDialogTitle = ""
@Published var paymentDialogDesc = ""
@Published var isShowPaymentDialog = false
@Published var paymentDialogConfirmAction = {}
@Published var paymentDialogConfirmTitle = ""
@Published var secretOrPasswordDialogCan = 0
@Published var passwordDialogConfirmAction: (String) -> Void = { _ in }
@Published var isShowPasswordDialog = false
@Published var isFollowingList = UserDefaults.bool(forKey: .isFollowedChannel) {
didSet {
UserDefaults.set(isFollowingList, forKey: .isFollowedChannel)
}
}
let paymentDialogCancelTitle = "취소"
var page = 1
var isLast = false
private let pageSize = 10
var selectedDateString: String = "" {
didSet {
if !selectedDateString.trimmingCharacters(in: .whitespaces).isEmpty {
page = 1
isLast = false
liveReservationItems.removeAll()
getLiveReservationList()
}
}
}
func hidePopup() {
isShowPaymentDialog = false
isShowPasswordDialog = false
paymentDialogTitle = ""
paymentDialogDesc = ""
paymentDialogConfirmAction = {}
secretOrPasswordDialogCan = 0
passwordDialogConfirmAction = { _ in }
}
func getSummary() {
getFollowedChannelList()
getRecommendChannelList()
getRecommendLive()
getLatestPostListFromCreatorsYouFollow()
isLoading = true
eventBannerItems.removeAll()
liveNowItems.removeAll()
liveReservationItems.removeAll()
let liveNow = repository.roomList(
request: GetRoomListRequest(
timezone: TimeZone.current.identifier,
dateString: nil,
status: .NOW,
page: 1,
size: 10
)
)
let liveReservation = repository.roomList(
request: GetRoomListRequest(
timezone: TimeZone.current.identifier,
dateString: nil,
status: .RESERVATION,
page: 1,
size: 10
)
)
let event = eventRepository.getEvents()
Publishers
.CombineLatest3(liveNow, liveReservation, event)
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { (now, reservation, eventResponse) in
let nowData = now.data
let reservationData = reservation.data
let eventData = eventResponse.data
let jsonDecoder = JSONDecoder()
do {
let nowDecoded = try jsonDecoder.decode(ApiResponse<[GetRoomListResponse]>.self, from: nowData)
if let data = nowDecoded.data, nowDecoded.success {
self.liveNowItems.removeAll()
self.liveNowItems.append(contentsOf: data)
} else {
if let message = nowDecoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
do {
let reservationDecoded = try jsonDecoder.decode(ApiResponse<[GetRoomListResponse]>.self, from: reservationData)
if let data = reservationDecoded.data, reservationDecoded.success {
self.liveReservationItems.removeAll()
self.liveReservationItems.append(contentsOf: data)
} else {
if let message = reservationDecoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
do {
let eventDecoded = try jsonDecoder.decode(ApiResponse<GetEventResponse>.self, from: eventData)
if let data = eventDecoded.data, eventDecoded.success {
self.eventBannerItems.removeAll()
self.eventBannerItems.append(contentsOf: data.eventList)
} else {
if let message = eventDecoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
self.isLoading = false
self.isRefresh = false
}
.store(in: &subscription)
}
func enterRoom(roomId: Int, onSuccess: (() -> Void)? = nil, password: String? = nil) {
isLoading = true
let request = EnterOrQuitLiveRoomRequest(roomId: roomId, password: password)
repository.enterRoom(request: request)
.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(ApiResponseWithoutData.self, from: responseData)
if decoded.success {
AppState.shared.roomId = roomId
if let onSuccess = onSuccess {
onSuccess()
} else {
if roomId > 0 {
AppState.shared.isShowPlayer = true
}
}
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "라이브에 입장하지 못했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "라이브에 입장하지 못했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
}
.store(in: &subscription)
}
func startLive(roomId: Int) {
isLoading = true
repository.startLive(roomId: 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(ApiResponseWithoutData.self, from: responseData)
if decoded.success {
getSummary()
enterRoom(roomId: roomId)
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
}
.store(in: &subscription)
}
func getLiveNowList() {
if (!isLast && !isLoading) {
isLoading = true
repository.roomList(
request: GetRoomListRequest(
timezone: TimeZone.current.identifier,
dateString: nil,
status: .NOW,
page: page,
size: pageSize
)
)
.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<[GetRoomListResponse]>.self, from: responseData)
if let data = decoded.data, decoded.success {
if !data.isEmpty {
page += 1
self.liveNowItems.append(contentsOf: data)
} else {
isLast = true
}
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
}
.store(in: &subscription)
}
}
func getLiveReservationList() {
if (!isLast && !isLoading) {
isLoading = true
repository.roomList(
request: GetRoomListRequest(
timezone: TimeZone.current.identifier,
dateString: selectedDateString,
status: .RESERVATION,
page: page,
size: pageSize
)
)
.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<[GetRoomListResponse]>.self, from: responseData)
if let data = decoded.data, decoded.success {
if !data.isEmpty {
page += 1
self.liveReservationItems.append(contentsOf: data)
} else {
isLast = true
}
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
}
.store(in: &subscription)
}
}
func reservationLiveRoom(roomId: Int) {
getRoomDetail(roomId: roomId) { [unowned self] in
if ($0.manager.id == UserDefaults.int(forKey: .userId)) {
self.errorMessage = "내가 만든 라이브는 예약할 수 없습니다."
self.isShowPopup = true
} else {
if $0.isPrivateRoom {
self.passwordDialogConfirmAction = { password in
self.reservation(roomId: roomId, password: password)
}
self.isShowPasswordDialog = true
} else {
if ($0.price == 0 || $0.isPaid) {
self.reservation(roomId: roomId)
} else {
self.paymentDialogTitle = "\($0.price)캔으로 예약"
self.paymentDialogDesc = "'\($0.title)' 라이브에 참여하기 위해 결제합니다."
self.paymentDialogConfirmTitle = "결제 후 예약하기"
self.paymentDialogConfirmAction = { [unowned self] in
hidePopup()
reservation(roomId: roomId)
}
self.isShowPaymentDialog = true
}
}
}
}
}
func enterLiveRoom(roomId: Int) {
getRoomDetail(roomId: roomId) {
if let _ = $0.channelName {
if $0.manager.id == UserDefaults.int(forKey: .userId) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {
self.enterRoom(roomId: roomId)
}
} else if ($0.price == 0 || $0.isPaid) {
if $0.isPrivateRoom {
self.passwordDialogConfirmAction = { password in
self.enterRoom(roomId: roomId, password: password)
}
self.isShowPasswordDialog = true
} else {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {
self.enterRoom(roomId: roomId)
}
}
} else {
if $0.isPrivateRoom {
self.secretOrPasswordDialogCan = $0.price
self.passwordDialogConfirmAction = { password in
self.enterRoom(roomId: roomId, password: password)
}
self.isShowPasswordDialog = true
} else {
self.paymentDialogTitle = "\($0.price)캔으로 입장"
self.paymentDialogDesc = "'\($0.title)' 라이브에 참여하기 위해 결제합니다."
self.paymentDialogConfirmTitle = "결제 후 참여하기"
self.paymentDialogConfirmAction = { [unowned self] in
hidePopup()
self.enterRoom(roomId: roomId)
}
self.isShowPaymentDialog = true
}
}
} else {
AppState.shared.setAppStep(
step: .liveDetail(
roomId: roomId,
onClickParticipant: {},
onClickReservation: { self.reservationLiveRoom(roomId: roomId) },
onClickStart: { self.startLive(roomId: roomId) },
onClickCancel: { self.getSummary() }
)
)
}
}
}
private func getRoomDetail(roomId: Int, onSuccess: @escaping (GetRoomDetailResponse) -> Void) {
isLoading = true
repository.getRoomDetail(roomId: roomId)
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { response in
let responseData = response.data
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(ApiResponse<GetRoomDetailResponse>.self, from: responseData)
if let data = decoded.data, decoded.success {
onSuccess(data)
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "라이브 정보를 가져오지 못했습니다.\n다시 시도해 주세요."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "라이브 정보를 가져오지 못했습니다.\n다시 시도해 주세요."
self.isShowPopup = true
}
self.isLoading = false
}
.store(in: &subscription)
}
private func reservation(roomId: Int, password: String? = nil) {
isLoading = true
let request = MakeLiveReservationRequest(roomId: roomId, password: password)
repository.makeReservation(request: request)
.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<MakeLiveReservationResponse>.self, from: responseData)
if let response = decoded.data, decoded.success {
self.getSummary()
AppState.shared.setAppStep(step: .liveReservationComplete(response: response))
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
}
.store(in: &subscription)
}
private func getFollowedChannelList() {
followedChannelItems.removeAll()
isFollowedChannelLoading = true
liveRecommendRepository.getFollowedChannelList()
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { [unowned self] response in
self.isFollowedChannelLoading = false
let responseData = response.data
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(ApiResponse<[GetRecommendChannelResponse]>.self, from: responseData)
if let data = decoded.data, decoded.success {
self.followedChannelItems.append(contentsOf: data)
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
}
.store(in: &subscription)
}
private func getRecommendChannelList() {
recommendChannelItems.removeAll()
isRecommendChannelLoading = true
liveRecommendRepository.getRecommendChannelList()
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { [unowned self] response in
self.isRecommendChannelLoading = false
let responseData = response.data
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(ApiResponse<[GetRecommendChannelResponse]>.self, from: responseData)
if let data = decoded.data, decoded.success {
self.recommendChannelItems.append(contentsOf: data)
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
}
.store(in: &subscription)
}
private func getRecommendLive() {
recommendLiveItems.removeAll()
isRecommendLiveLoading = true
liveRecommendRepository.getRecommendLive()
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { [unowned self] response in
self.isRecommendLiveLoading = false
let responseData = response.data
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(ApiResponse<[GetRecommendLiveResponse]>.self, from: responseData)
if let data = decoded.data, decoded.success {
self.recommendLiveItems.append(contentsOf: data)
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
}
.store(in: &subscription)
}
private func getLatestPostListFromCreatorsYouFollow() {
communityPostItems.removeAll()
creatorCommunityRepository.getLatestPostListFromCreatorsYouFollow()
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { [unowned self] response in
self.isRecommendLiveLoading = false
let responseData = response.data
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(ApiResponse<[GetCommunityPostListResponse]>.self, from: responseData)
if let data = decoded.data, decoded.success {
self.communityPostItems.append(contentsOf: data)
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
}
.store(in: &subscription)
}
}