룰렛 변경
- 확률 수동 설정 - 여러개의 룰렛이 켜져있을 때 선택하여 돌리기 - 후원 히스토리에 룰렛 히스토리
This commit is contained in:
		
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 2.4 KiB | 
| @@ -1,21 +0,0 @@ | |||||||
| { |  | ||||||
|   "images" : [ |  | ||||||
|     { |  | ||||||
|       "idiom" : "universal", |  | ||||||
|       "scale" : "1x" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "idiom" : "universal", |  | ||||||
|       "scale" : "2x" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "filename" : "btn_plus_round_rect.png", |  | ||||||
|       "idiom" : "universal", |  | ||||||
|       "scale" : "3x" |  | ||||||
|     } |  | ||||||
|   ], |  | ||||||
|   "info" : { |  | ||||||
|     "author" : "xcode", |  | ||||||
|     "version" : 1 |  | ||||||
|   } |  | ||||||
| } |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 2.8 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 996 B | 
| @@ -9,7 +9,7 @@ | |||||||
|       "scale" : "2x" |       "scale" : "2x" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "filename" : "btn_minus_round_rect.png", |       "filename" : "ic_select_check_black.png", | ||||||
|       "idiom" : "universal", |       "idiom" : "universal", | ||||||
|       "scale" : "3x" |       "scale" : "3x" | ||||||
|     } |     } | ||||||
							
								
								
									
										
											BIN
										
									
								
								SodaLive/Resources/Assets.xcassets/ic_select_check_black.imageset/ic_select_check_black.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								SodaLive/Resources/Assets.xcassets/ic_select_check_black.imageset/ic_select_check_black.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 418 B | 
| @@ -23,7 +23,7 @@ struct LiveRoomDonationMessageDialog: View { | |||||||
|              |              | ||||||
|             VStack(spacing: 0) { |             VStack(spacing: 0) { | ||||||
|                 HStack(spacing: 0) { |                 HStack(spacing: 0) { | ||||||
|                     Text("후원메시지") |                     Text("후원 히스토리") | ||||||
|                         .font(.custom(Font.bold.rawValue, size: 14.7)) |                         .font(.custom(Font.bold.rawValue, size: 14.7)) | ||||||
|                         .foregroundColor(Color(hex: "eeeeee")) |                         .foregroundColor(Color(hex: "eeeeee")) | ||||||
|                      |                      | ||||||
| @@ -45,43 +45,19 @@ struct LiveRoomDonationMessageDialog: View { | |||||||
|                             ForEach(0..<viewModel.donationMessageList.count, id: \.self) { index in |                             ForEach(0..<viewModel.donationMessageList.count, id: \.self) { index in | ||||||
|                                 let donationMessage = viewModel.donationMessageList[index] |                                 let donationMessage = viewModel.donationMessageList[index] | ||||||
|                                  |                                  | ||||||
|                                 HStack(alignment: .top, spacing: 0) { |                                 LiveRoomDonationMessageItemView(message: donationMessage) { | ||||||
|                                     VStack(alignment: .leading, spacing: 8) { |                                     viewModel.deleteDonationMessage(uuid: $0) | ||||||
|                                         Text("\(donationMessage.nickname)님이") |  | ||||||
|                                             .font(.custom(Font.medium.rawValue, size: 13.3)) |  | ||||||
|                                             .foregroundColor(.white) |  | ||||||
|                                          |  | ||||||
|                                         Text("\(donationMessage.canMessage)") |  | ||||||
|                                             .font(.custom(Font.medium.rawValue, size: 13.3)) |  | ||||||
|                                             .foregroundColor(.white) |  | ||||||
|                                          |  | ||||||
|                                         Text("'\(donationMessage.donationMessage)'") |  | ||||||
|                                             .font(.custom(Font.medium.rawValue, size: 13.3)) |  | ||||||
|                                             .foregroundColor(.white) |  | ||||||
|                                     } |  | ||||||
|                                      |  | ||||||
|                                     Spacer() |  | ||||||
|                                      |  | ||||||
|                                     Image("ic_close_white") |  | ||||||
|                                         .resizable() |  | ||||||
|                                         .frame(width: 13.3, height: 13.3) |  | ||||||
|                                         .onTapGesture { |  | ||||||
|                                             viewModel.deleteDonationMessage(uuid: donationMessage.uuid) |  | ||||||
|                                         } |  | ||||||
|                                 } |                                 } | ||||||
|                                 .padding(13.3) |  | ||||||
|                                 .background(Color(hex: "333333")) |  | ||||||
|                                 .cornerRadius(5.3) |  | ||||||
|                                 .onTapGesture { |                                 .onTapGesture { | ||||||
|                                     UIPasteboard.general.string = donationMessage.donationMessage |                                     UIPasteboard.general.string = donationMessage.donationMessage | ||||||
|                                     self.viewModel.errorMessage = "후원 메시지가 복사되었습니다." |                                     self.viewModel.errorMessage = "후원 히스토리가 복사되었습니다." | ||||||
|                                     self.viewModel.isShowPopup = true |                                     self.viewModel.isShowPopup = true | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         .padding(.top, 18.7) |                         .padding(.top, 18.7) | ||||||
|                     } else { |                     } else { | ||||||
|                         Text("후원메시지가 없습니다.") |                         Text("후원 히스토리가 없습니다.") | ||||||
|                             .font(.custom(Font.medium.rawValue, size: 14.7)) |                             .font(.custom(Font.medium.rawValue, size: 14.7)) | ||||||
|                             .foregroundColor(Color(hex: "eeeeee")) |                             .foregroundColor(Color(hex: "eeeeee")) | ||||||
|                             .padding(.top, 30) |                             .padding(.top, 30) | ||||||
|   | |||||||
| @@ -0,0 +1,68 @@ | |||||||
|  | // | ||||||
|  | //  LiveRoomDonationMessageItemView.swift | ||||||
|  | //  SodaLive | ||||||
|  | // | ||||||
|  | //  Created by klaus on 5/11/24. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | import SwiftUI | ||||||
|  |  | ||||||
|  | struct LiveRoomDonationMessageItemView: View { | ||||||
|  |      | ||||||
|  |     let message: LiveRoomDonationMessage | ||||||
|  |     let deleteDonationMessage: (String) -> Void | ||||||
|  |      | ||||||
|  |     var body: some View { | ||||||
|  |         HStack(alignment: .top, spacing: 0) { | ||||||
|  |             VStack(alignment: .leading, spacing: 8) { | ||||||
|  |                 Text("\(message.nickname)님이") | ||||||
|  |                     .font(.custom(Font.medium.rawValue, size: 13.3)) | ||||||
|  |                     .foregroundColor(.white) | ||||||
|  |                  | ||||||
|  |                 if !message.canMessage.trimmingCharacters(in: .whitespaces).isEmpty { | ||||||
|  |                     Text("\(message.canMessage)") | ||||||
|  |                         .font(.custom(Font.medium.rawValue, size: 13.3)) | ||||||
|  |                         .foregroundColor(.white) | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  |                 Text("'\(message.donationMessage)'") | ||||||
|  |                     .font(.custom(Font.medium.rawValue, size: 13.3)) | ||||||
|  |                     .foregroundColor(.white) | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             Spacer() | ||||||
|  |              | ||||||
|  |             Image("ic_close_white") | ||||||
|  |                 .resizable() | ||||||
|  |                 .frame(width: 13.3, height: 13.3) | ||||||
|  |                 .onTapGesture { deleteDonationMessage(message.uuid) } | ||||||
|  |         } | ||||||
|  |         .padding(13.3) | ||||||
|  |         .background(message.canMessage.trimmingCharacters(in: .whitespaces).isEmpty ? Color(hex: "c25264").opacity(0.8) : Color.gray33) | ||||||
|  |         .cornerRadius(5.3) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #Preview("일반후원 메시지") { | ||||||
|  |     LiveRoomDonationMessageItemView( | ||||||
|  |         message: LiveRoomDonationMessage( | ||||||
|  |             uuid: "", | ||||||
|  |             nickname: "유저2님이", | ||||||
|  |             canMessage: "10캔을 후원하셨습니다", | ||||||
|  |             donationMessage: "ㅅㅅㅅ" | ||||||
|  |         ), | ||||||
|  |         deleteDonationMessage: { _ in } | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #Preview("룰렛후원 메시지") { | ||||||
|  |     LiveRoomDonationMessageItemView( | ||||||
|  |         message: LiveRoomDonationMessage( | ||||||
|  |             uuid: "", | ||||||
|  |             nickname: "유저2님의 룰렛 결과?", | ||||||
|  |             canMessage: "", | ||||||
|  |             donationMessage: "[테스트] 당첨!" | ||||||
|  |         ), | ||||||
|  |         deleteDonationMessage: { _ in } | ||||||
|  |     ) | ||||||
|  | } | ||||||
| @@ -151,7 +151,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject { | |||||||
|     @Published var isShowRouletteSettings = false |     @Published var isShowRouletteSettings = false | ||||||
|      |      | ||||||
|     @Published var isShowRoulettePreview = false |     @Published var isShowRoulettePreview = false | ||||||
|     @Published var roulettePreview: RoulettePreview? = nil |     @Published var roulettePreviewList = [RoulettePreview]() | ||||||
|      |      | ||||||
|     @Published var isShowRoulette = false |     @Published var isShowRoulette = false | ||||||
|     @Published var rouletteItems = [String]() |     @Published var rouletteItems = [String]() | ||||||
| @@ -1456,6 +1456,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject { | |||||||
|      |      | ||||||
|     func showRoulette() { |     func showRoulette() { | ||||||
|         if let liveRoomInfo = liveRoomInfo, !isLoading { |         if let liveRoomInfo = liveRoomInfo, !isLoading { | ||||||
|  |             self.roulettePreviewList.removeAll() | ||||||
|             isLoading = true |             isLoading = true | ||||||
|              |              | ||||||
|             rouletteRepository.getRoulette(creatorId: liveRoomInfo.creatorId) |             rouletteRepository.getRoulette(creatorId: liveRoomInfo.creatorId) | ||||||
| @@ -1472,10 +1473,14 @@ final class LiveRoomViewModel: NSObject, ObservableObject { | |||||||
|                      |                      | ||||||
|                     do { |                     do { | ||||||
|                         let jsonDecoder = JSONDecoder() |                         let jsonDecoder = JSONDecoder() | ||||||
|                         let decoded = try jsonDecoder.decode(ApiResponse<GetRouletteResponse>.self, from: responseData) |                         let decoded = try jsonDecoder.decode(ApiResponse<[GetRouletteResponse]>.self, from: responseData) | ||||||
|                          |                          | ||||||
|                         if let data = decoded.data, decoded.success, !data.items.isEmpty { |                         if let data = decoded.data, decoded.success, !data.isEmpty { | ||||||
|                             self.roulettePreview = RoulettePreview(can: data.can, items: calculatePercentages(options: data.items)) |                             let roulettePreviewList = data | ||||||
|  |                                 .filter { $0.isActive } | ||||||
|  |                                 .filter { !$0.items.isEmpty} | ||||||
|  |                                 .map { RoulettePreview(id: $0.id, can: $0.can, items: calculatePercentages(options: $0.items)) } | ||||||
|  |                             self.roulettePreviewList.append(contentsOf: roulettePreviewList) | ||||||
|                             self.isShowRoulettePreview = true |                             self.isShowRoulettePreview = true | ||||||
|                         } else { |                         } else { | ||||||
|                             if let message = decoded.message { |                             if let message = decoded.message { | ||||||
| @@ -1494,10 +1499,10 @@ final class LiveRoomViewModel: NSObject, ObservableObject { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     func spinRoulette() { |     func spinRoulette(rouletteId: Int) { | ||||||
|         if !isLoading { |         if !isLoading { | ||||||
|             isLoading = true |             isLoading = true | ||||||
|             rouletteRepository.spinRoulette(request: SpinRouletteRequest(roomId: AppState.shared.roomId)) |             rouletteRepository.spinRoulette(request: SpinRouletteRequest(roomId: AppState.shared.roomId, rouletteId: rouletteId)) | ||||||
|                 .sink { result in |                 .sink { result in | ||||||
|                     switch result { |                     switch result { | ||||||
|                     case .finished: |                     case .finished: | ||||||
| @@ -1511,11 +1516,15 @@ final class LiveRoomViewModel: NSObject, ObservableObject { | |||||||
|                      |                      | ||||||
|                     do { |                     do { | ||||||
|                         let jsonDecoder = JSONDecoder() |                         let jsonDecoder = JSONDecoder() | ||||||
|                         let decoded = try jsonDecoder.decode(ApiResponse<GetRouletteResponse>.self, from: responseData) |                         let decoded = try jsonDecoder.decode(ApiResponse<SpinRouletteResponse>.self, from: responseData) | ||||||
|                          |                          | ||||||
|                         if let data = decoded.data, decoded.success, !data.items.isEmpty { |                         if let data = decoded.data, decoded.success, !data.items.isEmpty { | ||||||
|                             UserDefaults.set(UserDefaults.int(forKey: .can) - data.can, forKey: .can) |                             UserDefaults.set(UserDefaults.int(forKey: .can) - data.can, forKey: .can) | ||||||
|                             randomSelectRouletteItem(can: data.can, items: data.items) |                             self.rouletteItems.removeAll() | ||||||
|  |                             self.rouletteItems.append(contentsOf: data.items.map { $0.title }) | ||||||
|  |                             self.rouletteSelectedItem = data.result | ||||||
|  |                             self.rouletteCan = data.can | ||||||
|  |                             self.isShowRoulette = true | ||||||
|                         } else { |                         } else { | ||||||
|                             if let message = decoded.message { |                             if let message = decoded.message { | ||||||
|                                 self.errorMessage = message |                                 self.errorMessage = message | ||||||
| @@ -1574,35 +1583,13 @@ final class LiveRoomViewModel: NSObject, ObservableObject { | |||||||
|     } |     } | ||||||
|      |      | ||||||
|     private func calculatePercentages(options: [RouletteItem]) -> [RoulettePreviewItem] { |     private func calculatePercentages(options: [RouletteItem]) -> [RoulettePreviewItem] { | ||||||
|         let totalWeight = options.reduce(0) { $0 + $1.weight } |  | ||||||
|         let updatedOptions = options.map { option in |         let updatedOptions = options.map { option in | ||||||
|             let percent = floor(Double(option.weight) / Double(totalWeight) * 10000) / 100 |             return RoulettePreviewItem(title: option.title, percent: "\(String(format: "%.2f", option.percentage))%") | ||||||
|             return RoulettePreviewItem(title: option.title, percent: "\(String(format: "%.2f", percent))%") |  | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         return updatedOptions |         return updatedOptions | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     private func randomSelectRouletteItem(can: Int, items: [RouletteItem]) { |  | ||||||
|         isLoading = true |  | ||||||
|          |  | ||||||
|         var rouletteItems = [String]() |  | ||||||
|         items.forEach { |  | ||||||
|             var i = 1 |  | ||||||
|             while (i < $0.weight * 10) { |  | ||||||
|                 rouletteItems.append($0.title) |  | ||||||
|                 i += 1 |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         isLoading = false |  | ||||||
|         self.rouletteItems.removeAll() |  | ||||||
|         self.rouletteItems.append(contentsOf: items.map { $0.title }) |  | ||||||
|         self.rouletteSelectedItem = rouletteItems.randomElement()! |  | ||||||
|         self.rouletteCan = can |  | ||||||
|         self.isShowRoulette = true |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     private func refundRouletteDonation() { |     private func refundRouletteDonation() { | ||||||
|         isLoading = true |         isLoading = true | ||||||
|          |          | ||||||
|   | |||||||
| @@ -9,11 +9,10 @@ import SwiftUI | |||||||
|  |  | ||||||
| class RouletteOption: ObservableObject { | class RouletteOption: ObservableObject { | ||||||
|     var title: String |     var title: String | ||||||
|     var weight: Int |     var percentage: String = "" | ||||||
|     var percentage: String = "50.00" |  | ||||||
|      |      | ||||||
|     init(title: String, weight: Int) { |     init(title: String, percentage: String) { | ||||||
|         self.title = title |         self.title = title | ||||||
|         self.weight = weight |         self.percentage = percentage | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,10 +12,7 @@ struct RouletteSettingsOptionView: View { | |||||||
|     @ObservedObject var option: RouletteOption |     @ObservedObject var option: RouletteOption | ||||||
|      |      | ||||||
|     let index: Int |     let index: Int | ||||||
|      |  | ||||||
|     let onClickPlus: () -> Void |  | ||||||
|     let onClickDelete: () -> Void |     let onClickDelete: () -> Void | ||||||
|     let onClickSubstract: () -> Void |  | ||||||
|      |      | ||||||
|     var body: some View { |     var body: some View { | ||||||
|         VStack(spacing: 6.7) { |         VStack(spacing: 6.7) { | ||||||
| @@ -47,19 +44,28 @@ struct RouletteSettingsOptionView: View { | |||||||
|                     .background(Color(hex: "222222")) |                     .background(Color(hex: "222222")) | ||||||
|                     .cornerRadius(6.7) |                     .cornerRadius(6.7) | ||||||
|                  |                  | ||||||
|                 Text("\(option.percentage)%") |                 HStack(spacing: 0) { | ||||||
|  |                     TextField("0.00", text: $option.percentage) | ||||||
|  |                     .autocapitalization(.none) | ||||||
|  |                     .disableAutocorrection(true) | ||||||
|                     .font(.custom(Font.medium.rawValue, size: 13.3)) |                     .font(.custom(Font.medium.rawValue, size: 13.3)) | ||||||
|                     .foregroundColor(Color(hex: "eeeeee")) |                     .foregroundColor(Color(hex: "eeeeee")) | ||||||
|                     .padding(.horizontal, 13.3) |                     .keyboardType(.decimalPad) | ||||||
|                     .padding(.vertical, 16.7) |                     .onChange(of: option.percentage) { newValue in | ||||||
|                     .background(Color(hex: "222222")) |                         if newValue.count > 5 { | ||||||
|                     .cornerRadius(6.7) |                             option.percentage = String(newValue.prefix(5)) | ||||||
|                  |                         } | ||||||
|                 Image("btn_minus_round_rect") |                     } | ||||||
|                     .onTapGesture { onClickSubstract() } |                      | ||||||
|                  |                     Text("%") | ||||||
|                 Image("btn_plus_round_rect") |                         .font(.custom(Font.medium.rawValue, size: 13.3)) | ||||||
|                     .onTapGesture { onClickPlus() } |                         .foregroundColor(Color(hex: "eeeeee")) | ||||||
|  |                 } | ||||||
|  |                 .padding(.horizontal, 13.3) | ||||||
|  |                 .padding(.vertical, 16.7) | ||||||
|  |                 .frame(maxWidth: 85) | ||||||
|  |                 .background(Color(hex: "222222")) | ||||||
|  |                 .cornerRadius(6.7) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -68,11 +74,9 @@ struct RouletteSettingsOptionView: View { | |||||||
| struct RouletteSettingsOptionView_Previews: PreviewProvider { | struct RouletteSettingsOptionView_Previews: PreviewProvider { | ||||||
|     static var previews: some View { |     static var previews: some View { | ||||||
|         RouletteSettingsOptionView( |         RouletteSettingsOptionView( | ||||||
|             option: RouletteOption(title: "옵션1", weight: 1), |             option: RouletteOption(title: "옵션1", percentage: ""), | ||||||
|             index: 2, |             index: 2, | ||||||
|             onClickPlus: {}, |             onClickDelete: {} | ||||||
|             onClickDelete: {}, |  | ||||||
|             onClickSubstract: {} |  | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -128,9 +128,7 @@ struct RouletteSettingsView: View { | |||||||
|                                     RouletteSettingsOptionView( |                                     RouletteSettingsOptionView( | ||||||
|                                         option: viewModel.options[index], |                                         option: viewModel.options[index], | ||||||
|                                         index: index, |                                         index: index, | ||||||
|                                         onClickPlus: { viewModel.plusWeight(index: index) }, |                                         onClickDelete: { viewModel.deleteOption(index: index) } | ||||||
|                                         onClickDelete: { viewModel.deleteOption(index: index) }, |  | ||||||
|                                         onClickSubstract: { viewModel.subtractWeight(index: index) } |  | ||||||
|                                     ) |                                     ) | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
| @@ -190,7 +188,7 @@ struct RouletteSettingsView: View { | |||||||
|                         isShowing: $viewModel.isShowPreview, |                         isShowing: $viewModel.isShowPreview, | ||||||
|                         title: "룰렛 미리보기", |                         title: "룰렛 미리보기", | ||||||
|                         onClickSpin: nil, |                         onClickSpin: nil, | ||||||
|                         preview: preview |                         previewList: [preview] | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -46,47 +46,15 @@ final class RouletteSettingsViewModel: ObservableObject { | |||||||
|     private var rouletteId = 0 |     private var rouletteId = 0 | ||||||
|     @Published var rouletteList = [GetNewRouletteResponse]() |     @Published var rouletteList = [GetNewRouletteResponse]() | ||||||
|      |      | ||||||
|     func plusWeight(index: Int) { |  | ||||||
|         options[index].weight += 1 |  | ||||||
|         recalculatePercentages() |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     func subtractWeight(index: Int) { |  | ||||||
|         if options[index].weight > 1 { |  | ||||||
|             options[index].weight -= 1 |  | ||||||
|             recalculatePercentages() |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     func addOption() { |     func addOption() { | ||||||
|         if (options.count >= 10) { |         if (options.count >= 10) { | ||||||
|             return |             return | ||||||
|         } |         } | ||||||
|         options.append(RouletteOption(title: "", weight: 1)) |         options.append(RouletteOption(title: "", percentage: "")) | ||||||
|         recalculatePercentages() |  | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     func deleteOption(index: Int) { |     func deleteOption(index: Int) { | ||||||
|         options.remove(at: index) |         options.remove(at: index) | ||||||
|         recalculatePercentages() |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     private func recalculatePercentages() { |  | ||||||
|         let options = options |  | ||||||
|          |  | ||||||
|         var totalWeight = 0 |  | ||||||
|         for option in options { |  | ||||||
|             totalWeight += option.weight |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         guard totalWeight > 0 else { return } |  | ||||||
|          |  | ||||||
|         for i in 0..<options.count { |  | ||||||
|             let percent = floor(Double(options[i].weight) / Double(totalWeight) * 10000) / 100 |  | ||||||
|             options[i].percentage = String(format: "%.2f", percent) |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         removeAllAndAddOptions(options: options) |  | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     private func removeAllAndAddOptions(options: [RouletteOption]) { |     private func removeAllAndAddOptions(options: [RouletteOption]) { | ||||||
| @@ -143,7 +111,7 @@ final class RouletteSettingsViewModel: ObservableObject { | |||||||
|             items.append(RoulettePreviewItem(title: option.title, percent: "\(option.percentage)%")) |             items.append(RoulettePreviewItem(title: option.title, percent: "\(option.percentage)%")) | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         previewData = RoulettePreview(can: self.can, items: items) |         previewData = RoulettePreview(id: 0, can: self.can, items: items) | ||||||
|         isLoading = false |         isLoading = false | ||||||
|         isShowPreview = true |         isShowPreview = true | ||||||
|     } |     } | ||||||
| @@ -152,25 +120,48 @@ final class RouletteSettingsViewModel: ObservableObject { | |||||||
|         if !isLoading { |         if !isLoading { | ||||||
|             isLoading = true |             isLoading = true | ||||||
|              |              | ||||||
|             if rouletteId > 0 { |             if validationOptions() { | ||||||
|                 updateRoulette(onSuccess: onSuccess) |                 if rouletteId > 0 { | ||||||
|             } else { |                     updateRoulette(onSuccess: onSuccess) | ||||||
|                 createRoulette(onSuccess: onSuccess) |                 } else { | ||||||
|  |                     createRoulette(onSuccess: onSuccess) | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     private func createRoulette(onSuccess: @escaping (Bool, String) -> Void) { |     private func validationOptions() -> Bool { | ||||||
|         var items = [RouletteItem]() |         var totalPercentage = Float(0) | ||||||
|  |          | ||||||
|         for option in options { |         for option in options { | ||||||
|             if option.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { |             if option.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { | ||||||
|                 isLoading = false |                 isLoading = false | ||||||
|                 errorMessage = "옵션은 빈칸일 수 없습니다." |                 errorMessage = "옵션은 빈칸일 수 없습니다." | ||||||
|                 isShowErrorPopup = true |                 isShowErrorPopup = true | ||||||
|                 return |                 return false | ||||||
|             } |             } | ||||||
|              |              | ||||||
|             items.append(RouletteItem(title: option.title, weight: option.weight)) |             if let percentage = Float(option.percentage) { | ||||||
|  |                 totalPercentage += percentage | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if totalPercentage != Float(100) { | ||||||
|  |             isLoading = false | ||||||
|  |             errorMessage = "확률이 100%가 아닙니다" | ||||||
|  |             isShowErrorPopup = true | ||||||
|  |             return false | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         return true | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     private func createRoulette(onSuccess: @escaping (Bool, String) -> Void) { | ||||||
|  |         var items = [RouletteItem]() | ||||||
|  |         for option in options { | ||||||
|  |             if let percentage = Float(option.percentage) { | ||||||
|  |                 items.append(RouletteItem(title: option.title, percentage: percentage)) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         let selectedRouletteTitle: String |         let selectedRouletteTitle: String | ||||||
| @@ -227,14 +218,9 @@ final class RouletteSettingsViewModel: ObservableObject { | |||||||
|     private func updateRoulette(onSuccess: @escaping (Bool, String) -> Void) { |     private func updateRoulette(onSuccess: @escaping (Bool, String) -> Void) { | ||||||
|         var items = [RouletteItem]() |         var items = [RouletteItem]() | ||||||
|         for option in options { |         for option in options { | ||||||
|             if option.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { |             if let percentage = Float(option.percentage) { | ||||||
|                 isLoading = false |                 items.append(RouletteItem(title: option.title, percentage: percentage)) | ||||||
|                 errorMessage = "옵션은 빈칸일 수 없습니다." |  | ||||||
|                 isShowErrorPopup = true |  | ||||||
|                 return |  | ||||||
|             } |             } | ||||||
|              |  | ||||||
|             items.append(RouletteItem(title: option.title, weight: option.weight)) |  | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         let selectedRoulette = rouletteList[selectedRoulette!.rawValue] |         let selectedRoulette = rouletteList[selectedRoulette!.rawValue] | ||||||
| @@ -259,24 +245,10 @@ final class RouletteSettingsViewModel: ObservableObject { | |||||||
|             selectedRouletteTitle = "룰렛 1" |             selectedRouletteTitle = "룰렛 1" | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         var isAllActive = false |  | ||||||
|          |  | ||||||
|         rouletteList |  | ||||||
|             .filter { |  | ||||||
|                 $0.id != selectedRoulette.id |  | ||||||
|             } |  | ||||||
|             .forEach { |  | ||||||
|                 if $0.isActive { |  | ||||||
|                     isAllActive = true |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|          |  | ||||||
|         if isActive { |         if isActive { | ||||||
|             successMessage = "\(selectedRouletteTitle)로 설정하였습니다." |             successMessage = "\(selectedRouletteTitle)을 활성화 했습니다." | ||||||
|         } else if !isAllActive { |  | ||||||
|             successMessage = "\(selectedRouletteTitle)이 비활성화 되었습니다." |  | ||||||
|         } else { |         } else { | ||||||
|             successMessage = "\(selectedRouletteTitle)을 설정했습니다." |             successMessage = "\(selectedRouletteTitle)을 비활성화 했습니다." | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         let request = UpdateRouletteRequest(id: rouletteId, can: can, isActive: isActive, items: items) |         let request = UpdateRouletteRequest(id: rouletteId, can: can, isActive: isActive, items: items) | ||||||
| @@ -337,10 +309,9 @@ final class RouletteSettingsViewModel: ObservableObject { | |||||||
|                 self.rouletteId = roulette.id |                 self.rouletteId = roulette.id | ||||||
|                 self.isActive = roulette.isActive |                 self.isActive = roulette.isActive | ||||||
|                 let options = roulette.items.map { |                 let options = roulette.items.map { | ||||||
|                     RouletteOption(title: $0.title, weight: $0.weight) |                     RouletteOption(title: $0.title, percentage: String($0.percentage)) | ||||||
|                 } |                 } | ||||||
|                 removeAllAndAddOptions(options: options) |                 removeAllAndAddOptions(options: options) | ||||||
|                 recalculatePercentages() |  | ||||||
|             } else { |             } else { | ||||||
|                 self.canText = "" |                 self.canText = "" | ||||||
|                 self.isActive = false |                 self.isActive = false | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
| // | // | ||||||
|  |  | ||||||
| struct GetRouletteResponse: Decodable { | struct GetRouletteResponse: Decodable { | ||||||
|  |     let id: Int | ||||||
|     let can: Int |     let can: Int | ||||||
|     let isActive: Bool |     let isActive: Bool | ||||||
|     let items: [RouletteItem] |     let items: [RouletteItem] | ||||||
| @@ -13,5 +14,5 @@ struct GetRouletteResponse: Decodable { | |||||||
|  |  | ||||||
| struct RouletteItem: Codable, Equatable { | struct RouletteItem: Codable, Equatable { | ||||||
|     let title: String |     let title: String | ||||||
|     let weight: Int |     let percentage: Float | ||||||
| } | } | ||||||
|   | |||||||
| @@ -25,16 +25,16 @@ extension RouletteApi: TargetType { | |||||||
|     var path: String { |     var path: String { | ||||||
|         switch self { |         switch self { | ||||||
|         case .getRoulette, .createRoulette, .updateRoulette: |         case .getRoulette, .createRoulette, .updateRoulette: | ||||||
|             return "/new-roulette" |             return "/v2/roulette" | ||||||
|              |              | ||||||
|         case .getAllRoulette: |         case .getAllRoulette: | ||||||
|             return "/new-roulette/creator" |             return "/v2/roulette/creator" | ||||||
|              |              | ||||||
|         case .spinRoulette: |         case .spinRoulette: | ||||||
|             return "/new-roulette/spin" |             return "/v2/roulette/spin" | ||||||
|              |              | ||||||
|         case .refundRouletteDonation(let roomId): |         case .refundRouletteDonation(let roomId): | ||||||
|             return "/new-roulette/refund/\(roomId)" |             return "/v2/roulette/refund/\(roomId)" | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
| import Foundation | import Foundation | ||||||
|  |  | ||||||
| struct RoulettePreview { | struct RoulettePreview { | ||||||
|  |     let id: Int | ||||||
|     let can: Int |     let can: Int | ||||||
|     let items: [RoulettePreviewItem] |     let items: [RoulettePreviewItem] | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,13 +12,61 @@ struct RoulettePreviewDialog: View { | |||||||
|     @Binding var isShowing: Bool |     @Binding var isShowing: Bool | ||||||
|      |      | ||||||
|     let title: String? |     let title: String? | ||||||
|     let onClickSpin: (() -> Void)? |     let onClickSpin: ((Int) -> Void)? | ||||||
|     let preview: RoulettePreview |     let previewList: [RoulettePreview] | ||||||
|  |      | ||||||
|  |     @State var selectedRoulette = SelectedRoulette.ROULETTE_1 | ||||||
|      |      | ||||||
|     var body: some View { |     var body: some View { | ||||||
|         GeometryReader { geo in |         GeometryReader { geo in | ||||||
|             ZStack { |             ZStack { | ||||||
|                 VStack(spacing: 0) { |                 VStack(spacing: 16.7) { | ||||||
|  |                     if previewList.count > 1 { | ||||||
|  |                         HStack(spacing: 13.3) { | ||||||
|  |                             SelectedButtonView( | ||||||
|  |                                 title: "룰렛 1", | ||||||
|  |                                 isActive: true, | ||||||
|  |                                 isSelected: selectedRoulette == .ROULETTE_1 | ||||||
|  |                             ) | ||||||
|  |                             .onTapGesture { | ||||||
|  |                                 if selectedRoulette != .ROULETTE_1 { | ||||||
|  |                                     selectedRoulette = .ROULETTE_1 | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                              | ||||||
|  |                             SelectedButtonView( | ||||||
|  |                                 title: "룰렛 2", | ||||||
|  |                                 isActive: true, | ||||||
|  |                                 isSelected: selectedRoulette == .ROULETTE_2, | ||||||
|  |                                 checkImage: "ic_select_check_black", | ||||||
|  |                                 bgSelectedColor: Color(hex: "ffcb14"), | ||||||
|  |                                 textSelectedColor: Color.black, | ||||||
|  |                                 textDefaultColor: Color(hex: "ffcb14") | ||||||
|  |                             ) | ||||||
|  |                             .onTapGesture { | ||||||
|  |                                 if selectedRoulette != .ROULETTE_2 { | ||||||
|  |                                     selectedRoulette = .ROULETTE_2 | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                              | ||||||
|  |                             if previewList.count > 2 { | ||||||
|  |                                 SelectedButtonView( | ||||||
|  |                                     title: "룰렛 3", | ||||||
|  |                                     isActive: true, | ||||||
|  |                                     isSelected: selectedRoulette == .ROULETTE_3, | ||||||
|  |                                     bgSelectedColor: Color(hex: "ff14d9"), | ||||||
|  |                                     textDefaultColor: Color(hex: "ff14d9") | ||||||
|  |                                 ) | ||||||
|  |                                 .onTapGesture { | ||||||
|  |                                     if selectedRoulette != .ROULETTE_3 { | ||||||
|  |                                         selectedRoulette = .ROULETTE_3 | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         .padding(.top, 16.7) | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|                     HStack(spacing: 0) { |                     HStack(spacing: 0) { | ||||||
|                         Text(title ?? "룰렛") |                         Text(title ?? "룰렛") | ||||||
|                             .font(.custom(Font.bold.rawValue, size: 18.3)) |                             .font(.custom(Font.bold.rawValue, size: 18.3)) | ||||||
| @@ -42,54 +90,66 @@ struct RoulettePreviewDialog: View { | |||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     .padding(.top, 16.7) |                     .padding(.top, previewList.count > 1 ? 0 : 16.7) | ||||||
|                     .padding(.horizontal, 13.3) |                     .padding(.horizontal, 13.3) | ||||||
|                      |                      | ||||||
|                     LazyVStack(alignment: .leading, spacing: 13.3) { |                     LazyVStack(alignment: .leading, spacing: 13.3) { | ||||||
|                         ForEach(preview.items.indices, id: \.self) { index in |                         ForEach(previewList[selectedRoulette.rawValue].items.indices, id: \.self) { index in | ||||||
|                             HStack(spacing:13.3) { |                             HStack(spacing:13.3) { | ||||||
|                                 Text("\(index + 1)") |                                 Text("\(index + 1)") | ||||||
|                                     .font(.custom(Font.bold.rawValue, size: 14.7)) |                                     .font(.custom(Font.bold.rawValue, size: 14.7)) | ||||||
|                                     .foregroundColor(Color(hex: "e2e2e2")) |                                     .foregroundColor(Color(hex: "e2e2e2")) | ||||||
|                                  |                                  | ||||||
|                                 Text("\(preview.items[index].title) (\(preview.items[index].percent))") |                                 Text("\(previewList[selectedRoulette.rawValue].items[index].title) (\(previewList[selectedRoulette.rawValue].items[index].percent))") | ||||||
|                                     .font(.custom(Font.medium.rawValue, size: 14.7)) |                                     .font(.custom(Font.medium.rawValue, size: 14.7)) | ||||||
|                                     .foregroundColor(Color(hex: "e2e2e2")) |                                     .foregroundColor(Color(hex: "e2e2e2")) | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     .padding(.top, 13.3) |  | ||||||
|                     .padding(.horizontal, 13.3) |                     .padding(.horizontal, 13.3) | ||||||
|                      |                      | ||||||
|                     HStack(spacing: 13.3) { |                     HStack(spacing: 13.3) { | ||||||
|                         Text("취소") |                         Text("취소") | ||||||
|                             .font(.custom(Font.bold.rawValue, size: 16)) |                             .font(.custom(Font.bold.rawValue, size: 16)) | ||||||
|                             .foregroundColor(Color(hex: "3bb9f1")) |                             .foregroundColor( | ||||||
|  |                                 selectedRoulette == .ROULETTE_2 ? Color(hex: "ffcb14") : | ||||||
|  |                                     selectedRoulette == .ROULETTE_3 ? Color(hex: "ff14d9") : | ||||||
|  |                                     Color.button | ||||||
|  |                             ) | ||||||
|                             .padding(.horizontal, 18) |                             .padding(.horizontal, 18) | ||||||
|                             .padding(.vertical, 16) |                             .padding(.vertical, 16) | ||||||
|                             .overlay( |                             .overlay( | ||||||
|                                 RoundedRectangle(cornerRadius: 10) |                                 RoundedRectangle(cornerRadius: 10) | ||||||
|                                     .stroke(Color(hex: "3bb9f1"), lineWidth: 1) |                                     .stroke( | ||||||
|  |                                         selectedRoulette == .ROULETTE_2 ? Color(hex: "ffcb14") : | ||||||
|  |                                             selectedRoulette == .ROULETTE_3 ? Color(hex: "ff14d9") : | ||||||
|  |                                             Color.button, | ||||||
|  |                                         lineWidth: 1 | ||||||
|  |                                     ) | ||||||
|                             ) |                             ) | ||||||
|                             .onTapGesture { |                             .onTapGesture { | ||||||
|                                 isShowing = false |                                 isShowing = false | ||||||
|                             } |                             } | ||||||
|                          |                          | ||||||
|                         Text("\(preview.can)캔으로 룰렛 돌리기") |                         Text("\(previewList[selectedRoulette.rawValue].can)캔으로 룰렛 돌리기") | ||||||
|                             .font(.custom(Font.bold.rawValue, size: 16)) |                             .font(.custom(Font.bold.rawValue, size: 16)) | ||||||
|                             .foregroundColor(.white) |                             .foregroundColor(selectedRoulette == .ROULETTE_2 ? .black : .white) | ||||||
|                             .padding(.vertical, 16) |                             .padding(.vertical, 16) | ||||||
|                             .frame(maxWidth: .infinity) |                             .frame(maxWidth: .infinity) | ||||||
|                             .background(Color(hex: "3bb9f1")) |                             .background( | ||||||
|  |                                 selectedRoulette == .ROULETTE_2 ? Color(hex: "ffcb14") : | ||||||
|  |                                     selectedRoulette == .ROULETTE_3 ? Color(hex: "ff14d9") : | ||||||
|  |                                     Color.button | ||||||
|  |                             ) | ||||||
|                             .cornerRadius(10) |                             .cornerRadius(10) | ||||||
|                             .onTapGesture { |                             .onTapGesture { | ||||||
|                                 if let onClickSpin = onClickSpin { |                                 if let onClickSpin = onClickSpin { | ||||||
|                                     onClickSpin() |                                     onClickSpin(previewList[selectedRoulette.rawValue].id) | ||||||
|                                 } |                                 } | ||||||
|                                 isShowing = false |                                 isShowing = false | ||||||
|                             } |                             } | ||||||
|                     } |                     } | ||||||
|                     .padding(.top, 26.7) |                     .padding(.top, 6.7) | ||||||
|                 } |                 } | ||||||
|                 .padding(13.3) |                 .padding(13.3) | ||||||
|                 .background(Color(hex: "222222")) |                 .background(Color(hex: "222222")) | ||||||
| @@ -108,13 +168,32 @@ struct RoulettePreviewDialog_Previews: PreviewProvider { | |||||||
|             isShowing: .constant(true), |             isShowing: .constant(true), | ||||||
|             title: nil, |             title: nil, | ||||||
|             onClickSpin: nil, |             onClickSpin: nil, | ||||||
|             preview: RoulettePreview( |             previewList: [ | ||||||
|                 can: 100, |                 RoulettePreview( | ||||||
|                 items: [ |                     id: 0, | ||||||
|                     RoulettePreviewItem(title: "옵션1", percent: "10%"), |                     can: 100, | ||||||
|                     RoulettePreviewItem(title: "옵션2", percent: "90%"), |                     items: [ | ||||||
|                 ] |                         RoulettePreviewItem(title: "옵션1", percent: "33.40%"), | ||||||
|             ) |                         RoulettePreviewItem(title: "옵션2", percent: "66.60%"), | ||||||
|  |                     ] | ||||||
|  |                 ), | ||||||
|  |                 RoulettePreview( | ||||||
|  |                     id: 1, | ||||||
|  |                     can: 10, | ||||||
|  |                     items: [ | ||||||
|  |                         RoulettePreviewItem(title: "옵션3", percent: "10.8%"), | ||||||
|  |                         RoulettePreviewItem(title: "옵션4", percent: "89.2%"), | ||||||
|  |                     ] | ||||||
|  |                 ), | ||||||
|  |                 RoulettePreview( | ||||||
|  |                     id: 2, | ||||||
|  |                     can: 1000, | ||||||
|  |                     items: [ | ||||||
|  |                         RoulettePreviewItem(title: "옵션5", percent: "10%"), | ||||||
|  |                         RoulettePreviewItem(title: "옵션6", percent: "90%"), | ||||||
|  |                     ] | ||||||
|  |                 ) | ||||||
|  |             ] | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,5 +9,6 @@ import Foundation | |||||||
|  |  | ||||||
| struct SpinRouletteRequest: Encodable { | struct SpinRouletteRequest: Encodable { | ||||||
|     let roomId: Int |     let roomId: Int | ||||||
|  |     let rouletteId: Int | ||||||
|     let container: String = "ios" |     let container: String = "ios" | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | // | ||||||
|  | //  SpinRouletteResponse.swift | ||||||
|  | //  SodaLive | ||||||
|  | // | ||||||
|  | //  Created by klaus on 5/11/24. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | struct SpinRouletteResponse: Decodable { | ||||||
|  |     let can: Int | ||||||
|  |     let result: String | ||||||
|  |     let items: [RouletteItem] | ||||||
|  | } | ||||||
| @@ -168,76 +168,46 @@ struct LiveRoomViewV2: View { | |||||||
|                                 .padding(.top, 16) |                                 .padding(.top, 16) | ||||||
|                                  |                                  | ||||||
|                                 VStack(alignment: .trailing, spacing: 0) { |                                 VStack(alignment: .trailing, spacing: 0) { | ||||||
|                                     Image(viewModel.isSpeakerMute ? "ic_speaker_off" : "ic_speaker_on") |                                     LiveRoomRightBottomButton( | ||||||
|                                         .resizable() |                                         imageName: viewModel.isSpeakerMute ? "ic_speaker_off" : "ic_speaker_on", | ||||||
|                                         .frame(width: 26.7, height: 26.7) |                                         onClick: { viewModel.toggleSpeakerMute() } | ||||||
|                                         .padding(11) |                                     ) | ||||||
|                                         .background(Color(hex: "525252").opacity(0.6)) |                                     .padding(.top, 26.7) | ||||||
|                                         .cornerRadius(10) |                                     .padding(.trailing, 13.3) | ||||||
|                                         .onTapGesture { |  | ||||||
|                                             viewModel.toggleSpeakerMute() |  | ||||||
|                                         } |  | ||||||
|                                         .padding(.top, 26.7) |  | ||||||
|                                         .padding(.trailing, 13.3) |  | ||||||
|                                      |                                      | ||||||
|                                     Spacer() |                                     Spacer() | ||||||
|                                      |                                      | ||||||
|                                     VStack(spacing: 13.3) { |                                     VStack(spacing: 13.3) { | ||||||
|                                         if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) { |                                         if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) { | ||||||
|                                             Image("ic_roulette_settings") |                                             LiveRoomRightBottomButton( | ||||||
|                                                 .resizable() |                                                 imageName: "ic_roulette_settings", | ||||||
|                                                 .frame(width: 26.7, height: 26.7) |                                                 onClick: { viewModel.isShowRouletteSettings = true } | ||||||
|                                                 .padding(11) |                                             ) | ||||||
|                                                 .background(Color(hex: "525252").opacity(0.6)) |  | ||||||
|                                                 .cornerRadius(10) |  | ||||||
|                                                 .onTapGesture { |  | ||||||
|                                                     viewModel.isShowRouletteSettings = true |  | ||||||
|                                                 } |  | ||||||
|                                         } else if liveRoomInfo.creatorId != UserDefaults.int(forKey: .userId) && viewModel.isActiveRoulette { |                                         } else if liveRoomInfo.creatorId != UserDefaults.int(forKey: .userId) && viewModel.isActiveRoulette { | ||||||
|                                             Image("ic_roulette") |                                             LiveRoomRightBottomButton( | ||||||
|                                                 .resizable() |                                                 imageName: "ic_roulette", | ||||||
|                                                 .frame(width: 26.7, height: 26.7) |                                                 onClick: { viewModel.showRoulette() } | ||||||
|                                                 .padding(11) |                                             ) | ||||||
|                                                 .background(Color(hex: "525252").opacity(0.6)) |  | ||||||
|                                                 .cornerRadius(10) |  | ||||||
|                                                 .onTapGesture { |  | ||||||
|                                                     viewModel.showRoulette() |  | ||||||
|                                                 } |  | ||||||
|                                         } |                                         } | ||||||
|                                          |                                          | ||||||
|                                         if viewModel.role == .SPEAKER { |                                         if viewModel.role == .SPEAKER { | ||||||
|                                             Image(viewModel.isMute ? "ic_mic_off" : "ic_mic_on") |                                             LiveRoomRightBottomButton( | ||||||
|                                                 .resizable() |                                                 imageName: viewModel.isMute ? "ic_mic_off" : "ic_mic_on", | ||||||
|                                                 .frame(width: 26.7, height: 26.7) |                                                 onClick: { viewModel.toggleMute() } | ||||||
|                                                 .padding(11) |                                             ) | ||||||
|                                                 .background(Color(hex: "525252").opacity(0.6)) |  | ||||||
|                                                 .cornerRadius(10) |  | ||||||
|                                                 .onTapGesture { |  | ||||||
|                                                     viewModel.toggleMute() |  | ||||||
|                                                 } |  | ||||||
|                                         } |                                         } | ||||||
|                                          |                                          | ||||||
|                                         if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) && |                                         if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) && | ||||||
|                                             UserDefaults.string(forKey: .role) == MemberRole.CREATOR.rawValue { |                                             UserDefaults.string(forKey: .role) == MemberRole.CREATOR.rawValue { | ||||||
|                                             Image("ic_donation_message_list") |                                             LiveRoomRightBottomButton( | ||||||
|                                                 .resizable() |                                                 imageName: "ic_donation_message_list", | ||||||
|                                                 .frame(width: 26.7, height: 26.7) |                                                 onClick: { viewModel.isShowDonationMessagePopup = true } | ||||||
|                                                 .padding(11) |                                             ) | ||||||
|                                                 .background(Color(hex: "525252").opacity(0.6)) |  | ||||||
|                                                 .cornerRadius(10) |  | ||||||
|                                                 .onTapGesture { |  | ||||||
|                                                     viewModel.isShowDonationMessagePopup = true |  | ||||||
|                                                 } |  | ||||||
|                                         } else { |                                         } else { | ||||||
|                                             Image("ic_donation") |                                             LiveRoomRightBottomButton( | ||||||
|                                                 .resizable() |                                                 imageName: "ic_donation", | ||||||
|                                                 .frame(width: 26.7, height: 26.7) |                                                 onClick: { viewModel.isShowDonationPopup = true } | ||||||
|                                                 .padding(11) |                                             ) | ||||||
|                                                 .background(Color(hex: "525252").opacity(0.6)) |  | ||||||
|                                                 .cornerRadius(10) |  | ||||||
|                                                 .onTapGesture { |  | ||||||
|                                                     viewModel.isShowDonationPopup = true |  | ||||||
|                                                 } |  | ||||||
|                                         } |                                         } | ||||||
|                                     } |                                     } | ||||||
|                                     .padding(.trailing, 13.3) |                                     .padding(.trailing, 13.3) | ||||||
| @@ -629,12 +599,12 @@ struct LiveRoomViewV2: View { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|              |              | ||||||
|             if let preview = viewModel.roulettePreview, viewModel.isShowRoulettePreview { |             if !viewModel.roulettePreviewList.isEmpty && viewModel.isShowRoulettePreview { | ||||||
|                 RoulettePreviewDialog( |                 RoulettePreviewDialog( | ||||||
|                     isShowing: $viewModel.isShowRoulettePreview, |                     isShowing: $viewModel.isShowRoulettePreview, | ||||||
|                     title: nil, |                     title: nil, | ||||||
|                     onClickSpin: { viewModel.spinRoulette() }, |                     onClickSpin: { viewModel.spinRoulette(rouletteId: $0) }, | ||||||
|                     preview: preview |                     previewList: viewModel.roulettePreviewList | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|              |              | ||||||
|   | |||||||
| @@ -13,23 +13,51 @@ struct SelectedButtonView: View { | |||||||
|     let isActive: Bool |     let isActive: Bool | ||||||
|     let isSelected: Bool |     let isSelected: Bool | ||||||
|      |      | ||||||
|  |     var checkImage = "ic_select_check" | ||||||
|  |     var bgDisabledColor = Color.gray55 | ||||||
|  |     var bgSelectedColor = Color.button | ||||||
|  |     var bgDefaultColor = Color.bg | ||||||
|  |     var textDisabledColor = Color.gray77 | ||||||
|  |     var textSelectedColor = Color.white | ||||||
|  |     var textDefaultColor = Color.button | ||||||
|  |      | ||||||
|     var body: some View { |     var body: some View { | ||||||
|         HStack(spacing: 6.7) { |         HStack(spacing: 6.7) { | ||||||
|             if isSelected { |             if isSelected { | ||||||
|                 Image("ic_select_check") |                 Image(checkImage) | ||||||
|             } |             } | ||||||
|              |              | ||||||
|             Text(title) |             Text(title) | ||||||
|                 .font(.custom(Font.bold.rawValue, size: 14.7)) |                 .font(.custom(Font.bold.rawValue, size: 14.7)) | ||||||
|                 .foregroundColor(!isActive ? Color.gray77 : isSelected ? .white : Color.button) |                 .foregroundColor(!isActive ? textDisabledColor : isSelected ? textSelectedColor : textDefaultColor) | ||||||
|         } |         } | ||||||
|         .padding(.vertical, 14.3) |         .padding(.vertical, isSelected ? 14.3 : 17) | ||||||
|         .frame(maxWidth: .infinity) |         .frame(maxWidth: .infinity) | ||||||
|         .background(!isActive ? Color.gray55 : isSelected ? Color.button : Color.bg) |         .background(!isActive ? bgDisabledColor : isSelected ? bgSelectedColor : bgDefaultColor) | ||||||
|         .cornerRadius(6.7) |         .cornerRadius(6.7) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #Preview { | #Preview("기본") { | ||||||
|     SelectedButtonView(title: "테스트", isActive: true, isSelected: true) |     SelectedButtonView(title: "테스트", isActive: true, isSelected: true) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #Preview("이미지와 컬러 수정 - selected") { | ||||||
|  |     SelectedButtonView( | ||||||
|  |         title: "테스트", | ||||||
|  |         isActive: true, | ||||||
|  |         isSelected: true, | ||||||
|  |         bgSelectedColor: Color(hex: "ff14d9"), | ||||||
|  |         textDefaultColor: Color(hex: "ff14d9") | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #Preview("이미지와 컬러 수정 - unselected") { | ||||||
|  |     SelectedButtonView( | ||||||
|  |         title: "테스트", | ||||||
|  |         isActive: true, | ||||||
|  |         isSelected: false, | ||||||
|  |         textDefaultColor: Color(hex: "ff14d9") | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Yu Sung
					Yu Sung