From b4f99af29175d93f2808819f56fc0e12055cc632 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Tue, 12 Nov 2024 00:33:28 +0900 Subject: [PATCH] =?UTF-8?q?=ED=95=98=ED=8A=B8=20=EB=9E=AD=ED=82=B9=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SodaLive/Sources/Live/LiveApi.swift | 8 +- SodaLive/Sources/Live/LiveRepository.swift | 4 + .../Dialog/LiveRoomHeartRankingDialog.swift | 132 ++++++++++++++++++ .../Dialog/LiveRoomHeartRankingItemView.swift | 130 +++++++++++++++++ .../Sources/Live/Room/LiveRoomViewModel.swift | 36 +++++ .../View/LiveRoomInfoGuestView.swift | 3 + .../Component/View/LiveRoomInfoHostView.swift | 3 + .../V2/GetLiveRoomHeartListResponse.swift | 18 +++ .../Sources/Live/Room/V2/LiveRoomViewV2.swift | 17 +++ 9 files changed, 349 insertions(+), 2 deletions(-) create mode 100644 SodaLive/Sources/Live/Room/Dialog/LiveRoomHeartRankingDialog.swift create mode 100644 SodaLive/Sources/Live/Room/Dialog/LiveRoomHeartRankingItemView.swift create mode 100644 SodaLive/Sources/Live/Room/V2/GetLiveRoomHeartListResponse.swift diff --git a/SodaLive/Sources/Live/LiveApi.swift b/SodaLive/Sources/Live/LiveApi.swift index b973fe0..15f6e29 100644 --- a/SodaLive/Sources/Live/LiveApi.swift +++ b/SodaLive/Sources/Live/LiveApi.swift @@ -39,6 +39,7 @@ enum LiveApi { case getAllMenuPreset(creatorId: Int) case likeHeart(request: LiveRoomLikeHeartRequest) case getTotalHeartCount(roomId: Int) + case heartStatus(roomId: Int) } extension LiveApi: TargetType { @@ -137,12 +138,15 @@ extension LiveApi: TargetType { case .getTotalHeartCount(let roomId): return "/live/room/\(roomId)/heart-total" + + case .heartStatus(let roomId): + return "/live/room/\(roomId)/heart-list" } } var method: Moya.Method { switch self { - case .roomList, .recentVisitRoomUsers, .getReservations, .getReservation, .getRoomDetail, .getTags, .getRecentRoomInfo, .getRoomInfo, .donationStatus, .donationTotal, .getDonationMessageList, .getUserProfile, .getAllMenuPreset, .getTotalHeartCount: + case .roomList, .recentVisitRoomUsers, .getReservations, .getReservation, .getRoomDetail, .getTags, .getRecentRoomInfo, .getRoomInfo, .donationStatus, .donationTotal, .getDonationMessageList, .getUserProfile, .getAllMenuPreset, .getTotalHeartCount, .heartStatus: return .get case .makeReservation, .enterRoom, .createRoom, .quitRoom, .donation, .refundDonation, .kickOut, .likeHeart: @@ -174,7 +178,7 @@ extension LiveApi: TargetType { parameters: parameters, encoding: URLEncoding.queryString) - case .recentVisitRoomUsers, .getTags, .getRecentRoomInfo, .getRoomInfo, .refundDonation, .donationStatus, .donationTotal, .getUserProfile, .getTotalHeartCount: + case .recentVisitRoomUsers, .getTags, .getRecentRoomInfo, .getRoomInfo, .refundDonation, .donationStatus, .donationTotal, .getUserProfile, .getTotalHeartCount, .heartStatus: return .requestPlain case .getReservations(let isActive): diff --git a/SodaLive/Sources/Live/LiveRepository.swift b/SodaLive/Sources/Live/LiveRepository.swift index 9ed713e..2c3dbc9 100644 --- a/SodaLive/Sources/Live/LiveRepository.swift +++ b/SodaLive/Sources/Live/LiveRepository.swift @@ -128,4 +128,8 @@ final class LiveRepository { func getTotalHeartCount(roomId: Int) -> AnyPublisher { return api.requestPublisher(.getTotalHeartCount(roomId: roomId)) } + + func heartStatus(roomId: Int) -> AnyPublisher { + return api.requestPublisher(.heartStatus(roomId: roomId)) + } } diff --git a/SodaLive/Sources/Live/Room/Dialog/LiveRoomHeartRankingDialog.swift b/SodaLive/Sources/Live/Room/Dialog/LiveRoomHeartRankingDialog.swift new file mode 100644 index 0000000..de79e75 --- /dev/null +++ b/SodaLive/Sources/Live/Room/Dialog/LiveRoomHeartRankingDialog.swift @@ -0,0 +1,132 @@ +// +// LiveRoomHeartRankingDialog.swift +// SodaLive +// +// Created by klaus on 11/11/24. +// + +import SwiftUI + +struct LiveRoomHeartRankingDialog: View { + + @Binding var isShowing: Bool + @Binding var isShowPopup: Bool + + let errorMessage: String + let isLoading: Bool + let columns = [GridItem(.flexible())] + let heartStatus: GetLiveRoomHeartListResponse? + let getHeartStatus: () -> Void + + var body: some View { + ZStack { + VStack(spacing: 0) { + HStack(spacing: 0) { + Text("현재 라이브 하트랭킹") + .font(.custom(Font.bold.rawValue, size: 14.7)) + .foregroundColor(Color.grayee) + + Spacer() + + Image("ic_close_white") + .onTapGesture { isShowing = false } + } + + if let heartStatus = heartStatus { + HStack(alignment: .center, spacing: 0) { + Text("합계") + .font(.custom(Font.bold.rawValue, size: 13.3)) + .foregroundColor(Color.grayd2) + + Spacer() + + Text("\(heartStatus.totalHeart)") + .font(.custom(Font.medium.rawValue, size: 14)) + .foregroundColor(Color.button) + + Text("하트") + .font(.custom(Font.medium.rawValue, size: 10.7)) + .foregroundColor(Color.graybb) + .padding(.leading, 4) + } + .padding(.horizontal, 18.7) + .padding(.vertical, 10.7) + .background(Color.bg) + .cornerRadius(8) + .padding(.top, 25) + + HStack(spacing: 0) { + Text("전체") + .font(.custom(Font.medium.rawValue, size: 14.7)) + .foregroundColor(Color.grayee) + + Text("\(heartStatus.totalCount)") + .font(.custom(Font.medium.rawValue, size: 12)) + .foregroundColor(Color.button) + .padding(.leading, 6.7) + + Text(" 명") + .font(.custom(Font.medium.rawValue, size: 12)) + .foregroundColor(Color.gray77) + + Spacer() + } + .padding(.top, 13.3) + + ScrollView(showsIndicators: false) { + LazyVGrid(columns: columns, spacing: 0) { + ForEach(0.. 10 ? "\(String(item.nickname.prefix(10)))..." : item.nickname + Text(nickname) + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color.grayee) + + Spacer() + + VStack(alignment: .trailing, spacing: 8) { + if item.heart > 0 { + HStack(spacing: 4) { + Text("\(item.heart)") + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color.button) + + Text("하트") + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color.grayee) + } + } + } + } + .padding(.horizontal, isTop3Index(index: index) ? 20 : 0) + .padding(.top, getTopPadding(index: index)) + .padding(.bottom, getBottomPadding(index: index)) + .background(Color.bg.opacity(isTop3Index(index: index) ? 1 : 0)) + .cornerRadius(4.7, corners: cornerRadiusConers(index: index)) + .padding(.horizontal, isTop3Index(index: index) ? 0 : 6.7) + } + + private func isTop3Index(index: Int) -> Bool { + return index == 0 || index == 1 || index == 2 + } + + private func getTopPadding(index: Int) -> CGFloat { + if index == 0 || index == 3 { + return 20 + } else { + return 6.7 + } + } + + private func getBottomPadding(index: Int) -> CGFloat { + if (index == 0 && itemCount == 1) || (index == 1 && itemCount == 2) || index == 2 { + return 20 + } else { + return 6.7 + } + } + + private func cornerRadiusConers(index: Int) -> UIRectCorner { + if (index == 0 && itemCount == 1) { + return [.topLeft, .topRight, .bottomLeft, .bottomRight] + } else if index == 0 { + return [.topLeft, .topRight] + } else if (index == 1 && itemCount == 2) || index == 2 { + return [.bottomLeft, .bottomRight] + } else { + return [] + } + } +} + +#Preview { + LiveRoomHeartRankingItemView( + index: 0, + item: GetLiveRoomHeartListItem(profileImage: "", nickname: "테스트", heart: 10), + itemCount: 3 + ) +} diff --git a/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift b/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift index 5daa638..70014ef 100644 --- a/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift +++ b/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift @@ -97,6 +97,8 @@ final class LiveRoomViewModel: NSObject, ObservableObject { @Published var isShowDonationRankingPopup = false + @Published var isShowHeartRankingPopup = false + @Published var isSpeakerFold = false @Published var isShowQuitPopup = false @@ -130,6 +132,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject { @Published var isBgOn = true @Published var isSignatureOn = true @Published var donationStatus: GetLiveRoomDonationStatusResponse? + @Published var heartStatus: GetLiveRoomHeartListResponse? @Published private(set) var offset: CGFloat = 0 @Published private(set) var originOffset: CGFloat = 0 @@ -1027,6 +1030,39 @@ final class LiveRoomViewModel: NSObject, ObservableObject { .store(in: &subscription) } + func getHeartStatus() { + isLoading = true + repository.heartStatus(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.heartStatus = data + } else { + self.errorMessage = "하트 랭킹을 가져오지 못했습니다\n다시 시도해 주세요." + self.isShowPopup = true + } + } catch { + self.isLoading = false + self.errorMessage = "하트 랭킹을 가져오지 못했습니다\n다시 시도해 주세요." + self.isShowPopup = true + } + } + .store(in: &subscription) + } + func creatorFollow(creatorId: Int? = nil, isGetUserProfile: Bool = false) { var userId = 0 diff --git a/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoGuestView.swift b/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoGuestView.swift index af0cbf7..62c845b 100644 --- a/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoGuestView.swift +++ b/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoGuestView.swift @@ -36,6 +36,7 @@ struct LiveRoomInfoGuestView: View { let onClickProfile: (Int) -> Void let onClickNotice: () -> Void let onClickMenuPan: () -> Void + let onClickTotalHeart: () -> Void let onClickTotalDonation: () -> Void let onClickChangeListener: () -> Void let onClickToggleSignature: () -> Void @@ -179,6 +180,7 @@ struct LiveRoomInfoGuestView: View { RoundedRectangle(cornerRadius: 5.3) .stroke(Color.graybb, lineWidth: 1) ) + .onTapGesture { onClickTotalHeart() } HStack(spacing: 6.7) { Image("ic_can") @@ -260,6 +262,7 @@ struct LiveRoomInfoGuestView_Previews: PreviewProvider { onClickProfile: { _ in }, onClickNotice: {}, onClickMenuPan: {}, + onClickTotalHeart: {}, onClickTotalDonation: {}, onClickChangeListener: {}, onClickToggleSignature: {} diff --git a/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoHostView.swift b/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoHostView.swift index 55c2cfb..7d2e072 100644 --- a/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoHostView.swift +++ b/SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoHostView.swift @@ -37,6 +37,7 @@ struct LiveRoomInfoHostView: View { let onClickProfile: (Int) -> Void let onClickNotice: () -> Void let onClickMenuPan: () -> Void + let onClickTotalHeart: () -> Void let onClickTotalDonation: () -> Void let onClickParticipants: () -> Void let onClickToggleSignature: () -> Void @@ -177,6 +178,7 @@ struct LiveRoomInfoHostView: View { RoundedRectangle(cornerRadius: 5.3) .stroke(Color.graybb, lineWidth: 1) ) + .onTapGesture { onClickTotalHeart() } HStack(spacing: 6.7) { Image("ic_can") @@ -269,6 +271,7 @@ struct LiveRoomInfoHostView_Previews: PreviewProvider { onClickProfile: { _ in }, onClickNotice: {}, onClickMenuPan: {}, + onClickTotalHeart: {}, onClickTotalDonation: {}, onClickParticipants: {}, onClickToggleSignature: {} diff --git a/SodaLive/Sources/Live/Room/V2/GetLiveRoomHeartListResponse.swift b/SodaLive/Sources/Live/Room/V2/GetLiveRoomHeartListResponse.swift new file mode 100644 index 0000000..b12a1b7 --- /dev/null +++ b/SodaLive/Sources/Live/Room/V2/GetLiveRoomHeartListResponse.swift @@ -0,0 +1,18 @@ +// +// GetLiveRoomHeartListResponse.swift +// SodaLive +// +// Created by klaus on 11/11/24. +// + +struct GetLiveRoomHeartListResponse: Decodable { + let heartList: [GetLiveRoomHeartListItem] + let totalCount: Int + let totalHeart: Int +} + +struct GetLiveRoomHeartListItem: Decodable { + let profileImage: String + let nickname: String + let heart: Int +} diff --git a/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift b/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift index 21f8959..fc0c1e8 100644 --- a/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift +++ b/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift @@ -64,6 +64,9 @@ struct LiveRoomViewV2: View { onClickMenuPan: { viewModel.isShowMenuPan.toggle() }, + onClickTotalHeart: { + viewModel.isShowHeartRankingPopup = true + }, onClickTotalDonation: { viewModel.isShowDonationRankingPopup = true }, @@ -119,6 +122,9 @@ struct LiveRoomViewV2: View { onClickMenuPan: { viewModel.isShowMenuPan.toggle() }, + onClickTotalHeart: { + viewModel.isShowHeartRankingPopup = true + }, onClickTotalDonation: { viewModel.isShowDonationRankingPopup = true }, @@ -747,6 +753,17 @@ struct LiveRoomViewV2: View { .sheet(isPresented: $viewModel.isShowDonationRankingPopup) { LiveRoomDonationRankingDialog(isShowing: $viewModel.isShowDonationRankingPopup) } + .sheet(isPresented: $viewModel.isShowHeartRankingPopup) { + LiveRoomHeartRankingDialog( + isShowing: $viewModel.isShowHeartRankingPopup, + isShowPopup: $viewModel.isShowPopup, + errorMessage: viewModel.errorMessage, + isLoading: viewModel.isLoading, + heartStatus: viewModel.heartStatus + ) { + viewModel.getHeartStatus() + } + } .sheet(isPresented: $viewModel.isShowDonationMessagePopup) { LiveRoomDonationMessageDialog(viewModel: viewModel, isShowing: $viewModel.isShowDonationMessagePopup) }