Compare commits
No commits in common. "3c6ac1c0607fc2a36702e6647c0555ab47fb35a8" and "27b87c9cbe36342dc665ff2177d11bb9597b3cac" have entirely different histories.
3c6ac1c060
...
27b87c9cbe
|
@ -36,22 +36,13 @@
|
|||
"version" : "5.7.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "app-check",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/app-check.git",
|
||||
"state" : {
|
||||
"revision" : "3e464dad87dad2d29bb29a97836789bf0f8f67d2",
|
||||
"version" : "10.18.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "firebase-ios-sdk",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/firebase-ios-sdk.git",
|
||||
"state" : {
|
||||
"revision" : "be49849dcba96f2b5ee550d4eceb2c0fa27dade4",
|
||||
"version" : "10.22.1"
|
||||
"revision" : "df2171b0c6afb9e9d4f7e07669d558c510b9f6be",
|
||||
"version" : "10.13.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -59,8 +50,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleAppMeasurement.git",
|
||||
"state" : {
|
||||
"revision" : "482cfa4e5880f0a29f66ecfd60c5a62af28bd1f0",
|
||||
"version" : "10.22.1"
|
||||
"revision" : "03b9beee1a61f62d32c521e172e192a1663a5e8b",
|
||||
"version" : "10.13.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -68,8 +59,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleDataTransport.git",
|
||||
"state" : {
|
||||
"revision" : "a637d318ae7ae246b02d7305121275bc75ed5565",
|
||||
"version" : "9.4.0"
|
||||
"revision" : "aae45a320fd0d11811820335b1eabc8753902a40",
|
||||
"version" : "9.2.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -77,8 +68,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleUtilities.git",
|
||||
"state" : {
|
||||
"revision" : "26c898aed8bed13b8a63057ee26500abbbcb8d55",
|
||||
"version" : "7.13.1"
|
||||
"revision" : "c38ce365d77b04a9a300c31061c5227589e5597b",
|
||||
"version" : "7.11.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -86,8 +77,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/grpc-binary.git",
|
||||
"state" : {
|
||||
"revision" : "a673bc2937fbe886dd1f99c401b01b6d977a9c98",
|
||||
"version" : "1.49.1"
|
||||
"revision" : "f1b366129d1125be7db83247e003fc333104b569",
|
||||
"version" : "1.50.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -99,15 +90,6 @@
|
|||
"version" : "3.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "interop-ios-for-google-sdks",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
|
||||
"state" : {
|
||||
"revision" : "2d12673670417654f08f5f90fdd62926dc3a2648",
|
||||
"version" : "100.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "kingfisher",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.5 KiB |
|
@ -9,7 +9,7 @@
|
|||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "btn_following_big.png",
|
||||
"filename" : "btn_notification.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
BIN
SodaLive/Resources/Assets.xcassets/btn_notification.imageset/btn_notification.png
vendored
Normal file
After Width: | Height: | Size: 4.6 KiB |
21
SodaLive/Resources/Assets.xcassets/btn_notification_selected.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "btn_notification_selected.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
SodaLive/Resources/Assets.xcassets/btn_notification_selected.imageset/btn_notification_selected.png
vendored
Normal file
After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 563 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 772 KiB |
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 554 KiB |
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 320 KiB |
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 617 KiB |
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 678 KiB |
|
@ -9,7 +9,7 @@
|
|||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ic_can_white.png",
|
||||
"filename" : "img_guide_5.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
BIN
SodaLive/Resources/Assets.xcassets/onboarding/img_guide_5.imageset/img_guide_5.png
vendored
Normal file
After Width: | Height: | Size: 531 KiB |
|
@ -9,7 +9,7 @@
|
|||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "btn_follow_big.png",
|
||||
"filename" : "img_guide_6.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
BIN
SodaLive/Resources/Assets.xcassets/onboarding/img_guide_6.imageset/img_guide_6.png
vendored
Normal file
After Width: | Height: | Size: 252 KiB |
|
@ -9,7 +9,7 @@
|
|||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "img_bg_short_play.png",
|
||||
"filename" : "splash_bg_2024.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
BIN
SodaLive/Resources/Assets.xcassets/splash/splash_bg_2024.imageset/splash_bg_2024.jpg
vendored
Normal file
After Width: | Height: | Size: 349 KiB |
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "splash_bg_2024_03.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 3.1 MiB |
21
SodaLive/Resources/Assets.xcassets/splash/splash_bubble.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "splash_bubble.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
SodaLive/Resources/Assets.xcassets/splash/splash_bubble.imageset/splash_bubble.png
vendored
Normal file
After Width: | Height: | Size: 11 KiB |
21
SodaLive/Resources/Assets.xcassets/splash/splash_logo.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "splash_logo.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
SodaLive/Resources/Assets.xcassets/splash/splash_logo.imageset/splash_logo.png
vendored
Normal file
After Width: | Height: | Size: 55 KiB |
21
SodaLive/Resources/Assets.xcassets/splash/splash_logo_2024.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "splash_logo_2024.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
SodaLive/Resources/Assets.xcassets/splash/splash_logo_2024.imageset/splash_logo_2024.png
vendored
Normal file
After Width: | Height: | Size: 40 KiB |
|
@ -9,7 +9,7 @@
|
|||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "img_bg_review_live.png",
|
||||
"filename" : "splash_logo_dragon_2024.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
After Width: | Height: | Size: 82 KiB |
21
SodaLive/Resources/Assets.xcassets/splash/splash_text.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "splash_text.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
SodaLive/Resources/Assets.xcassets/splash/splash_text.imageset/splash_text.png
vendored
Normal file
After Width: | Height: | Size: 15 KiB |
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "splash_text_2024_03.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 29 KiB |
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "splash_text_logo_2024_03.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 4.5 KiB |
21
SodaLive/Resources/Assets.xcassets/splash/vividnext_logo.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "vividnext_logo.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
SodaLive/Resources/Assets.xcassets/splash/vividnext_logo.imageset/vividnext_logo.png
vendored
Normal file
After Width: | Height: | Size: 6.6 KiB |
|
@ -1,41 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>CA92.1</string>
|
||||
</array>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>E174.1</string>
|
||||
</array>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
|
@ -89,7 +89,7 @@ struct ContentAllByThemeView: View {
|
|||
.padding(.horizontal, 20)
|
||||
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
LazyVGrid(columns: columns, spacing: 32) {
|
||||
LazyVGrid(columns: columns, spacing: 13.3) {
|
||||
ForEach(0..<viewModel.contentList.count, id: \.self) { index in
|
||||
ContentNewAllItemView(item: viewModel.contentList[index])
|
||||
.onAppear {
|
||||
|
|
|
@ -35,11 +35,11 @@ struct ContentNewAllItemView: View {
|
|||
Image("ic_card_can_gray")
|
||||
|
||||
Text("\(item.price)")
|
||||
.font(.custom(Font.medium.rawValue, size: 8.5))
|
||||
.font(.custom(Font.medium.rawValue, size: 8.3))
|
||||
.foregroundColor(Color.white)
|
||||
} else {
|
||||
Text("무료")
|
||||
.font(.custom(Font.medium.rawValue, size: 8.5))
|
||||
.font(.custom(Font.medium.rawValue, size: 8.3))
|
||||
.foregroundColor(Color.white)
|
||||
}
|
||||
}
|
||||
|
@ -52,8 +52,10 @@ struct ContentNewAllItemView: View {
|
|||
Spacer()
|
||||
|
||||
HStack(spacing: 2) {
|
||||
Image("ic_card_time_small_gray")
|
||||
|
||||
Text(item.duration)
|
||||
.font(.custom(Font.medium.rawValue, size: 8.5))
|
||||
.font(.custom(Font.medium.rawValue, size: 8.3))
|
||||
.foregroundColor(Color.white)
|
||||
}
|
||||
.padding(3)
|
||||
|
@ -91,7 +93,7 @@ struct ContentNewAllItemView: View {
|
|||
}
|
||||
.frame(width: width)
|
||||
.onAppear {
|
||||
width = (screenSize().width - 42) / 3
|
||||
width = (screenSize().width - 54) / 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ struct ContentNewAllView: View {
|
|||
.padding(.horizontal, 20)
|
||||
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
LazyVGrid(columns: columns, spacing: 32) {
|
||||
LazyVGrid(columns: columns, spacing: 13.3) {
|
||||
ForEach(0..<viewModel.newContentList.count, id: \.self) { index in
|
||||
ContentNewAllItemView(item: viewModel.newContentList[index])
|
||||
.onAppear {
|
||||
|
|
|
@ -91,7 +91,7 @@ struct ContentCurationView: View {
|
|||
.padding(.horizontal, 20)
|
||||
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
LazyVGrid(columns: columns, spacing: 32) {
|
||||
LazyVGrid(columns: columns, spacing: 13.3) {
|
||||
ForEach(0..<viewModel.contentList.count, id: \.self) { index in
|
||||
ContentNewAllItemView(item: viewModel.contentList[index])
|
||||
.onAppear {
|
||||
|
|
|
@ -33,8 +33,8 @@ struct AudioContentCommentItemView: View {
|
|||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(commentItem.nickname)
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color.gray90)
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Text(commentItem.date)
|
||||
.font(.custom(Font.medium.rawValue, size: 10.3))
|
||||
|
@ -107,15 +107,14 @@ struct AudioContentCommentItemView: View {
|
|||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.strokeBorder(lineWidth: 1)
|
||||
.foregroundColor(Color.button)
|
||||
.foregroundColor(Color(hex: "3bb9f1"))
|
||||
)
|
||||
} else {
|
||||
VStack(alignment: .leading, spacing: 13.3) {
|
||||
Text(commentItem.comment)
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.grayee)
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color(hex: "777777"))
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.lineSpacing(8)
|
||||
.padding(.top, commentItem.donationCan > 0 ? 0 : 13.3)
|
||||
|
||||
if !isReplyComment {
|
||||
|
@ -128,7 +127,7 @@ struct AudioContentCommentItemView: View {
|
|||
) {
|
||||
Text(commentItem.replyCount > 0 ? "답글 \(commentItem.replyCount)개" : "답글 쓰기")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.button)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ struct ContentDetailCommentView: View {
|
|||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color(hex: "bbbbbb"))
|
||||
.lineLimit(1)
|
||||
.lineSpacing(8)
|
||||
.padding(.leading, 3)
|
||||
} else {
|
||||
HStack(spacing: 0) {
|
||||
|
|
|
@ -29,7 +29,7 @@ struct ContentDetailCreatorProfileView: View {
|
|||
Spacer()
|
||||
|
||||
if creator.creatorId != UserDefaults.int(forKey: .userId) {
|
||||
Image(creator.isFollowing ? "btn_following_big" : "btn_follow_big")
|
||||
Image(creator.isFollowing ? "btn_notification_selected" : "btn_notification")
|
||||
.onTapGesture {
|
||||
if creator.isFollowing {
|
||||
onClickUnFollow(creator.creatorId)
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// ContentDetailInfoLimitedEditionView.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 3/27/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ContentDetailInfoLimitedEditionView: View {
|
||||
|
||||
let totalContentCount: Int
|
||||
let remainingContentCount: Int
|
||||
let orderSequence: Int?
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 0) {
|
||||
Text("한정판")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.button)
|
||||
|
||||
Spacer()
|
||||
|
||||
if let orderSequence = orderSequence {
|
||||
Text("\(orderSequence)")
|
||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.button)
|
||||
.padding(.leading, 5.3)
|
||||
|
||||
Text("/")
|
||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.grayd2)
|
||||
.padding(.leading, 2.3)
|
||||
|
||||
Text("\(totalContentCount)")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.grayd2)
|
||||
.padding(.leading, 2.3)
|
||||
} else if (remainingContentCount <= 0) {
|
||||
Text("Sold Out")
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color.grayd2)
|
||||
.padding(.horizontal, 5.3)
|
||||
.padding(.vertical, 3.3)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 2.6)
|
||||
.stroke(lineWidth: 1)
|
||||
.foregroundColor(Color.grayd2)
|
||||
)
|
||||
} else {
|
||||
Text("잔여수량")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.grayd2)
|
||||
|
||||
Text("\(remainingContentCount)")
|
||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.button)
|
||||
.padding(.leading, 5.3)
|
||||
}
|
||||
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 10.3)
|
||||
.background(Color(hex: "14262d"))
|
||||
.cornerRadius(5.3)
|
||||
.padding(.top, 13.3)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentDetailInfoLimitedEditionView(
|
||||
totalContentCount: 10,
|
||||
remainingContentCount: 0,
|
||||
orderSequence: nil
|
||||
)
|
||||
}
|
|
@ -142,14 +142,6 @@ struct ContentDetailInfoView: View {
|
|||
}
|
||||
.padding(.top, 13.3)
|
||||
|
||||
if let totalContentCount = audioContent.totalContentCount, let remainingContentCount = audioContent.remainingContentCount {
|
||||
ContentDetailInfoLimitedEditionView(
|
||||
totalContentCount: totalContentCount,
|
||||
remainingContentCount: remainingContentCount,
|
||||
orderSequence: audioContent.orderSequence
|
||||
)
|
||||
}
|
||||
|
||||
ZStack {
|
||||
VStack(spacing: 8) {
|
||||
if audioContent.tag.count > 0 {
|
||||
|
|
|
@ -35,17 +35,7 @@ struct ContentDetailPlayView: View {
|
|||
)
|
||||
.cornerRadius(10.7, corners: [.topLeft, .topRight])
|
||||
|
||||
if let _ = audioContent.totalContentCount, let remainingContentCount = audioContent.remainingContentCount, remainingContentCount <= 0, audioContent.creator.creatorId != UserDefaults.int(forKey: .userId), !audioContent.existOrdered {
|
||||
Text("Sold Out")
|
||||
.font(.custom(Font.bold.rawValue, size: 36.7))
|
||||
.foregroundColor(.white)
|
||||
.frame(
|
||||
width: screenSize().width - 26.7,
|
||||
height: screenSize().width - 26.7,
|
||||
alignment: .center
|
||||
)
|
||||
.background(Color.black.opacity(0.6))
|
||||
} else if audioContent.releaseDate == nil && !isAlertPreview || (audioContent.isActivePreview && !audioContent.contentUrl.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) {
|
||||
if audioContent.releaseDate == nil && !isAlertPreview || (isAlertPreview && audioContent.isActivePreview) {
|
||||
Image(isPlaying() ? "btn_audio_content_pause" : isAlertPreview ? "btn_audio_content_preview_play" : "btn_audio_content_play")
|
||||
.onTapGesture {
|
||||
if isPlaying() {
|
||||
|
@ -66,7 +56,7 @@ struct ContentDetailPlayView: View {
|
|||
isShowPreviewAlert = true
|
||||
}
|
||||
}
|
||||
} else if audioContent.releaseDate == nil {
|
||||
} else {
|
||||
Text("해당 콘텐츠는 크리에이터의 요청으로\n미리듣기를 제공하지 않습니다.")
|
||||
.font(.custom(Font.medium.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
|
|
|
@ -33,7 +33,7 @@ struct ContentDetailPurchaseButton: View {
|
|||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 48.7)
|
||||
.background(Color.button)
|
||||
.background(Color(hex: "9970ff"))
|
||||
.cornerRadius(5.3)
|
||||
.padding(.top, 18.3)
|
||||
}
|
||||
|
|
|
@ -107,29 +107,10 @@ struct ContentDetailView: View {
|
|||
!audioContent.existOrdered &&
|
||||
audioContent.orderType == nil &&
|
||||
audioContent.creator.creatorId != UserDefaults.int(forKey: .userId) {
|
||||
if let _ = audioContent.totalContentCount, let remainingContentCount = audioContent.remainingContentCount, remainingContentCount <= 0 {
|
||||
Text("해당 콘텐츠가 매진되었습니다.")
|
||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
||||
.foregroundColor(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 48.7)
|
||||
.background(Color(hex: "525252"))
|
||||
.cornerRadius(5.3)
|
||||
.padding(.top, 18.3)
|
||||
.padding(.horizontal, 13.3)
|
||||
} else {
|
||||
ContentDetailPurchaseButton(price: audioContent.price, isOnlyRental: audioContent.isOnlyRental)
|
||||
.contentShape(Rectangle())
|
||||
.padding(.horizontal, 13.3)
|
||||
.onTapGesture {
|
||||
if let _ = audioContent.totalContentCount, let _ = audioContent.remainingContentCount {
|
||||
viewModel.orderType = .KEEP
|
||||
isShowOrderConfirmView = true
|
||||
} else {
|
||||
isShowOrderView = true
|
||||
}
|
||||
}
|
||||
}
|
||||
ContentDetailPurchaseButton(price: audioContent.price, isOnlyRental: audioContent.isOnlyRental)
|
||||
.contentShape(Rectangle())
|
||||
.padding(.horizontal, 13.3)
|
||||
.onTapGesture { isShowOrderView = true }
|
||||
}
|
||||
|
||||
if audioContent.isCommentAvailable {
|
||||
|
|
|
@ -18,9 +18,6 @@ struct GetAudioContentDetailResponse: Decodable {
|
|||
let price: Int
|
||||
let duration: String
|
||||
let releaseDate: String?
|
||||
let totalContentCount: Int?
|
||||
let remainingContentCount: Int?
|
||||
let orderSequence: Int?
|
||||
let isActivePreview: Bool
|
||||
let isAdult: Bool
|
||||
let isMosaic: Bool
|
||||
|
|
|
@ -38,45 +38,47 @@ struct ContentMainView: View {
|
|||
.padding(.bottom, 26.7)
|
||||
.padding(.horizontal, 13.3)
|
||||
|
||||
HStack(spacing: 8) {
|
||||
ZStack {
|
||||
Image("img_bg_short_play")
|
||||
.resizable()
|
||||
.frame(height: 53.3)
|
||||
.frame(maxWidth: .infinity)
|
||||
.cornerRadius(2.6)
|
||||
|
||||
HStack(spacing: 13.3) {
|
||||
VStack(spacing: 2.7) {
|
||||
HStack(spacing: 2.7) {
|
||||
Image("ic_short_play")
|
||||
|
||||
Text("숏플")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.font(.custom(Font.bold.rawValue, size: 14.7))
|
||||
.foregroundColor(Color(hex: "dd158d"))
|
||||
}
|
||||
.cornerRadius(2.6)
|
||||
|
||||
Text("짧지만 꼴릿한 이야기")
|
||||
.font(.custom(Font.light.rawValue, size: 10))
|
||||
.foregroundColor(Color(hex: "dd158d"))
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color(hex: "ffecf7"))
|
||||
.cornerRadius(2.6)
|
||||
.onTapGesture {
|
||||
AppState.shared.setAppStep(
|
||||
step: .contentAllByTheme(themeId: 11)
|
||||
)
|
||||
}
|
||||
|
||||
ZStack {
|
||||
Image("img_bg_review_live")
|
||||
.resizable()
|
||||
.frame(height: 53.3)
|
||||
.frame(maxWidth: .infinity)
|
||||
.cornerRadius(2.6)
|
||||
|
||||
VStack(spacing: 2.7) {
|
||||
HStack(spacing: 2.7) {
|
||||
Image("ic_thumb_play_blue")
|
||||
|
||||
Text("라이브 다시듣기")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.font(.custom(Font.bold.rawValue, size: 14.7))
|
||||
.foregroundColor(Color(hex: "0057ff"))
|
||||
}
|
||||
.cornerRadius(2.6)
|
||||
|
||||
Text("놓쳤던 라이브 다시듣기")
|
||||
.font(.custom(Font.light.rawValue, size: 10))
|
||||
.foregroundColor(Color(hex: "0057ff"))
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color(hex: "ecf9ff"))
|
||||
.cornerRadius(2.6)
|
||||
.onTapGesture {
|
||||
AppState.shared.setAppStep(
|
||||
step: .contentAllByTheme(themeId: 7)
|
||||
|
|
|
@ -33,8 +33,8 @@ struct CreatorCommunityCommentItemView: View {
|
|||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(commentItem.nickname)
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color.gray90)
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Text(commentItem.date)
|
||||
.font(.custom(Font.medium.rawValue, size: 10.3))
|
||||
|
@ -86,10 +86,9 @@ struct CreatorCommunityCommentItemView: View {
|
|||
} else {
|
||||
VStack(alignment: .leading, spacing: 13.3) {
|
||||
Text(commentItem.comment)
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.grayee)
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color(hex: "777777"))
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.lineSpacing(8)
|
||||
.padding(.top, 13.3)
|
||||
|
||||
if !isReplyComment {
|
||||
|
@ -102,7 +101,7 @@ struct CreatorCommunityCommentItemView: View {
|
|||
) {
|
||||
Text(commentItem.replyCount > 0 ? "답글 \(commentItem.replyCount)개" : "답글 쓰기")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.button)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,12 +37,12 @@ struct UserProfileFanTalkCheersItemView: View {
|
|||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text("\(cheersItem.nickname)")
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color.gray90)
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Text("\(cheersItem.date)")
|
||||
.font(.custom(Font.medium.rawValue, size: 10.7))
|
||||
.foregroundColor(Color.gray55)
|
||||
.foregroundColor(Color(hex: "525252"))
|
||||
.padding(.top, 8.3)
|
||||
|
||||
if isModeModify {
|
||||
|
@ -60,14 +60,14 @@ struct UserProfileFanTalkCheersItemView: View {
|
|||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.strokeBorder(lineWidth: 1)
|
||||
.foregroundColor(Color.button)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
)
|
||||
|
||||
Text("수정")
|
||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
||||
.foregroundColor(Color(hex: "ffffff"))
|
||||
.padding(13.3)
|
||||
.background(Color.button)
|
||||
.background(Color(hex: "9970ff"))
|
||||
.cornerRadius(6.7)
|
||||
.onTapGesture {
|
||||
modifyCheer(cheersItem.cheersId, cheers)
|
||||
|
@ -76,7 +76,7 @@ struct UserProfileFanTalkCheersItemView: View {
|
|||
|
||||
Text("취소")
|
||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.button)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
.padding(13.3)
|
||||
.background(Color(hex: "222222"))
|
||||
.cornerRadius(6.7)
|
||||
|
@ -87,10 +87,9 @@ struct UserProfileFanTalkCheersItemView: View {
|
|||
.padding(.top, 13.3)
|
||||
} else {
|
||||
Text("\(cheersItem.content)")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.grayee)
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color(hex: "777777"))
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.lineSpacing(8)
|
||||
.padding(.top, 13.3)
|
||||
}
|
||||
|
||||
|
@ -132,7 +131,7 @@ struct UserProfileFanTalkCheersItemView: View {
|
|||
if userId == UserDefaults.int(forKey: .userId) {
|
||||
Text("답글쓰기")
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color.button)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
.padding(.top, 18.3)
|
||||
.onTapGesture {
|
||||
isShowInputReply = true
|
||||
|
@ -147,7 +146,7 @@ struct UserProfileFanTalkCheersItemView: View {
|
|||
.frame(minWidth: 100)
|
||||
.padding(.horizontal, 6.7)
|
||||
.padding(.vertical, 6.7)
|
||||
.background(Color.button.opacity(0.3))
|
||||
.background(Color(hex: "9970ff").opacity(0.3))
|
||||
.cornerRadius(16.7)
|
||||
.padding(.top, 18.3)
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ struct FollowerListItemView: View {
|
|||
Spacer()
|
||||
|
||||
if let isFollow = item.isFollow {
|
||||
Image(isFollow ? "btn_following_big" : "btn_follow_big")
|
||||
Image(isFollow ? "btn_notification_selected" : "btn_notification")
|
||||
.onTapGesture {
|
||||
isFollow ?
|
||||
creatorUnFollow(item.userId) :
|
||||
|
|
|
@ -57,7 +57,7 @@ struct UserProfileCreatorView: View {
|
|||
}
|
||||
} else {
|
||||
VStack(alignment: .leading, spacing: 9.3) {
|
||||
Image(creator.isNotification ? "btn_following_big" : "btn_follow_big")
|
||||
Image(creator.isNotification ? "btn_notification_selected" : "btn_notification")
|
||||
.resizable()
|
||||
.frame(width: 83.3, height: 26.7)
|
||||
.onTapGesture {
|
||||
|
|
|
@ -31,7 +31,7 @@ struct FollowCreatorItemView: View {
|
|||
|
||||
Spacer()
|
||||
|
||||
Image(isFollow ? "btn_following_big" : "btn_follow_big")
|
||||
Image(isFollow ? "btn_notification_selected" : "btn_notification")
|
||||
.onTapGesture {
|
||||
if isFollow {
|
||||
onClickUnFollow(creator.creatorId)
|
||||
|
|
|
@ -36,7 +36,6 @@ enum LiveApi {
|
|||
case getDonationMessageList(roomId: Int)
|
||||
case deleteDonationMessage(roomId: Int, messageUUID: String)
|
||||
case getUserProfile(roomId: Int, userId: Int)
|
||||
case getAllMenuPreset(creatorId: Int)
|
||||
}
|
||||
|
||||
extension LiveApi: TargetType {
|
||||
|
@ -126,15 +125,12 @@ extension LiveApi: TargetType {
|
|||
|
||||
case .getUserProfile(let roomId, let userId):
|
||||
return "/live/room/\(roomId)/profile/\(userId)"
|
||||
|
||||
case .getAllMenuPreset:
|
||||
return "/live/room/menu/all"
|
||||
}
|
||||
}
|
||||
|
||||
var method: Moya.Method {
|
||||
switch self {
|
||||
case .roomList, .recentVisitRoomUsers, .getReservations, .getReservation, .getRoomDetail, .getTags, .getRecentRoomInfo, .getRoomInfo, .donationStatus, .donationTotal, .getDonationMessageList, .getUserProfile, .getAllMenuPreset:
|
||||
case .roomList, .recentVisitRoomUsers, .getReservations, .getReservation, .getRoomDetail, .getTags, .getRecentRoomInfo, .getRoomInfo, .donationStatus, .donationTotal, .getDonationMessageList, .getUserProfile:
|
||||
return .get
|
||||
|
||||
case .makeReservation, .enterRoom, .createRoom, .quitRoom, .donation, .refundDonation, .kickOut:
|
||||
|
@ -230,9 +226,6 @@ extension LiveApi: TargetType {
|
|||
|
||||
case .deleteDonationMessage(let roomId, let messageUUID):
|
||||
return .requestJSONEncodable(DeleteLiveRoomDonationMessage(roomId: roomId, messageUUID: messageUUID))
|
||||
|
||||
case .getAllMenuPreset(let creatorId):
|
||||
return .requestParameters(parameters: ["creatorId" : creatorId], encoding: URLEncoding.queryString)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -116,8 +116,4 @@ final class LiveRepository {
|
|||
func getUserProfile(roomId: Int, userId: Int) -> AnyPublisher<Response, MoyaError> {
|
||||
api.requestPublisher(.getUserProfile(roomId: roomId, userId: userId))
|
||||
}
|
||||
|
||||
func getAllMenuPreset(creatorId: Int) -> AnyPublisher<Response, MoyaError> {
|
||||
api.requestPublisher(.getAllMenuPreset(creatorId: creatorId))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,39 +11,33 @@ import Kingfisher
|
|||
struct LiveNowAllItemView: View {
|
||||
|
||||
let item: GetRoomListResponse
|
||||
let itemWidth: CGFloat
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
ZStack {
|
||||
KFImage(URL(string: item.coverImageUrl))
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(width: itemWidth, height: itemWidth * 144 / 102, alignment: .center)
|
||||
.cornerRadius(4.7)
|
||||
.clipped()
|
||||
VStack(spacing: 13.3) {
|
||||
HStack(spacing: 20) {
|
||||
ZStack(alignment: .topLeading) {
|
||||
KFImage(URL(string: item.coverImageUrl))
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(width: 80, height: 116.7, alignment: .top)
|
||||
.cornerRadius(4.7)
|
||||
.clipped()
|
||||
}
|
||||
|
||||
LinearGradient(
|
||||
colors: [Color.black.opacity(0.1), Color.black.opacity(0.8)],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom
|
||||
)
|
||||
|
||||
VStack(alignment: .trailing, spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
HStack(spacing: 1) {
|
||||
if item.price > 0 {
|
||||
Image("ic_can_white")
|
||||
}
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
HStack(alignment: .top, spacing: 0) {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(item.creatorNickname)
|
||||
.font(.custom(Font.medium.rawValue, size: 11.3))
|
||||
.foregroundColor(Color(hex: "bbbbbb"))
|
||||
|
||||
Text(item.price > 0 ? "\(item.price)" : "무료")
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color.white)
|
||||
Text(item.title)
|
||||
.font(.custom(Font.medium.rawValue, size: 15.3))
|
||||
.foregroundColor(Color(hex: "e2e2e2"))
|
||||
.lineLimit(2)
|
||||
.padding(.top, 4.3)
|
||||
.padding(.trailing, 20)
|
||||
}
|
||||
.padding(.horizontal, 7.3)
|
||||
.padding(.vertical, 4)
|
||||
.background(item.price > 0 ? Color.mainRed3 : Color.gray11)
|
||||
.cornerRadius(13.3)
|
||||
|
||||
Spacer()
|
||||
|
||||
|
@ -51,92 +45,48 @@ struct LiveNowAllItemView: View {
|
|||
Image("ic_lock")
|
||||
.resizable()
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(2.7)
|
||||
.background(Color.gray33.opacity(0.7))
|
||||
.clipShape(Circle())
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 3.3)
|
||||
.padding(.top, 3.3)
|
||||
.padding(.top, 13.3)
|
||||
|
||||
Spacer()
|
||||
|
||||
if item.numberOfPeople - item.numberOfParticipate < 3 {
|
||||
HStack(spacing: 0) {
|
||||
Spacer()
|
||||
HStack(spacing: 0) {
|
||||
Text(item.numberOfPeople > item.numberOfParticipate ? "참여가능" : "Sold out")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(
|
||||
Color(
|
||||
hex: item.numberOfPeople > item.numberOfParticipate ?
|
||||
"3bb9f1" :
|
||||
"ffd300"
|
||||
)
|
||||
)
|
||||
|
||||
Spacer()
|
||||
|
||||
if item.price > 0 {
|
||||
Text("\(item.price)")
|
||||
.font(.custom(Font.bold.rawValue, size: 15.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
HStack(spacing: 2) {
|
||||
Text("잔여")
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color.grayee)
|
||||
|
||||
Text("\(item.numberOfPeople - item.numberOfParticipate)")
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color.button)
|
||||
}
|
||||
.padding(.horizontal, 4)
|
||||
.padding(.vertical, 3)
|
||||
.background(Color.gray33.opacity(0.7))
|
||||
.cornerRadius(13.3)
|
||||
Image("ic_can")
|
||||
.resizable()
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(.leading, 6.7)
|
||||
} else {
|
||||
Text("무료")
|
||||
.font(.custom(Font.bold.rawValue, size: 15.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
}
|
||||
.padding(.horizontal, 3.3)
|
||||
.padding(.bottom, 3.3)
|
||||
}
|
||||
.padding(.bottom, 3.3)
|
||||
}
|
||||
}
|
||||
.frame(width: itemWidth, height: itemWidth * 144 / 102)
|
||||
|
||||
Text("\(item.title)")
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.lineLimit(2)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
if item.tags.count > 0 {
|
||||
Text("\(item.tags.map { "#\($0)" }.joined(separator: " "))")
|
||||
.font(.custom(Font.medium.rawValue, size: 11))
|
||||
.foregroundColor(Color.button)
|
||||
}
|
||||
|
||||
HStack(spacing: 5.3) {
|
||||
KFImage(URL(string: item.creatorProfileImage))
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(width: 21.3, height: 21.3, alignment: .center)
|
||||
.clipShape(Circle())
|
||||
|
||||
Text("\(item.creatorNickname)")
|
||||
.font(.custom(Font.medium.rawValue, size: 10))
|
||||
.foregroundColor(Color.gray77)
|
||||
}
|
||||
Rectangle()
|
||||
.foregroundColor(Color(hex: "909090").opacity(0.5))
|
||||
.frame(width: screenSize().width - 26.7, height: 1)
|
||||
}
|
||||
.frame(width: itemWidth)
|
||||
.frame(width: screenSize().width - 26.7, height: 130, alignment: .center)
|
||||
}
|
||||
}
|
||||
|
||||
struct LiveNowAllItemView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
LiveNowAllItemView(
|
||||
item: GetRoomListResponse(
|
||||
roomId: 99,
|
||||
title: "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
content: "testtest",
|
||||
beginDateTime: "2022.05.23 Mon 03:00 PM",
|
||||
numberOfParticipate: 3,
|
||||
numberOfPeople: 5,
|
||||
coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
|
||||
isAdult: true,
|
||||
price: 20,
|
||||
tags: ["팬미팅", "힐링"],
|
||||
channelName: nil,
|
||||
creatorProfileImage: "https://test-cf.sodalive.net/profile/default-profile.png",
|
||||
creatorNickname: "user8",
|
||||
creatorId: 19,
|
||||
isReservation: false,
|
||||
isPrivateRoom: true
|
||||
),
|
||||
itemWidth: 102
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,14 +13,6 @@ struct LiveNowAllView: View {
|
|||
@StateObject var viewModel = LiveViewModel()
|
||||
@StateObject var liveAllViewModel = LiveAllViewModel()
|
||||
|
||||
let columns = [
|
||||
GridItem(.flexible(), alignment: .top),
|
||||
GridItem(.flexible(), alignment: .top),
|
||||
GridItem(.flexible(), alignment: .top)
|
||||
]
|
||||
|
||||
let spacing: CGFloat = 13.3
|
||||
|
||||
let onClickParticipant: (Int) -> Void
|
||||
|
||||
var body: some View {
|
||||
|
@ -37,11 +29,11 @@ struct LiveNowAllView: View {
|
|||
viewModel.getLiveNowList()
|
||||
},
|
||||
content: {
|
||||
LazyVGrid(columns: columns, spacing: spacing) {
|
||||
VStack(spacing: 13.3) {
|
||||
ForEach(0..<viewModel.liveNowItems.count, id: \.self) { index in
|
||||
let item = viewModel.liveNowItems[index]
|
||||
|
||||
LiveNowAllItemView(item: item, itemWidth: (screenSize().width - (spacing * (CGFloat(columns.count + 1)))) / 3)
|
||||
LiveNowAllItemView(item: item)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
self.liveAllViewModel.selectedRoomId = item.roomId
|
||||
|
|
|
@ -12,8 +12,8 @@ struct LiveNowItemView: View {
|
|||
|
||||
let item: GetRoomListResponse
|
||||
|
||||
let width: CGFloat = 128
|
||||
let height: CGFloat = 179
|
||||
let width: CGFloat = 133.3
|
||||
let height: CGFloat = 176.7
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
|
@ -32,20 +32,14 @@ struct LiveNowItemView: View {
|
|||
)
|
||||
|
||||
VStack(alignment: .trailing, spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
HStack(spacing: 1) {
|
||||
if item.price > 0 {
|
||||
Image("ic_can_white")
|
||||
}
|
||||
|
||||
Text(item.price > 0 ? "\(item.price)" : "무료")
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color.white)
|
||||
}
|
||||
.padding(.horizontal, 7.3)
|
||||
.padding(.vertical, 4)
|
||||
.background(item.price > 0 ? Color.mainRed3 : Color.gray11)
|
||||
.cornerRadius(13.3)
|
||||
HStack(spacing: 3.3) {
|
||||
Text(item.price > 0 ? "유료" : "무료")
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color.white)
|
||||
.padding(.horizontal, 7.3)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color(hex: item.price > 0 ? "881609" : "643bc8"))
|
||||
.cornerRadius(10)
|
||||
|
||||
Spacer()
|
||||
|
||||
|
@ -53,9 +47,6 @@ struct LiveNowItemView: View {
|
|||
Image("ic_lock")
|
||||
.resizable()
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(2.7)
|
||||
.background(Color.gray33.opacity(0.7))
|
||||
.clipShape(Circle())
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 3.3)
|
||||
|
@ -106,7 +97,7 @@ struct LiveNowItemView_Previews: PreviewProvider {
|
|||
numberOfPeople: 5,
|
||||
coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
|
||||
isAdult: true,
|
||||
price: 20,
|
||||
price: 0,
|
||||
tags: ["팬미팅", "힐링"],
|
||||
channelName: nil,
|
||||
creatorProfileImage: "https://test-cf.sodalive.net/profile/default-profile.png",
|
||||
|
|
|
@ -15,7 +15,6 @@ struct LiveRoomChatRawMessage: Codable {
|
|||
let type: LiveRoomChatRawMessageType
|
||||
let message: String
|
||||
let can: Int
|
||||
var signatureImageUrl: String? = nil
|
||||
let donationMessage: String?
|
||||
var isActiveRoulette: Bool? = nil
|
||||
}
|
||||
|
|
|
@ -19,7 +19,4 @@ struct CreateLiveRoomRequest: Encodable {
|
|||
var password: String? = nil
|
||||
let timezone: String = TimeZone.current.identifier
|
||||
var beginDateTimeString: String? = nil
|
||||
var menuPanId: Int = 0
|
||||
var menuPan: String = ""
|
||||
var isActiveMenuPan: Bool = false
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ struct LiveRoomCreateView: View {
|
|||
|
||||
Text("라이브 만들기")
|
||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
@ -49,13 +49,13 @@ struct LiveRoomCreateView: View {
|
|||
if viewModel.isShowGetRecentInfoButton {
|
||||
Text("최근 데이터 가져오기")
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(Color.button)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 10.7)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.stroke()
|
||||
.foregroundColor(Color.button)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
)
|
||||
.onTapGesture {
|
||||
viewModel.getRecentInfo()
|
||||
|
@ -71,7 +71,7 @@ struct LiveRoomCreateView: View {
|
|||
VStack(spacing: 0) {
|
||||
Text("썸네일")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.padding(.horizontal, 13.3)
|
||||
.padding(.top, 13.3)
|
||||
.frame(width: screenSize().width, alignment: .leading)
|
||||
|
@ -95,13 +95,13 @@ struct LiveRoomCreateView: View {
|
|||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 80, height: 116.8)
|
||||
.background(Color.bg)
|
||||
.background(Color(hex: "3e3358"))
|
||||
.cornerRadius(10)
|
||||
}
|
||||
|
||||
Image("ic_camera")
|
||||
.padding(10)
|
||||
.background(Color.button)
|
||||
.background(Color(hex: "9970ff"))
|
||||
.cornerRadius(30)
|
||||
.offset(x: 40, y: 40)
|
||||
}
|
||||
|
@ -115,32 +115,25 @@ struct LiveRoomCreateView: View {
|
|||
.frame(width: screenSize().width - 26.7)
|
||||
.padding(.top, 33.3)
|
||||
|
||||
ContentInputView()
|
||||
TagSelectView()
|
||||
.frame(width: screenSize().width - 26.7)
|
||||
.padding(.top, 33.3)
|
||||
}
|
||||
|
||||
VStack(spacing: 33.3) {
|
||||
LiveRoomMenuSelectView(
|
||||
menu: $viewModel.menu,
|
||||
isActivate: $viewModel.isActivateMenu,
|
||||
menuCount: viewModel.menuList.count,
|
||||
selectedMenu: viewModel.selectedMenu ?? .MENU_1,
|
||||
selectMenu: {
|
||||
viewModel.selectMenuPreset(selectedMenuPreset: $0)
|
||||
}
|
||||
)
|
||||
.frame(width: screenSize().width - 26.7)
|
||||
|
||||
TagSelectView()
|
||||
VStack(spacing: 0) {
|
||||
ContentInputView()
|
||||
.frame(width: screenSize().width - 26.7)
|
||||
.padding(.top, 33.3)
|
||||
|
||||
TimeSettingView()
|
||||
if viewModel.roomType != .SECRET {
|
||||
TimeSettingView()
|
||||
.padding(.top, 33.3)
|
||||
}
|
||||
|
||||
NumberOfPeopleLimitView()
|
||||
.frame(width: screenSize().width - 26.7)
|
||||
.padding(.top, 33.3)
|
||||
}
|
||||
.padding(.top, 33.3)
|
||||
|
||||
VStack(spacing: 0) {
|
||||
RoomTypeSettingView()
|
||||
|
@ -153,9 +146,11 @@ struct LiveRoomCreateView: View {
|
|||
.padding(.top, 33.3)
|
||||
}
|
||||
|
||||
PriceSettingView()
|
||||
.frame(width: screenSize().width - 26.7)
|
||||
.padding(.top, 33.3)
|
||||
if UserDefaults.string(forKey: .role) == MemberRole.CREATOR.rawValue {
|
||||
PriceSettingView()
|
||||
.frame(width: screenSize().width - 26.7)
|
||||
.padding(.top, 33.3)
|
||||
}
|
||||
}
|
||||
|
||||
HStack(alignment: .top, spacing: 0) {
|
||||
|
@ -172,23 +167,23 @@ struct LiveRoomCreateView: View {
|
|||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||
.foregroundColor(Color.white)
|
||||
.frame(width: screenSize().width - 26.7, height: 50)
|
||||
.background(Color.button)
|
||||
.background(Color(hex: "9970ff"))
|
||||
.cornerRadius(10)
|
||||
.padding(.vertical, 13.3)
|
||||
}
|
||||
}
|
||||
.frame(width: screenSize().width)
|
||||
.background(Color.gray22)
|
||||
.background(Color(hex: "222222"))
|
||||
.cornerRadius(16.7, corners: [.topLeft, .topRight])
|
||||
.padding(.top, 30)
|
||||
|
||||
Rectangle()
|
||||
.foregroundColor(Color.gray22)
|
||||
.foregroundColor(Color(hex: "222222"))
|
||||
.frame(width: screenSize().width, height: keyboardHandler.keyboardHeight)
|
||||
|
||||
if proxy.safeAreaInsets.bottom > 0 {
|
||||
Rectangle()
|
||||
.foregroundColor(Color.gray22)
|
||||
.foregroundColor(Color(hex: "222222"))
|
||||
.frame(width: screenSize().width, height: 15.3)
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +234,7 @@ struct LiveRoomCreateView: View {
|
|||
.padding(.vertical, 13.3)
|
||||
.frame(width: geo.size.width - 66.7, alignment: .center)
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.background(Color.button)
|
||||
.background(Color(hex: "9970ff"))
|
||||
.foregroundColor(Color.white)
|
||||
.multilineTextAlignment(.center)
|
||||
.cornerRadius(20)
|
||||
|
@ -250,7 +245,6 @@ struct LiveRoomCreateView: View {
|
|||
}
|
||||
.onAppear {
|
||||
viewModel.timeSettingMode = timeSettingMode
|
||||
viewModel.getAllMenuPreset()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,7 +253,7 @@ struct LiveRoomCreateView: View {
|
|||
VStack(spacing: 0) {
|
||||
Text("제목")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.padding(.horizontal, 13.3)
|
||||
.frame(width: screenSize().width, alignment: .leading)
|
||||
|
||||
|
@ -267,15 +261,15 @@ struct LiveRoomCreateView: View {
|
|||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.grayee)
|
||||
.accentColor(Color.button)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.accentColor(Color(hex: "3bb9f1"))
|
||||
.keyboardType(.default)
|
||||
.padding(.top, 12)
|
||||
.padding(.horizontal, 6.7)
|
||||
|
||||
Rectangle()
|
||||
.frame(height: 1)
|
||||
.foregroundColor(Color.gray90.opacity(0.7))
|
||||
.foregroundColor(Color(hex: "909090").opacity(0.7))
|
||||
.padding(.top, 8.3)
|
||||
}
|
||||
}
|
||||
|
@ -285,7 +279,7 @@ struct LiveRoomCreateView: View {
|
|||
VStack(alignment: .leading, spacing: 13.3) {
|
||||
Text("관심사")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Button(action: {
|
||||
hideKeyboard()
|
||||
|
@ -293,15 +287,15 @@ struct LiveRoomCreateView: View {
|
|||
}) {
|
||||
Text("관심사 선택")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.button)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
.padding(.vertical, 13.7)
|
||||
.frame(width: screenSize().width - 26.7)
|
||||
.background(Color.button.opacity(0.2))
|
||||
.background(Color(hex: "9970ff").opacity(0.2))
|
||||
.cornerRadius(24.3)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 24.3)
|
||||
.stroke()
|
||||
.foregroundColor(Color.button)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -321,7 +315,7 @@ struct LiveRoomCreateView: View {
|
|||
}
|
||||
}
|
||||
.padding(10)
|
||||
.background(Color.button)
|
||||
.background(Color(hex: "9970ff"))
|
||||
.cornerRadius(24.3)
|
||||
}
|
||||
}
|
||||
|
@ -336,25 +330,25 @@ struct LiveRoomCreateView: View {
|
|||
HStack(spacing: 0) {
|
||||
Text("공지")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("\(viewModel.content.count)자")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.mainRed) +
|
||||
.foregroundColor(Color(hex: "ff5c49")) +
|
||||
Text(" / 1000자")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.gray77)
|
||||
.foregroundColor(Color(hex: "777777"))
|
||||
}
|
||||
|
||||
TextViewWrapper(
|
||||
text: $viewModel.content,
|
||||
placeholder: viewModel.placeholder,
|
||||
textColorHex: "eeeeee",
|
||||
backgroundColorHex: "303030"
|
||||
backgroundColorHex: "222222"
|
||||
)
|
||||
.frame(width: screenSize().width - 26.7, height: 200)
|
||||
.frame(width: screenSize().width - 26.7, height: 133.3)
|
||||
.cornerRadius(6.7)
|
||||
.padding(.top, 13.3)
|
||||
}
|
||||
|
@ -365,7 +359,7 @@ struct LiveRoomCreateView: View {
|
|||
VStack(spacing: 0) {
|
||||
Text("시간설정")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.frame(width: screenSize().width - 26.7, alignment: .leading)
|
||||
|
||||
HStack(spacing: 13.3) {
|
||||
|
@ -406,11 +400,15 @@ struct LiveRoomCreateView: View {
|
|||
.foregroundColor(
|
||||
viewModel.timeSettingMode == timeSettingMode ?
|
||||
.white :
|
||||
Color.button
|
||||
Color(hex: "9970ff")
|
||||
)
|
||||
}
|
||||
.frame(width: buttonWidth, height: 48.7)
|
||||
.background(viewModel.timeSettingMode == timeSettingMode ? Color.button : Color.bg)
|
||||
.background(
|
||||
viewModel.timeSettingMode == timeSettingMode ?
|
||||
Color(hex: "9970ff") :
|
||||
Color(hex: "1f1734")
|
||||
)
|
||||
.cornerRadius(6.7)
|
||||
.onTapGesture {
|
||||
hideKeyboard()
|
||||
|
@ -426,7 +424,7 @@ struct LiveRoomCreateView: View {
|
|||
VStack(alignment: .leading, spacing: 6.7) {
|
||||
Text("예약 날짜")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Button(action: {
|
||||
hideKeyboard()
|
||||
|
@ -434,11 +432,11 @@ struct LiveRoomCreateView: View {
|
|||
}) {
|
||||
Text(viewModel.reservationDateString)
|
||||
.font(.custom(Font.medium.rawValue, size: 14.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.frame(width: buttonWidth, height: 48.7)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 6.7)
|
||||
.stroke(Color.button, lineWidth: 1.3)
|
||||
.stroke(Color(hex: "9970ff"), lineWidth: 1.3)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -446,7 +444,7 @@ struct LiveRoomCreateView: View {
|
|||
VStack(alignment: .leading, spacing: 6.7) {
|
||||
Text("예약 시간")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Button(action: {
|
||||
hideKeyboard()
|
||||
|
@ -454,18 +452,18 @@ struct LiveRoomCreateView: View {
|
|||
}) {
|
||||
Text(viewModel.reservationTimeString)
|
||||
.font(.custom(Font.medium.rawValue, size: 14.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.frame(width: buttonWidth, height: 48.7)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 6.7)
|
||||
.stroke(Color.button, lineWidth: 1.3)
|
||||
.stroke(Color(hex: "9970ff"), lineWidth: 1.3)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(width: screenSize().width)
|
||||
.padding(.vertical, 13.3)
|
||||
.background(Color.gray22)
|
||||
.background(Color(hex: "222222"))
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -473,7 +471,7 @@ struct LiveRoomCreateView: View {
|
|||
VStack(spacing: 13.3) {
|
||||
Text("참여인원 설정")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.frame(width: screenSize().width - 26.7, alignment: .leading)
|
||||
|
||||
TextField("최대 인원 999명", text: $viewModel.numberOfPeople)
|
||||
|
@ -481,12 +479,12 @@ struct LiveRoomCreateView: View {
|
|||
.disableAutocorrection(true)
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.custom(Font.medium.rawValue, size: 14.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.accentColor(Color.button)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.accentColor(Color(hex: "3bb9f1"))
|
||||
.keyboardType(.numberPad)
|
||||
.padding(.vertical, 15.7)
|
||||
.frame(width: screenSize().width - 26.7, alignment: .center)
|
||||
.background(Color.gray22)
|
||||
.background(Color(hex: "222222"))
|
||||
.cornerRadius(6.7)
|
||||
}
|
||||
}
|
||||
|
@ -510,12 +508,12 @@ struct LiveRoomCreateView: View {
|
|||
Button(action: { self.isShowSelectDateView = false }) {
|
||||
Text("확인")
|
||||
.font(.system(size: 16))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.padding(.vertical, 10)
|
||||
.frame(width: proxy.size.width - 53.4)
|
||||
}
|
||||
}
|
||||
.background(Color.gray22)
|
||||
.background(Color(hex: "222222"))
|
||||
.cornerRadius(6.7)
|
||||
}
|
||||
.frame(width: proxy.size.width)
|
||||
|
@ -541,12 +539,12 @@ struct LiveRoomCreateView: View {
|
|||
Button(action: { self.isShowSelectTimeView = false }) {
|
||||
Text("확인")
|
||||
.font(.system(size: 16))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.padding(.vertical, 10)
|
||||
.frame(width: proxy.size.width)
|
||||
}
|
||||
}
|
||||
.background(Color.gray22)
|
||||
.background(Color(hex: "222222"))
|
||||
.cornerRadius(6.7)
|
||||
}
|
||||
.frame(width: proxy.size.width)
|
||||
|
@ -558,7 +556,7 @@ struct LiveRoomCreateView: View {
|
|||
VStack(spacing: 0) {
|
||||
Text("공개 설정")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.frame(width: screenSize().width - 26.7, alignment: .leading)
|
||||
|
||||
HStack(spacing: 13.3) {
|
||||
|
@ -593,10 +591,18 @@ struct LiveRoomCreateView: View {
|
|||
|
||||
Text(title)
|
||||
.font(.custom(Font.bold.rawValue, size: 14.7))
|
||||
.foregroundColor(viewModel.roomType == type ? .white : Color.button)
|
||||
.foregroundColor(
|
||||
viewModel.roomType == type ?
|
||||
.white :
|
||||
Color(hex: "9970ff")
|
||||
)
|
||||
}
|
||||
.frame(width: buttonWidth, height: 48.7)
|
||||
.background(viewModel.roomType == type ? Color.button : Color.bg)
|
||||
.background(
|
||||
viewModel.roomType == type ?
|
||||
Color(hex: "9970ff") :
|
||||
Color(hex: "1f1734")
|
||||
)
|
||||
.cornerRadius(6.7)
|
||||
.onTapGesture {
|
||||
hideKeyboard()
|
||||
|
@ -611,7 +617,7 @@ struct LiveRoomCreateView: View {
|
|||
VStack(spacing: 13.3) {
|
||||
Text("방 비밀번호 입력")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.frame(width: screenSize().width - 26.7, alignment: .leading)
|
||||
|
||||
TextField("방 입장 비밀번호 6자리를 입력해 주세요.", text: $viewModel.password)
|
||||
|
@ -619,12 +625,12 @@ struct LiveRoomCreateView: View {
|
|||
.disableAutocorrection(true)
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.custom(Font.medium.rawValue, size: 14.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.accentColor(Color.button)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.accentColor(Color(hex: "3bb9f1"))
|
||||
.keyboardType(.numberPad)
|
||||
.padding(.vertical, 15.7)
|
||||
.frame(width: screenSize().width - 26.7, alignment: .center)
|
||||
.background(Color.gray22)
|
||||
.background(Color(hex: "222222"))
|
||||
.cornerRadius(6.7)
|
||||
}
|
||||
}
|
||||
|
@ -634,7 +640,7 @@ struct LiveRoomCreateView: View {
|
|||
VStack(spacing: 13.3) {
|
||||
Text("연령 제한")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.frame(width: screenSize().width - 26.7, alignment: .leading)
|
||||
|
||||
HStack(spacing: 13.3) {
|
||||
|
@ -662,10 +668,18 @@ struct LiveRoomCreateView: View {
|
|||
|
||||
Text(title)
|
||||
.font(.custom(Font.bold.rawValue, size: 14.7))
|
||||
.foregroundColor(viewModel.isAdult == isAdult ? .white : Color.button)
|
||||
.foregroundColor(
|
||||
viewModel.isAdult == isAdult ?
|
||||
.white :
|
||||
Color(hex: "9970ff")
|
||||
)
|
||||
}
|
||||
.frame(width: buttonWidth, height: 48.7)
|
||||
.background(viewModel.isAdult == isAdult ? Color.button : Color.bg)
|
||||
.background(
|
||||
viewModel.isAdult == isAdult ?
|
||||
Color(hex: "9970ff") :
|
||||
Color(hex: "1f1734")
|
||||
)
|
||||
.cornerRadius(6.7)
|
||||
.onTapGesture {
|
||||
hideKeyboard()
|
||||
|
@ -680,7 +694,7 @@ struct LiveRoomCreateView: View {
|
|||
VStack(spacing: 13.3) {
|
||||
Text("티켓 가격")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.frame(width: screenSize().width - 26.7, alignment: .leading)
|
||||
|
||||
HStack(spacing: 13.3) {
|
||||
|
@ -705,23 +719,35 @@ struct LiveRoomCreateView: View {
|
|||
.disableAutocorrection(true)
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.button)
|
||||
.accentColor(Color.button)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
.accentColor(Color(hex: "3bb9f1"))
|
||||
.keyboardType(.numberPad)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("캔")
|
||||
.font(.custom(Font.medium.rawValue, size: 14.7))
|
||||
.foregroundColor(Color.button)
|
||||
.foregroundColor(
|
||||
Color(hex: "9970ff")
|
||||
)
|
||||
}
|
||||
.padding(.horizontal, 13.3)
|
||||
.frame(width: screenSize().width - 26.7, height: 48.7)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 6.7)
|
||||
.stroke(!viewModel.prices.contains(viewModel.price) ? Color.button : Color.gray77, lineWidth: 1)
|
||||
.stroke(
|
||||
Color(hex: !viewModel.prices.contains(viewModel.price) ?
|
||||
"9970ff" :
|
||||
"777777"
|
||||
),
|
||||
lineWidth: 1
|
||||
)
|
||||
)
|
||||
.background(
|
||||
!viewModel.prices.contains(viewModel.price) ?
|
||||
Color(hex:"9970ff").opacity(0.3):
|
||||
Color(hex: "232323")
|
||||
)
|
||||
.background(!viewModel.prices.contains(viewModel.price) ? Color.button.opacity(0.3): Color.gray23)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -735,17 +761,23 @@ struct LiveRoomCreateView: View {
|
|||
Font.medium.rawValue,
|
||||
size: 14.7
|
||||
))
|
||||
.foregroundColor(viewModel.price == price ? Color.button : Color.gray77)
|
||||
.foregroundColor(
|
||||
Color(hex: viewModel.price == price ? "9970ff" : "777777")
|
||||
)
|
||||
}
|
||||
.frame(width: buttonWidth, height: 48.7)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 6.7)
|
||||
.stroke(
|
||||
viewModel.price == price ? Color.button : Color.gray77,
|
||||
Color(hex: viewModel.price == price ? "9970ff" : "777777"),
|
||||
lineWidth: 1
|
||||
)
|
||||
)
|
||||
.background(viewModel.price == price ? Color.button.opacity(0.3) : Color.gray23)
|
||||
.background(
|
||||
viewModel.price == price ?
|
||||
Color(hex:"9970ff").opacity(0.3):
|
||||
Color(hex: "232323")
|
||||
)
|
||||
.cornerRadius(6.7)
|
||||
.onTapGesture {
|
||||
hideKeyboard()
|
||||
|
|
|
@ -9,19 +9,13 @@ import UIKit
|
|||
import Moya
|
||||
import Combine
|
||||
|
||||
enum SelectedMenu: Int {
|
||||
case MENU_1 = 0
|
||||
case MENU_2 = 1
|
||||
case MENU_3 = 2
|
||||
}
|
||||
|
||||
final class LiveRoomCreateViewModel: ObservableObject {
|
||||
enum TimeSettingMode: String {
|
||||
case NOW, RESERVATION
|
||||
}
|
||||
|
||||
enum LiveRoomType: String, Codable {
|
||||
case OPEN, PRIVATE
|
||||
case OPEN, PRIVATE, SECRET
|
||||
}
|
||||
|
||||
let prices = [0, 100, 300, 500, 1000, 2000]
|
||||
|
@ -46,12 +40,15 @@ final class LiveRoomCreateViewModel: ObservableObject {
|
|||
|
||||
@Published var roomType: LiveRoomType = .OPEN {
|
||||
didSet {
|
||||
if roomType == .SECRET {
|
||||
timeSettingMode = .NOW
|
||||
}
|
||||
|
||||
if roomType != .PRIVATE {
|
||||
password = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var password = "" {
|
||||
didSet {
|
||||
if password.count > 6 {
|
||||
|
@ -80,14 +77,6 @@ final class LiveRoomCreateViewModel: ObservableObject {
|
|||
@Published var isShowPopup = false
|
||||
@Published var isShowGetRecentInfoButton = true
|
||||
|
||||
|
||||
private var menuId = 0
|
||||
@Published var menu = ""
|
||||
@Published var menuList = [GetMenuPresetResponse]()
|
||||
|
||||
@Published var isActivateMenu = false
|
||||
@Published var selectedMenu: SelectedMenu? = nil
|
||||
|
||||
private let repository = LiveRepository()
|
||||
private var subscription = Set<AnyCancellable>()
|
||||
|
||||
|
@ -175,13 +164,10 @@ final class LiveRoomCreateViewModel: ObservableObject {
|
|||
isAdult: isAdult,
|
||||
price: price,
|
||||
type: roomType,
|
||||
password: (roomType == .PRIVATE && !password.trimmingCharacters(in: .whitespaces).isEmpty) ? password : nil,
|
||||
menuPanId: isActivateMenu ? menuId : 0,
|
||||
menuPan: isActivateMenu ? menu : "",
|
||||
isActiveMenuPan: isActivateMenu
|
||||
password: (roomType == .PRIVATE && !password.trimmingCharacters(in: .whitespaces).isEmpty) ? password : nil
|
||||
)
|
||||
|
||||
if timeSettingMode == .RESERVATION {
|
||||
if timeSettingMode == .RESERVATION && roomType != .SECRET {
|
||||
request.beginDateTimeString = "\(reservationDate.convertDateFormat(dateFormat: "yyyy-MM-dd")) \(reservationTime.convertDateFormat(dateFormat: "HH:mm"))"
|
||||
}
|
||||
|
||||
|
@ -247,73 +233,6 @@ final class LiveRoomCreateViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func selectMenuPreset(selectedMenuPreset: SelectedMenu) {
|
||||
if menuList.isEmpty && (selectedMenuPreset == .MENU_2 || selectedMenuPreset == .MENU_3) {
|
||||
errorMessage = "메뉴 1을 먼저 설정하세요"
|
||||
isShowPopup = true
|
||||
return
|
||||
}
|
||||
|
||||
if menuList.count == 1 && selectedMenuPreset == .MENU_3 {
|
||||
errorMessage = "메뉴 1과 메뉴 2를 먼저 설정하세요"
|
||||
isShowPopup = true
|
||||
return
|
||||
}
|
||||
|
||||
if self.selectedMenu != selectedMenuPreset {
|
||||
self.selectedMenu = selectedMenuPreset
|
||||
|
||||
if menuList.count > selectedMenuPreset.rawValue {
|
||||
let menu = menuList[selectedMenuPreset.rawValue]
|
||||
self.menu = menu.menu
|
||||
self.menuId = menu.id
|
||||
} else {
|
||||
self.menu = ""
|
||||
self.menuId = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getAllMenuPreset() {
|
||||
isLoading = true
|
||||
|
||||
repository.getAllMenuPreset(creatorId: UserDefaults.int(forKey: .userId))
|
||||
.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<[GetMenuPresetResponse]>.self, from: responseData)
|
||||
|
||||
if let data = decoded.data, decoded.success {
|
||||
self.menuList.removeAll()
|
||||
self.menuList.append(contentsOf: data)
|
||||
self.selectMenuPreset(selectedMenuPreset: .MENU_1)
|
||||
} 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 validate() -> Bool {
|
||||
if coverImage == nil && coverImagePath == nil {
|
||||
self.errorMessage = "커버이미지를 선택해주세요."
|
||||
|
|
|
@ -18,7 +18,7 @@ struct LiveRoomInfoEditDialog: View {
|
|||
|
||||
let placeholder = "라이브 공지를 입력하세요"
|
||||
|
||||
@StateObject var viewModel: LiveRoomViewModel
|
||||
let viewModel: LiveRoomViewModel
|
||||
|
||||
let isLoading: Bool
|
||||
let coverImageUrl: String?
|
||||
|
@ -39,7 +39,7 @@ struct LiveRoomInfoEditDialog: View {
|
|||
self._isShowing = isShowing
|
||||
self._isShowPhotoPicker = isShowPhotoPicker
|
||||
|
||||
self._viewModel = StateObject(wrappedValue: viewModel)
|
||||
self.viewModel = viewModel
|
||||
self.isLoading = isLoading
|
||||
|
||||
self.title = currentTitle
|
||||
|
@ -53,123 +53,112 @@ struct LiveRoomInfoEditDialog: View {
|
|||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
Text("라이브 수정")
|
||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||
.foregroundColor(Color.grayee)
|
||||
GeometryReader { proxy in
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
Text("라이브 수정")
|
||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Spacer()
|
||||
|
||||
Image("ic_close_white")
|
||||
.onTapGesture {
|
||||
isShowing = false
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Image("ic_close_white")
|
||||
.onTapGesture {
|
||||
isShowing = false
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
ZStack {
|
||||
if let coverImage = coverImage {
|
||||
Image(uiImage: coverImage)
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(width: 80, height: 116.8, alignment: .top)
|
||||
.clipped()
|
||||
.cornerRadius(10)
|
||||
} else if let coverImageUrl = coverImageUrl {
|
||||
KFImage(URL(string: coverImageUrl))
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(width: 80, height: 116.8, alignment: .top)
|
||||
.clipped()
|
||||
.cornerRadius(10)
|
||||
} else {
|
||||
Image("ic_logo")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 80, height: 116.8)
|
||||
.background(Color(hex: "3e3358"))
|
||||
.cornerRadius(10)
|
||||
}
|
||||
|
||||
Image("ic_camera")
|
||||
.padding(10)
|
||||
.background(Color(hex: "9970ff"))
|
||||
.cornerRadius(30)
|
||||
.offset(x: 40, y: 40)
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
ZStack {
|
||||
if let coverImage = coverImage {
|
||||
Image(uiImage: coverImage)
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(width: 80, height: 116.8, alignment: .top)
|
||||
.clipped()
|
||||
.cornerRadius(10)
|
||||
} else if let coverImageUrl = coverImageUrl {
|
||||
KFImage(URL(string: coverImageUrl))
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(width: 80, height: 116.8, alignment: .top)
|
||||
.clipped()
|
||||
.cornerRadius(10)
|
||||
} else {
|
||||
Image("ic_logo")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 80, height: 116.8)
|
||||
.background(Color.bg)
|
||||
.cornerRadius(10)
|
||||
.frame(alignment: .bottomTrailing)
|
||||
.padding(.top, 13.3)
|
||||
.onTapGesture {
|
||||
isShowPhotoPicker = true
|
||||
}
|
||||
|
||||
Image("ic_camera")
|
||||
.padding(10)
|
||||
.background(Color.button)
|
||||
.cornerRadius(30)
|
||||
.offset(x: 40, y: 40)
|
||||
Spacer()
|
||||
}
|
||||
.frame(alignment: .bottomTrailing)
|
||||
.padding(.top, 13.3)
|
||||
.onTapGesture {
|
||||
isShowPhotoPicker = true
|
||||
|
||||
TitleInputView()
|
||||
.padding(.top, 40)
|
||||
|
||||
ContentInputView()
|
||||
.padding(.top, 33.3)
|
||||
|
||||
HStack(spacing: 13.3) {
|
||||
Text("취소")
|
||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
.padding(.vertical, 16)
|
||||
.frame(width: (screenSize().width - 40) / 2)
|
||||
.background(Color(hex: "9970ff").opacity(0.2))
|
||||
.cornerRadius(10)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.strokeBorder(lineWidth: 1)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
)
|
||||
.onTapGesture {
|
||||
isShowing = false
|
||||
}
|
||||
|
||||
Text("수정하기")
|
||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||
.foregroundColor(.white)
|
||||
.padding(.vertical, 16)
|
||||
.frame(width: (screenSize().width - 40) / 2)
|
||||
.background(Color(hex: "9970ff"))
|
||||
.cornerRadius(10)
|
||||
.onTapGesture {
|
||||
confirmAction(
|
||||
title,
|
||||
notice.trimmingCharacters(in: .whitespacesAndNewlines) != placeholder ? notice : ""
|
||||
)
|
||||
isShowing = false
|
||||
}
|
||||
}
|
||||
.padding(.top, 45)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
TitleInputView()
|
||||
.padding(.top, 40)
|
||||
|
||||
ContentInputView()
|
||||
.padding(.top, 33.3)
|
||||
|
||||
LiveRoomMenuSelectView(
|
||||
menu: $viewModel.menu,
|
||||
isActivate: $viewModel.isActivateMenu,
|
||||
menuCount: viewModel.menuList.count,
|
||||
selectedMenu: viewModel.selectedMenu,
|
||||
selectMenu: { viewModel.selectMenuPreset(selectedMenuPreset: $0) }
|
||||
)
|
||||
.padding(.top, 33.3)
|
||||
|
||||
HStack(spacing: 13.3) {
|
||||
Text("취소")
|
||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||
.foregroundColor(Color.button)
|
||||
.padding(.vertical, 16)
|
||||
.frame(width: (screenSize().width - 40) / 2)
|
||||
.background(Color.button.opacity(0.2))
|
||||
.cornerRadius(10)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.strokeBorder(lineWidth: 1)
|
||||
.foregroundColor(Color.button)
|
||||
)
|
||||
.onTapGesture {
|
||||
isShowing = false
|
||||
}
|
||||
|
||||
Text("수정하기")
|
||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||
.foregroundColor(.white)
|
||||
.padding(.vertical, 16)
|
||||
.frame(width: (screenSize().width - 40) / 2)
|
||||
.background(Color.button)
|
||||
.cornerRadius(10)
|
||||
.onTapGesture {
|
||||
confirmAction(
|
||||
title,
|
||||
notice.trimmingCharacters(in: .whitespacesAndNewlines) != placeholder ? notice : ""
|
||||
)
|
||||
isShowing = false
|
||||
}
|
||||
}
|
||||
.padding(.top, 45)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(13.3)
|
||||
.onTapGesture { hideKeyboard() }
|
||||
.onAppear {
|
||||
viewModel.getAllMenuPreset {
|
||||
self.isShowing = false
|
||||
}
|
||||
.padding(13.3)
|
||||
.frame(width: proxy.size.width, height: proxy.size.height)
|
||||
.onTapGesture { hideKeyboard() }
|
||||
}
|
||||
.background(Color(hex: "222222").edgesIgnoringSafeArea(.all))
|
||||
}
|
||||
.background(Color(hex: "222222").edgesIgnoringSafeArea(.all))
|
||||
|
||||
if viewModel.isShowPopup {
|
||||
LiveRoomDialogView(
|
||||
|
@ -204,15 +193,15 @@ struct LiveRoomInfoEditDialog: View {
|
|||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.grayee)
|
||||
.accentColor(Color.button)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.accentColor(Color(hex: "3bb9f1"))
|
||||
.keyboardType(.default)
|
||||
.padding(.top, 12)
|
||||
.padding(.horizontal, 6.7)
|
||||
|
||||
Rectangle()
|
||||
.frame(height: 1)
|
||||
.foregroundColor(Color.gray90.opacity(0.7))
|
||||
.foregroundColor(Color(hex: "909090").opacity(0.7))
|
||||
.padding(.top, 8.3)
|
||||
}
|
||||
}
|
||||
|
@ -223,16 +212,16 @@ struct LiveRoomInfoEditDialog: View {
|
|||
HStack(spacing: 0) {
|
||||
Text("공지")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color.grayee)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("\(notice.count)자")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.mainRed) +
|
||||
.foregroundColor(Color(hex: "ff5c49")) +
|
||||
Text(" / 1000자")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color.gray77)
|
||||
.foregroundColor(Color(hex: "777777"))
|
||||
}
|
||||
|
||||
TextViewWrapper(
|
||||
|
|
|
@ -13,7 +13,4 @@ struct EditLiveRoomInfoRequest: Encodable {
|
|||
let numberOfPeople: Int?
|
||||
let beginDateTimeString: String?
|
||||
let timezone: String?
|
||||
var menuPanId: Int = 0
|
||||
var menuPan: String = ""
|
||||
var isActiveMenuPan: Bool? = nil
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ struct GetRoomInfoResponse: Decodable {
|
|||
let listenerList: [LiveRoomMember]
|
||||
let managerList: [LiveRoomMember]
|
||||
let donationRankingTop3UserIds: [Int]
|
||||
let menuPan: String
|
||||
let isActiveRoulette: Bool
|
||||
let isPrivateRoom: Bool
|
||||
let password: String?
|
||||
|
|
|
@ -67,21 +67,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
}
|
||||
@Published var selectedProfile: LiveRoomMember?
|
||||
|
||||
@Published var isShowNotice = false {
|
||||
didSet {
|
||||
if isShowNotice {
|
||||
isShowMenuPan = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var isShowMenuPan = false {
|
||||
didSet {
|
||||
if isShowMenuPan {
|
||||
isShowNotice = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@Published var isShowNotice = false
|
||||
|
||||
@Published var isShowDonationPopup = false
|
||||
|
||||
|
@ -157,38 +143,6 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
@Published var rouletteSelectedItem = ""
|
||||
var rouletteCan = 0
|
||||
|
||||
@Published var signatureImageUrl = "" {
|
||||
didSet {
|
||||
if signatureImageUrl.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 7) {
|
||||
if let imageUrl = self.signatureImageUrls.first {
|
||||
self.signatureImageUrl = imageUrl
|
||||
self.signatureImageUrls.removeFirst()
|
||||
} else {
|
||||
self.signatureImageUrl = ""
|
||||
self.isShowSignatureImage = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var menuId = 0
|
||||
@Published var menu = ""
|
||||
@Published var menuList = [GetMenuPresetResponse]()
|
||||
|
||||
@Published var isActivateMenu = false {
|
||||
didSet {
|
||||
if !isActivateMenu {
|
||||
menu = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@Published var selectedMenu: SelectedMenu? = nil
|
||||
|
||||
var signatureImageUrls = [String]()
|
||||
var isShowSignatureImage = false
|
||||
|
||||
var timer: DispatchSourceTimer?
|
||||
|
||||
func setOriginOffset(_ offset: CGFloat) {
|
||||
|
@ -399,7 +353,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
|
||||
do {
|
||||
let jsonDecoder = JSONDecoder()
|
||||
let decoded = try jsonDecoder.decode(ApiResponse<String>.self, from: responseData)
|
||||
let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
|
||||
|
||||
self.isLoading = false
|
||||
|
||||
|
@ -409,7 +363,6 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
type: .DONATION,
|
||||
message: rawMessage,
|
||||
can: can,
|
||||
signatureImageUrl: decoded.data,
|
||||
donationMessage: message
|
||||
)
|
||||
|
||||
|
@ -431,7 +384,6 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
)
|
||||
|
||||
totalDonationCan += can
|
||||
showSignatureImage(imageUrl: decoded.data ?? "")
|
||||
|
||||
self.messageChangeFlag.toggle()
|
||||
if self.messages.count > 100 {
|
||||
|
@ -647,13 +599,10 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
notice: liveRoomInfo!.notice != notice ? notice : nil,
|
||||
numberOfPeople: nil,
|
||||
beginDateTimeString: nil,
|
||||
timezone: nil,
|
||||
menuPanId: isActivateMenu ? menuId : 0,
|
||||
menuPan: isActivateMenu ? menu : "",
|
||||
isActiveMenuPan: isActivateMenu
|
||||
timezone: nil
|
||||
)
|
||||
|
||||
if (request.title == nil && request.notice == nil && coverImage == nil && menu == liveRoomInfo?.menuPan) {
|
||||
if (request.title == nil && request.notice == nil && coverImage == nil) {
|
||||
self.errorMessage = "변경사항이 없습니다."
|
||||
self.isShowErrorPopup = true
|
||||
return
|
||||
|
@ -664,7 +613,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
let encoder = JSONEncoder()
|
||||
encoder.outputFormatting = .withoutEscapingSlashes
|
||||
|
||||
if (request.title != nil || request.notice != nil || menu != liveRoomInfo?.menuPan) {
|
||||
if (request.title != nil || request.notice != nil) {
|
||||
let jsonData = try? encoder.encode(request)
|
||||
if let jsonData = jsonData {
|
||||
multipartData.append(MultipartFormData(provider: .data(jsonData), name: "request"))
|
||||
|
@ -681,6 +630,8 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
repository.editLiveRoomInfo(roomId: AppState.shared.roomId, parameters: multipartData)
|
||||
.sink { result in
|
||||
switch result {
|
||||
|
@ -698,11 +649,6 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
|
||||
if decoded.success {
|
||||
self.coverImage = nil
|
||||
self.menuList.removeAll()
|
||||
self.isActivateMenu = false
|
||||
self.selectedMenu = nil
|
||||
self.menu = ""
|
||||
self.isShowMenuPan = false
|
||||
self.getRoomInfo()
|
||||
|
||||
let editRoomInfoMessage = LiveRoomChatRawMessage(
|
||||
|
@ -713,102 +659,12 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
)
|
||||
|
||||
self.agora.sendRawMessageToGroup(rawMessage: editRoomInfoMessage)
|
||||
self.errorMessage = "라이브 정보가 수정되었습니다."
|
||||
self.isShowErrorPopup = true
|
||||
} else {
|
||||
self.errorMessage = decoded.message ?? "라이브 정보를 수정하지 못했습니다.\n다시 시도해 주세요."
|
||||
self.isShowErrorPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "라이브 정보를 수정하지 못했습니다.\n다시 시도해 주세요."
|
||||
self.isShowErrorPopup = true
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
|
||||
func selectMenuPreset(selectedMenuPreset: SelectedMenu) {
|
||||
if menuList.isEmpty && (selectedMenuPreset == .MENU_2 || selectedMenuPreset == .MENU_3) {
|
||||
errorMessage = "메뉴 1을 먼저 설정하세요"
|
||||
isShowPopup = true
|
||||
return
|
||||
}
|
||||
|
||||
if menuList.count == 1 && selectedMenuPreset == .MENU_3 {
|
||||
errorMessage = "메뉴 1과 메뉴 2를 먼저 설정하세요"
|
||||
isShowPopup = true
|
||||
return
|
||||
}
|
||||
|
||||
if self.selectedMenu != selectedMenuPreset {
|
||||
self.selectedMenu = selectedMenuPreset
|
||||
|
||||
if menuList.count > selectedMenuPreset.rawValue {
|
||||
let menu = menuList[selectedMenuPreset.rawValue]
|
||||
self.menu = menu.menu
|
||||
self.menuId = menu.id
|
||||
} else {
|
||||
self.menu = ""
|
||||
self.menuId = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getAllMenuPreset(onFailure: @escaping () -> Void) {
|
||||
isLoading = true
|
||||
|
||||
repository.getAllMenuPreset(creatorId: UserDefaults.int(forKey: .userId))
|
||||
.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<[GetMenuPresetResponse]>.self, from: responseData)
|
||||
|
||||
if let data = decoded.data, decoded.success {
|
||||
self.menuList.removeAll()
|
||||
self.menuList.append(contentsOf: data)
|
||||
self.isActivateMenu = false
|
||||
self.selectedMenu = nil
|
||||
|
||||
for (index, menuPreset) in self.menuList.enumerated() {
|
||||
if menuPreset.isActive {
|
||||
switch index {
|
||||
case 1:
|
||||
self.selectMenuPreset(selectedMenuPreset: .MENU_2)
|
||||
|
||||
case 2:
|
||||
self.selectMenuPreset(selectedMenuPreset: .MENU_3)
|
||||
|
||||
default:
|
||||
self.selectMenuPreset(selectedMenuPreset: .MENU_1)
|
||||
}
|
||||
|
||||
self.isActivateMenu = true
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
onFailure()
|
||||
if let message = decoded.message {
|
||||
self.errorMessage = message
|
||||
} else {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
}
|
||||
|
||||
self.isShowPopup = true
|
||||
}
|
||||
} catch {
|
||||
onFailure()
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.errorMessage = "라이브 정보를 수정하지 못했습니다.\n다시 시도해 주세요."
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
|
@ -1485,8 +1341,8 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func setActiveRoulette(isActiveRoulette: Bool, message: String) {
|
||||
self.popupContent = message
|
||||
func setActiveRoulette(isActiveRoulette: Bool) {
|
||||
self.popupContent = isActiveRoulette ? "룰렛을 활성화 했습니다." : "룰렛을 비활성화 했습니다."
|
||||
self.isShowPopup = true
|
||||
self.agora.sendRawMessageToGroup(
|
||||
rawMessage: LiveRoomChatRawMessage(
|
||||
|
@ -1688,17 +1544,6 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
|
||||
private func showSignatureImage(imageUrl: String) {
|
||||
if imageUrl.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 {
|
||||
if !isShowSignatureImage {
|
||||
isShowSignatureImage = true
|
||||
signatureImageUrl = imageUrl
|
||||
} else {
|
||||
signatureImageUrls.append(imageUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension LiveRoomViewModel: AgoraRtcEngineDelegate {
|
||||
|
@ -1863,7 +1708,6 @@ extension LiveRoomViewModel: AgoraRtmChannelDelegate {
|
|||
)
|
||||
|
||||
self.totalDonationCan += decoded.can
|
||||
self.showSignatureImage(imageUrl: decoded.signatureImageUrl ?? "")
|
||||
} else if decoded.type == .ROULETTE_DONATION {
|
||||
self.messages.append(
|
||||
LiveRoomRouletteDonationChat(
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
//
|
||||
// GetMenuPresetResponse.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 3/8/24.
|
||||
//
|
||||
|
||||
struct GetMenuPresetResponse: Decodable {
|
||||
let id: Int
|
||||
let menu: String
|
||||
let isActive: Bool
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
//
|
||||
// LiveRoomMenuSelectView.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 3/8/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LiveRoomMenuSelectView: View {
|
||||
|
||||
@Binding var menu: String
|
||||
@Binding var isActivate: Bool
|
||||
|
||||
let menuCount: Int
|
||||
let selectedMenu: SelectedMenu?
|
||||
let selectMenu: (SelectedMenu) -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text("메뉴")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
HStack(spacing: 0) {
|
||||
Text("메뉴를 활성화 하시겠습니까?")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(isActivate ? "btn_toggle_on_big" : "btn_toggle_off_big")
|
||||
.resizable()
|
||||
.frame(width: 33.3, height: 20)
|
||||
.onTapGesture {
|
||||
isActivate.toggle()
|
||||
selectMenu(.MENU_1)
|
||||
}
|
||||
}
|
||||
.padding(.top, 8)
|
||||
|
||||
if isActivate {
|
||||
VStack(spacing: 13.3) {
|
||||
HStack(spacing: 13.3) {
|
||||
SelectedButtonView(
|
||||
title: "메뉴 1",
|
||||
isActive: true,
|
||||
isSelected: selectedMenu == .MENU_1
|
||||
)
|
||||
.onTapGesture {
|
||||
selectMenu(.MENU_1)
|
||||
}
|
||||
|
||||
SelectedButtonView(
|
||||
title: "메뉴 2",
|
||||
isActive: menuCount > 0,
|
||||
isSelected: selectedMenu == .MENU_2
|
||||
)
|
||||
.onTapGesture {
|
||||
selectMenu(.MENU_2)
|
||||
}
|
||||
|
||||
SelectedButtonView(
|
||||
title: "메뉴 3",
|
||||
isActive: menuCount > 1,
|
||||
isSelected: selectedMenu == .MENU_3
|
||||
)
|
||||
.onTapGesture {
|
||||
selectMenu(.MENU_3)
|
||||
}
|
||||
}
|
||||
|
||||
TextViewWrapper(
|
||||
text: $menu,
|
||||
placeholder: "메뉴판을 작성해주세요.",
|
||||
textColorHex: "eeeeee",
|
||||
backgroundColorHex: "303030"
|
||||
)
|
||||
.frame(height: 200)
|
||||
.cornerRadius(6.7)
|
||||
}
|
||||
.padding(.top, 13.3)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
LiveRoomMenuSelectView(
|
||||
menu: .constant("메뉴 1 입니다\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n테스트"),
|
||||
isActivate: .constant(true),
|
||||
menuCount: 2,
|
||||
selectedMenu: .MENU_1,
|
||||
selectMenu: { _ in }
|
||||
)
|
||||
}
|
|
@ -7,14 +7,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
struct CreateRouletteRequest: Encodable {
|
||||
let can: Int
|
||||
let isActive: Bool
|
||||
let items: [RouletteItem]
|
||||
}
|
||||
|
||||
struct UpdateRouletteRequest: Encodable {
|
||||
let id: Int
|
||||
struct CreateOrUpdateRouletteRequest: Encodable {
|
||||
let can: Int
|
||||
let isActive: Bool
|
||||
let items: [RouletteItem]
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
//
|
||||
// GetNewRouletteResponse.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 2/23/24.
|
||||
//
|
||||
|
||||
struct GetNewRouletteResponse: Decodable {
|
||||
let id: Int
|
||||
let can: Int
|
||||
let isActive: Bool
|
||||
let items: [RouletteItem]
|
||||
}
|
|
@ -13,7 +13,7 @@ struct RouletteSettingsView: View {
|
|||
@StateObject var viewModel = RouletteSettingsViewModel()
|
||||
|
||||
@Binding var isShowing: Bool
|
||||
let onComplete: (Bool, String) -> Void
|
||||
let onComplete: (Bool) -> Void
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { proxy in
|
||||
|
@ -25,36 +25,6 @@ struct RouletteSettingsView: View {
|
|||
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 13.3) {
|
||||
SelectedButtonView(
|
||||
title: "룰렛 1",
|
||||
isActive: true,
|
||||
isSelected: viewModel.selectedRoulette == .ROULETTE_1
|
||||
)
|
||||
.onTapGesture {
|
||||
viewModel.selectRoulette(selectedRoulette: .ROULETTE_1)
|
||||
}
|
||||
|
||||
SelectedButtonView(
|
||||
title: "룰렛 2",
|
||||
isActive: viewModel.rouletteList.count > 0,
|
||||
isSelected: viewModel.selectedRoulette == .ROULETTE_2
|
||||
)
|
||||
.onTapGesture {
|
||||
viewModel.selectRoulette(selectedRoulette: .ROULETTE_2)
|
||||
}
|
||||
|
||||
SelectedButtonView(
|
||||
title: "룰렛 3",
|
||||
isActive: viewModel.rouletteList.count > 1,
|
||||
isSelected: viewModel.selectedRoulette == .ROULETTE_3
|
||||
)
|
||||
.onTapGesture {
|
||||
viewModel.selectRoulette(selectedRoulette: .ROULETTE_3)
|
||||
}
|
||||
}
|
||||
.padding(.top, 26.7)
|
||||
|
||||
HStack(spacing: 0) {
|
||||
Text("룰렛을 활성화 하시겠습니까?")
|
||||
.font(.custom(Font.bold.rawValue, size: 16))
|
||||
|
@ -69,7 +39,6 @@ struct RouletteSettingsView: View {
|
|||
viewModel.isActive.toggle()
|
||||
}
|
||||
}
|
||||
.padding(.top, 26.7)
|
||||
|
||||
VStack(alignment: .leading, spacing: 13.3) {
|
||||
Text("룰렛 금액 설정")
|
||||
|
@ -166,7 +135,7 @@ struct RouletteSettingsView: View {
|
|||
.cornerRadius(10)
|
||||
.onTapGesture {
|
||||
viewModel.createOrUpdateRoulette {
|
||||
onComplete($0, $1)
|
||||
onComplete($0)
|
||||
isShowing = false
|
||||
}
|
||||
}
|
||||
|
@ -212,7 +181,7 @@ struct RouletteSettingsView: View {
|
|||
}
|
||||
}
|
||||
.onAppear {
|
||||
viewModel.getAllRoulette(creatorId: UserDefaults.int(forKey: .userId))
|
||||
viewModel.getRoulette(creatorId: UserDefaults.int(forKey: .userId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,6 +189,6 @@ struct RouletteSettingsView: View {
|
|||
|
||||
struct RouletteSettingsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
RouletteSettingsView(isShowing: .constant(true)) { _, _ in }
|
||||
RouletteSettingsView(isShowing: .constant(true)) { _ in }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,6 @@
|
|||
import Foundation
|
||||
import Combine
|
||||
|
||||
enum SelectedRoulette: Int {
|
||||
case ROULETTE_1 = 0
|
||||
case ROULETTE_2 = 1
|
||||
case ROULETTE_3 = 2
|
||||
}
|
||||
|
||||
final class RouletteSettingsViewModel: ObservableObject {
|
||||
private let repository = RouletteRepository()
|
||||
private var subscription = Set<AnyCancellable>()
|
||||
|
@ -40,11 +34,7 @@ final class RouletteSettingsViewModel: ObservableObject {
|
|||
}
|
||||
@Published var previewData: RoulettePreview? = nil
|
||||
|
||||
@Published var selectedRoulette: SelectedRoulette? = nil
|
||||
|
||||
var can = 0
|
||||
private var rouletteId = 0
|
||||
@Published var rouletteList = [GetNewRouletteResponse]()
|
||||
|
||||
func plusWeight(index: Int) {
|
||||
options[index].weight += 1
|
||||
|
@ -94,9 +84,9 @@ final class RouletteSettingsViewModel: ObservableObject {
|
|||
self.options.append(contentsOf: options)
|
||||
}
|
||||
|
||||
func getAllRoulette(creatorId: Int) {
|
||||
func getRoulette(creatorId: Int) {
|
||||
self.isLoading = true
|
||||
repository.getAllRoulette(creatorId: creatorId)
|
||||
repository.getRoulette(creatorId: creatorId)
|
||||
.sink { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
|
@ -110,15 +100,32 @@ final class RouletteSettingsViewModel: ObservableObject {
|
|||
|
||||
do {
|
||||
let jsonDecoder = JSONDecoder()
|
||||
let decoded = try jsonDecoder.decode(ApiResponse<[GetNewRouletteResponse]>.self, from: responseData)
|
||||
let decoded = try jsonDecoder.decode(ApiResponse<GetRouletteResponse>.self, from: responseData)
|
||||
|
||||
if let data = decoded.data, decoded.success {
|
||||
rouletteList.removeAll()
|
||||
rouletteList.append(contentsOf: data)
|
||||
selectRoulette(selectedRoulette: .ROULETTE_1)
|
||||
self.isActive = data.isActive
|
||||
|
||||
if data.can > 0 {
|
||||
self.canText = String(data.can)
|
||||
} else {
|
||||
self.canText = ""
|
||||
}
|
||||
|
||||
if !data.items.isEmpty {
|
||||
let options = data.items.map {
|
||||
RouletteOption(title: $0.title, weight: $0.weight)
|
||||
}
|
||||
removeAllAndAddOptions(options: options)
|
||||
recalculatePercentages()
|
||||
} else {
|
||||
self.addOption()
|
||||
self.addOption()
|
||||
}
|
||||
} else {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.isShowErrorPopup = true
|
||||
self.isActive = false
|
||||
self.canText = ""
|
||||
self.addOption()
|
||||
self.addOption()
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
|
@ -148,208 +155,51 @@ final class RouletteSettingsViewModel: ObservableObject {
|
|||
isShowPreview = true
|
||||
}
|
||||
|
||||
func createOrUpdateRoulette(onSuccess: @escaping (Bool, String) -> Void) {
|
||||
func createOrUpdateRoulette(onSuccess: @escaping (Bool) -> Void) {
|
||||
if !isLoading {
|
||||
isLoading = true
|
||||
|
||||
if rouletteId > 0 {
|
||||
updateRoulette(onSuccess: onSuccess)
|
||||
} else {
|
||||
createRoulette(onSuccess: onSuccess)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func createRoulette(onSuccess: @escaping (Bool, String) -> Void) {
|
||||
var items = [RouletteItem]()
|
||||
for option in options {
|
||||
if option.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
isLoading = false
|
||||
errorMessage = "옵션은 빈칸일 수 없습니다."
|
||||
isShowErrorPopup = true
|
||||
return
|
||||
}
|
||||
|
||||
items.append(RouletteItem(title: option.title, weight: option.weight))
|
||||
}
|
||||
|
||||
let selectedRouletteTitle: String
|
||||
let successMessage: String
|
||||
|
||||
switch (self.selectedRoulette) {
|
||||
case .ROULETTE_2:
|
||||
selectedRouletteTitle = "룰렛 2"
|
||||
|
||||
case .ROULETTE_3:
|
||||
selectedRouletteTitle = "룰렛 3"
|
||||
|
||||
default:
|
||||
selectedRouletteTitle = "룰렛 1"
|
||||
}
|
||||
|
||||
if isActive {
|
||||
successMessage = "\(selectedRouletteTitle)로 설정하였습니다."
|
||||
} else {
|
||||
successMessage = "\(selectedRouletteTitle)을 설정했습니다."
|
||||
}
|
||||
|
||||
let request = CreateRouletteRequest(can: can, isActive: isActive, items: items)
|
||||
repository.createRoulette(request: request)
|
||||
.sink { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
DEBUG_LOG("finish")
|
||||
case .failure(let error):
|
||||
ERROR_LOG(error.localizedDescription)
|
||||
var items = [RouletteItem]()
|
||||
for option in options {
|
||||
if option.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
isLoading = false
|
||||
errorMessage = "옵션은 빈칸일 수 없습니다."
|
||||
isShowErrorPopup = true
|
||||
return
|
||||
}
|
||||
} 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)
|
||||
items.append(RouletteItem(title: option.title, weight: option.weight))
|
||||
}
|
||||
|
||||
let request = CreateOrUpdateRouletteRequest(can: can, isActive: isActive, items: items)
|
||||
repository.createOrUpdateRoulette(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
|
||||
|
||||
if decoded.success {
|
||||
onSuccess(isActive, successMessage)
|
||||
} else {
|
||||
do {
|
||||
let jsonDecoder = JSONDecoder()
|
||||
let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
|
||||
|
||||
if decoded.success {
|
||||
onSuccess(isActive)
|
||||
} else {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.isShowErrorPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.isShowErrorPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.isShowErrorPopup = true
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
|
||||
private func updateRoulette(onSuccess: @escaping (Bool, String) -> Void) {
|
||||
var items = [RouletteItem]()
|
||||
for option in options {
|
||||
if option.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
isLoading = false
|
||||
errorMessage = "옵션은 빈칸일 수 없습니다."
|
||||
isShowErrorPopup = true
|
||||
return
|
||||
}
|
||||
|
||||
items.append(RouletteItem(title: option.title, weight: option.weight))
|
||||
}
|
||||
|
||||
let selectedRoulette = rouletteList[selectedRoulette!.rawValue]
|
||||
if selectedRoulette.isActive == isActive && selectedRoulette.can == can && selectedRoulette.items == items {
|
||||
self.errorMessage = "변동사항이 없습니다."
|
||||
self.isShowErrorPopup = true
|
||||
self.isLoading = false
|
||||
return
|
||||
}
|
||||
|
||||
let selectedRouletteTitle: String
|
||||
let successMessage: String
|
||||
|
||||
switch (self.selectedRoulette) {
|
||||
case .ROULETTE_2:
|
||||
selectedRouletteTitle = "룰렛 2"
|
||||
|
||||
case .ROULETTE_3:
|
||||
selectedRouletteTitle = "룰렛 3"
|
||||
|
||||
default:
|
||||
selectedRouletteTitle = "룰렛 1"
|
||||
}
|
||||
|
||||
var isAllActive = false
|
||||
|
||||
rouletteList
|
||||
.filter {
|
||||
$0.id != selectedRoulette.id
|
||||
}
|
||||
.forEach {
|
||||
if $0.isActive {
|
||||
isAllActive = true
|
||||
}
|
||||
}
|
||||
|
||||
if isActive {
|
||||
successMessage = "\(selectedRouletteTitle)로 설정하였습니다."
|
||||
} else if !isAllActive {
|
||||
successMessage = "\(selectedRouletteTitle)이 비활성화 되었습니다."
|
||||
} else {
|
||||
successMessage = "\(selectedRouletteTitle)을 설정했습니다."
|
||||
}
|
||||
|
||||
let request = UpdateRouletteRequest(id: rouletteId, can: can, isActive: isActive, items: items)
|
||||
repository.updateRoulette(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 {
|
||||
onSuccess(isActive, successMessage)
|
||||
} else {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.isShowErrorPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.isShowErrorPopup = true
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
|
||||
func selectRoulette(selectedRoulette: SelectedRoulette) {
|
||||
if rouletteList.isEmpty && (selectedRoulette == .ROULETTE_2 || selectedRoulette == .ROULETTE_3) {
|
||||
errorMessage = "룰렛 1을 먼저 설정하세요"
|
||||
isShowErrorPopup = true
|
||||
return
|
||||
}
|
||||
|
||||
if rouletteList.count == 1 && selectedRoulette == .ROULETTE_3 {
|
||||
errorMessage = "룰렛 1과 룰렛 2를 먼저 설정하세요"
|
||||
isShowErrorPopup = true
|
||||
return
|
||||
}
|
||||
|
||||
if self.selectedRoulette != selectedRoulette {
|
||||
self.selectedRoulette = selectedRoulette
|
||||
|
||||
if rouletteList.count > selectedRoulette.rawValue {
|
||||
let roulette = rouletteList[selectedRoulette.rawValue]
|
||||
if roulette.can > 0 {
|
||||
self.canText = String(roulette.can)
|
||||
} else {
|
||||
self.canText = ""
|
||||
}
|
||||
|
||||
self.rouletteId = roulette.id
|
||||
self.isActive = roulette.isActive
|
||||
let options = roulette.items.map {
|
||||
RouletteOption(title: $0.title, weight: $0.weight)
|
||||
}
|
||||
removeAllAndAddOptions(options: options)
|
||||
recalculatePercentages()
|
||||
} else {
|
||||
self.canText = ""
|
||||
self.isActive = false
|
||||
self.rouletteId = 0
|
||||
|
||||
options.removeAll()
|
||||
self.addOption()
|
||||
self.addOption()
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ struct GetRouletteResponse: Decodable {
|
|||
let items: [RouletteItem]
|
||||
}
|
||||
|
||||
struct RouletteItem: Codable, Equatable {
|
||||
struct RouletteItem: Codable {
|
||||
let title: String
|
||||
let weight: Int
|
||||
}
|
||||
|
|
|
@ -10,9 +10,7 @@ import Moya
|
|||
|
||||
enum RouletteApi {
|
||||
case getRoulette(creatorId: Int)
|
||||
case getAllRoulette(creatorId: Int)
|
||||
case createRoulette(request: CreateRouletteRequest)
|
||||
case updateRoulette(request: UpdateRouletteRequest)
|
||||
case createOrUpdateRoulette(request: CreateOrUpdateRouletteRequest)
|
||||
case spinRoulette(request: SpinRouletteRequest)
|
||||
case refundRouletteDonation(roomId: Int)
|
||||
}
|
||||
|
@ -24,30 +22,24 @@ extension RouletteApi: TargetType {
|
|||
|
||||
var path: String {
|
||||
switch self {
|
||||
case .getRoulette, .createRoulette, .updateRoulette:
|
||||
return "/new-roulette"
|
||||
|
||||
case .getAllRoulette:
|
||||
return "/new-roulette/creator"
|
||||
case .getRoulette, .createOrUpdateRoulette:
|
||||
return "/roulette"
|
||||
|
||||
case .spinRoulette:
|
||||
return "/new-roulette/spin"
|
||||
return "/roulette/spin"
|
||||
|
||||
case .refundRouletteDonation(let roomId):
|
||||
return "/new-roulette/refund/\(roomId)"
|
||||
return "/roulette/refund/\(roomId)"
|
||||
}
|
||||
}
|
||||
|
||||
var method: Moya.Method {
|
||||
switch self {
|
||||
case .getRoulette, .getAllRoulette:
|
||||
case .getRoulette:
|
||||
return .get
|
||||
|
||||
case .createRoulette, .spinRoulette, .refundRouletteDonation:
|
||||
case .createOrUpdateRoulette, .spinRoulette, .refundRouletteDonation:
|
||||
return .post
|
||||
|
||||
case .updateRoulette:
|
||||
return .put
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,20 +55,7 @@ extension RouletteApi: TargetType {
|
|||
encoding: URLEncoding.queryString
|
||||
)
|
||||
|
||||
case .getAllRoulette(let creatorId):
|
||||
let parameters = [
|
||||
"creatorId": creatorId
|
||||
] as [String : Any]
|
||||
|
||||
return .requestParameters(
|
||||
parameters: parameters,
|
||||
encoding: URLEncoding.queryString
|
||||
)
|
||||
|
||||
case .createRoulette(let request):
|
||||
return .requestJSONEncodable(request)
|
||||
|
||||
case .updateRoulette(let request):
|
||||
case .createOrUpdateRoulette(let request):
|
||||
return .requestJSONEncodable(request)
|
||||
|
||||
case .spinRoulette(let request):
|
||||
|
|
|
@ -17,16 +17,8 @@ final class RouletteRepository {
|
|||
return api.requestPublisher(.getRoulette(creatorId: creatorId))
|
||||
}
|
||||
|
||||
func getAllRoulette(creatorId: Int) -> AnyPublisher<Response, MoyaError> {
|
||||
return api.requestPublisher(.getAllRoulette(creatorId: creatorId))
|
||||
}
|
||||
|
||||
func createRoulette(request: CreateRouletteRequest) -> AnyPublisher<Response, MoyaError> {
|
||||
return api.requestPublisher(.createRoulette(request: request))
|
||||
}
|
||||
|
||||
func updateRoulette(request: UpdateRouletteRequest) -> AnyPublisher<Response, MoyaError> {
|
||||
return api.requestPublisher(.updateRoulette(request: request))
|
||||
func createOrUpdateRoulette(request: CreateOrUpdateRouletteRequest) -> AnyPublisher<Response, MoyaError> {
|
||||
return api.requestPublisher(.createOrUpdateRoulette(request: request))
|
||||
}
|
||||
|
||||
func spinRoulette(request: SpinRouletteRequest) -> AnyPublisher<Response, MoyaError> {
|
||||
|
|
|
@ -20,7 +20,7 @@ struct LiveRoomOverlayStrokeTextButton: View {
|
|||
var body: some View {
|
||||
Text(text)
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(textColor)
|
||||
.foregroundColor(Color.red)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 6)
|
||||
.overlay(
|
||||
|
|
|
@ -63,17 +63,18 @@ struct LiveRoomInfoCreatorView: View {
|
|||
.lineLimit(1)
|
||||
}
|
||||
|
||||
HStack(spacing: 5.3) {
|
||||
Text(creatorNickname)
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(.gray77)
|
||||
|
||||
if isShowFollowingButton {
|
||||
Image(isFollowing ? "btn_following" : "btn_follow")
|
||||
.onTapGesture { onClickFollow() }
|
||||
}
|
||||
}
|
||||
Text(creatorNickname)
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.foregroundColor(.gray77)
|
||||
}
|
||||
|
||||
if isShowFollowingButton {
|
||||
Image(isFollowing ? "btn_select_checked" : "btn_plus_round")
|
||||
.resizable()
|
||||
.frame(width: 20, height: 20)
|
||||
.onTapGesture { onClickFollow() }
|
||||
}
|
||||
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 5.3)
|
||||
|
|
|
@ -14,8 +14,6 @@ struct LiveRoomInfoGuestView: View {
|
|||
|
||||
let isOnBg: Bool
|
||||
let isOnNotice: Bool
|
||||
let isOnMenuPan: Bool
|
||||
let isShowMenuPanButton: Bool
|
||||
|
||||
let creatorId: Int
|
||||
let creatorNickname: String
|
||||
|
@ -33,9 +31,7 @@ struct LiveRoomInfoGuestView: View {
|
|||
let onClickFollow: (Bool) -> Void
|
||||
let onClickProfile: (Int) -> Void
|
||||
let onClickNotice: () -> Void
|
||||
let onClickMenuPan: () -> Void
|
||||
let onClickTotalDonation: () -> Void
|
||||
let onClickChangeListener: () -> Void
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
|
@ -51,16 +47,6 @@ struct LiveRoomInfoGuestView: View {
|
|||
|
||||
Spacer()
|
||||
|
||||
if speakerList.contains(where: { $0.id == UserDefaults.int(forKey: .userId)}) {
|
||||
LiveRoomOverlayStrokeTextButton(
|
||||
text: "리스너 변경",
|
||||
textColor: Color.grayee,
|
||||
strokeColor: Color.graybb,
|
||||
strokeWidth: 1,
|
||||
strokeCornerRadius: 5.3
|
||||
) { onClickChangeListener() }
|
||||
}
|
||||
|
||||
LiveRoomOverlayStrokeTextToggleButton(
|
||||
isOn: isOnBg,
|
||||
onText: "배경 ON",
|
||||
|
@ -127,21 +113,6 @@ struct LiveRoomInfoGuestView: View {
|
|||
onClick: { onClickNotice() }
|
||||
)
|
||||
|
||||
if isShowMenuPanButton {
|
||||
LiveRoomOverlayStrokeTextToggleButton(
|
||||
isOn: isOnMenuPan,
|
||||
onText: "메뉴판",
|
||||
onTextColor: .button,
|
||||
onStrokeColor: .button,
|
||||
offText: nil,
|
||||
offTextColor: .graybb,
|
||||
offStrokeColor: .graybb,
|
||||
strokeWidth: 1,
|
||||
strokeCornerRadius: 5.3,
|
||||
onClick: { onClickMenuPan() }
|
||||
)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack(spacing: 2.7) {
|
||||
|
@ -180,8 +151,6 @@ struct LiveRoomInfoGuestView_Previews: PreviewProvider {
|
|||
totalDonationCan: 123456,
|
||||
isOnBg: true,
|
||||
isOnNotice: false,
|
||||
isOnMenuPan: false,
|
||||
isShowMenuPanButton: false,
|
||||
creatorId: 1,
|
||||
creatorNickname: "도화",
|
||||
creatorProfileUrl: "https://cf.sodalive.net/profile/26/26-profile-ddf78b4d-0300-4c50-9c84-5d8a95fd5fe2-4892-1705256364320",
|
||||
|
@ -215,9 +184,7 @@ struct LiveRoomInfoGuestView_Previews: PreviewProvider {
|
|||
onClickFollow: { _ in },
|
||||
onClickProfile: { _ in },
|
||||
onClickNotice: {},
|
||||
onClickMenuPan: {},
|
||||
onClickTotalDonation: {},
|
||||
onClickChangeListener: {}
|
||||
onClickTotalDonation: {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@ struct LiveRoomInfoHostView: View {
|
|||
|
||||
let isOnBg: Bool
|
||||
let isOnNotice: Bool
|
||||
let isOnMenuPan: Bool
|
||||
let isShowMenuPanButton: Bool
|
||||
|
||||
let creatorId: Int
|
||||
let creatorNickname: String
|
||||
|
@ -34,7 +32,6 @@ struct LiveRoomInfoHostView: View {
|
|||
let onClickEdit: () -> Void
|
||||
let onClickProfile: (Int) -> Void
|
||||
let onClickNotice: () -> Void
|
||||
let onClickMenuPan: () -> Void
|
||||
let onClickTotalDonation: () -> Void
|
||||
let onClickParticipants: () -> Void
|
||||
|
||||
|
@ -125,21 +122,6 @@ struct LiveRoomInfoHostView: View {
|
|||
onClick: { onClickNotice() }
|
||||
)
|
||||
|
||||
if isShowMenuPanButton {
|
||||
LiveRoomOverlayStrokeTextToggleButton(
|
||||
isOn: isOnMenuPan,
|
||||
onText: "메뉴판",
|
||||
onTextColor: .button,
|
||||
onStrokeColor: .button,
|
||||
offText: nil,
|
||||
offTextColor: .graybb,
|
||||
offStrokeColor: .graybb,
|
||||
strokeWidth: 1,
|
||||
strokeCornerRadius: 5.3,
|
||||
onClick: { onClickMenuPan() }
|
||||
)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack(spacing: 2.7) {
|
||||
|
@ -196,8 +178,6 @@ struct LiveRoomInfoHostView_Previews: PreviewProvider {
|
|||
participantsCount: 18,
|
||||
isOnBg: true,
|
||||
isOnNotice: true,
|
||||
isOnMenuPan: false,
|
||||
isShowMenuPanButton: false,
|
||||
creatorId: 1,
|
||||
creatorNickname: "도화",
|
||||
creatorProfileUrl: "https://cf.sodalive.net/profile/26/26-profile-ddf78b4d-0300-4c50-9c84-5d8a95fd5fe2-4892-1705256364320",
|
||||
|
@ -230,7 +210,6 @@ struct LiveRoomInfoHostView_Previews: PreviewProvider {
|
|||
onClickEdit: {},
|
||||
onClickProfile: { _ in },
|
||||
onClickNotice: {},
|
||||
onClickMenuPan: {},
|
||||
onClickTotalDonation: {},
|
||||
onClickParticipants: {}
|
||||
)
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import SwiftUI
|
||||
import Kingfisher
|
||||
import SDWebImageSwiftUI
|
||||
|
||||
struct LiveRoomViewV2: View {
|
||||
|
||||
|
@ -29,8 +28,6 @@ struct LiveRoomViewV2: View {
|
|||
participantsCount: liveRoomInfo.participantsCount,
|
||||
isOnBg: viewModel.isBgOn,
|
||||
isOnNotice: viewModel.isShowNotice,
|
||||
isOnMenuPan: viewModel.isShowMenuPan,
|
||||
isShowMenuPanButton: !liveRoomInfo.menuPan.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
|
||||
creatorId: liveRoomInfo.creatorId,
|
||||
creatorNickname: liveRoomInfo.creatorNickname,
|
||||
creatorProfileUrl: liveRoomInfo.creatorProfileUrl,
|
||||
|
@ -58,9 +55,6 @@ struct LiveRoomViewV2: View {
|
|||
onClickNotice: {
|
||||
viewModel.isShowNotice.toggle()
|
||||
},
|
||||
onClickMenuPan: {
|
||||
viewModel.isShowMenuPan.toggle()
|
||||
},
|
||||
onClickTotalDonation: {
|
||||
viewModel.isShowDonationRankingPopup = true
|
||||
},
|
||||
|
@ -74,8 +68,6 @@ struct LiveRoomViewV2: View {
|
|||
totalDonationCan: viewModel.totalDonationCan,
|
||||
isOnBg: viewModel.isBgOn,
|
||||
isOnNotice: viewModel.isShowNotice,
|
||||
isOnMenuPan: viewModel.isShowMenuPan,
|
||||
isShowMenuPanButton: !liveRoomInfo.menuPan.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
|
||||
creatorId: liveRoomInfo.creatorId,
|
||||
creatorNickname: liveRoomInfo.creatorNickname,
|
||||
creatorProfileUrl: liveRoomInfo.creatorProfileUrl,
|
||||
|
@ -108,14 +100,8 @@ struct LiveRoomViewV2: View {
|
|||
onClickNotice: {
|
||||
viewModel.isShowNotice.toggle()
|
||||
},
|
||||
onClickMenuPan: {
|
||||
viewModel.isShowMenuPan.toggle()
|
||||
},
|
||||
onClickTotalDonation: {
|
||||
viewModel.isShowDonationRankingPopup = true
|
||||
},
|
||||
onClickChangeListener: {
|
||||
viewModel.setListener()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -255,20 +241,6 @@ struct LiveRoomViewV2: View {
|
|||
proxy.scrollTo(viewModel.messages.count - 1, anchor: .center)
|
||||
}.padding(.bottom, 70)
|
||||
}
|
||||
|
||||
if viewModel.signatureImageUrl.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 {
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
|
||||
AnimatedImage(url: URL(string: viewModel.signatureImageUrl))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(height: 300)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.bottom, 65)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,28 +278,6 @@ struct LiveRoomViewV2: View {
|
|||
.padding(.bottom, 120)
|
||||
}
|
||||
}
|
||||
|
||||
if viewModel.isShowMenuPan {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Image("ic_notice_triangle")
|
||||
.padding(.leading, 60)
|
||||
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("[메뉴판]")
|
||||
.font(.custom(Font.bold.rawValue, size: 11.3))
|
||||
.foregroundColor(.white)
|
||||
|
||||
Text(liveRoomInfo.menuPan)
|
||||
.font(.custom(Font.light.rawValue, size: 11.3))
|
||||
.foregroundColor(.white)
|
||||
.lineSpacing(4)
|
||||
}
|
||||
.padding(8)
|
||||
.background(Color.gray33)
|
||||
.padding(.horizontal, 60)
|
||||
.padding(.bottom, 120)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -347,9 +297,7 @@ struct LiveRoomViewV2: View {
|
|||
Spacer()
|
||||
}
|
||||
.onDisappear {
|
||||
if viewModel.liveRoomInfo == nil {
|
||||
viewModel.quitRoom()
|
||||
}
|
||||
viewModel.quitRoom()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -600,11 +548,8 @@ struct LiveRoomViewV2: View {
|
|||
}
|
||||
|
||||
if viewModel.isShowRouletteSettings {
|
||||
RouletteSettingsView(isShowing: $viewModel.isShowRouletteSettings) { isActiveRoulette, message in
|
||||
self.viewModel.setActiveRoulette(
|
||||
isActiveRoulette: isActiveRoulette,
|
||||
message: message
|
||||
)
|
||||
RouletteSettingsView(isShowing: $viewModel.isShowRouletteSettings) { isActiveRoulette in
|
||||
self.viewModel.setActiveRoulette(isActiveRoulette: isActiveRoulette)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -114,7 +114,6 @@ struct HomeView: View {
|
|||
}
|
||||
.onAppear {
|
||||
pushTokenUpdate()
|
||||
viewModel.fetchAndUpdateIdfa()
|
||||
viewModel.getMemberInfo()
|
||||
viewModel.getEventPopup()
|
||||
viewModel.addAllPlaybackTracking()
|
||||
|
|
|
@ -8,9 +8,6 @@
|
|||
import Foundation
|
||||
import Combine
|
||||
|
||||
import AppTrackingTransparency
|
||||
import AdSupport
|
||||
|
||||
final class HomeViewModel: ObservableObject {
|
||||
|
||||
private var subscription = Set<AnyCancellable>()
|
||||
|
@ -40,25 +37,6 @@ final class HomeViewModel: ObservableObject {
|
|||
.store(in: &subscription)
|
||||
}
|
||||
|
||||
func fetchAndUpdateIdfa() {
|
||||
ATTrackingManager.requestTrackingAuthorization { [unowned self] status in
|
||||
if status == .authorized {
|
||||
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
|
||||
self.userRepository.updateIdfa(request: IdfaUpdateRequest(adid: idfa))
|
||||
.sink { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
DEBUG_LOG("finish")
|
||||
case .failure(let error):
|
||||
ERROR_LOG(error.localizedDescription)
|
||||
}
|
||||
} receiveValue: { _ in
|
||||
}
|
||||
.store(in: &self.subscription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getMemberInfo() {
|
||||
userRepository.getMemberInfo()
|
||||
.sink { result in
|
||||
|
|
|
@ -40,6 +40,16 @@ struct OnboardingView: View {
|
|||
.resizable()
|
||||
.scaledToFit()
|
||||
.tag(4)
|
||||
|
||||
Image("img_guide_5")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.tag(5)
|
||||
|
||||
Image("img_guide_6")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.tag(6)
|
||||
}
|
||||
.tabViewStyle(PageTabViewStyle())
|
||||
.onAppear {
|
||||
|
@ -49,7 +59,7 @@ struct OnboardingView: View {
|
|||
|
||||
Text("시작하기")
|
||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||
.foregroundColor(.white)
|
||||
.foregroundColor(Color(hex: "1313BC"))
|
||||
.frame(width: screenSize().width, height: 60)
|
||||
.background(Color(hex: "80D8FF"))
|
||||
.onTapGesture {
|
||||
|
|
|
@ -17,19 +17,23 @@ struct SplashView: View {
|
|||
|
||||
var body: some View {
|
||||
ZStack(alignment: .top) {
|
||||
Image("splash_bg_2024_03")
|
||||
Image("splash_bg_2024")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
|
||||
VStack(spacing: 0) {
|
||||
Image("splash_text_2024_03")
|
||||
.padding(.top, 262)
|
||||
|
||||
Spacer()
|
||||
|
||||
Image("splash_text_logo_2024_03")
|
||||
.padding(.bottom, 35)
|
||||
Image("splash_logo_2024")
|
||||
|
||||
Image("splash_logo_dragon_2024")
|
||||
.padding(.leading, 25)
|
||||
.padding(.top, 50)
|
||||
.padding(.bottom, 45)
|
||||
|
||||
Image("vividnext_logo")
|
||||
.padding(.bottom, 45)
|
||||
}
|
||||
|
||||
if isShowUpdatePopup {
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
//
|
||||
// SelectedButtonView.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 2/23/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SelectedButtonView: View {
|
||||
|
||||
let title: String
|
||||
let isActive: Bool
|
||||
let isSelected: Bool
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 6.7) {
|
||||
if isSelected {
|
||||
Image("ic_select_check")
|
||||
}
|
||||
|
||||
Text(title)
|
||||
.font(.custom(Font.bold.rawValue, size: 14.7))
|
||||
.foregroundColor(!isActive ? Color.gray77 : isSelected ? .white : Color.button)
|
||||
}
|
||||
.padding(.vertical, 14.3)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(!isActive ? Color.gray55 : isSelected ? Color.button : Color.bg)
|
||||
.cornerRadius(6.7)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
SelectedButtonView(title: "테스트", isActive: true, isSelected: true)
|
||||
}
|
|
@ -15,7 +15,6 @@ extension Color {
|
|||
static let bg = Color(hex: "13181B")
|
||||
static let gray11 = Color(hex: "111111")
|
||||
static let gray22 = Color(hex: "222222")
|
||||
static let gray23 = Color(hex: "232323")
|
||||
static let gray30 = Color(hex: "303030")
|
||||
static let gray33 = Color(hex: "333333")
|
||||
static let gray52 = Color(hex: "525252")
|
||||
|
@ -28,6 +27,5 @@ extension Color {
|
|||
|
||||
static let mainRed = Color(hex: "ff5c49")
|
||||
static let mainRed2 = Color(hex: "ea3a25")
|
||||
static let mainRed3 = Color(hex: "dd4500")
|
||||
static let mainYellow = Color(hex: "ffdc00")
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
//
|
||||
// IdfaUpdateRequest.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 2/26/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct IdfaUpdateRequest: Encodable {
|
||||
let adid: String
|
||||
}
|
|
@ -30,7 +30,6 @@ enum UserApi {
|
|||
case getChangeNicknamePrice
|
||||
case checkNickname(nickname: String)
|
||||
case changeNickname(request: ProfileUpdateRequest)
|
||||
case updateIdfa(request: IdfaUpdateRequest)
|
||||
}
|
||||
|
||||
extension UserApi: TargetType {
|
||||
|
@ -99,9 +98,6 @@ extension UserApi: TargetType {
|
|||
|
||||
case .changeNickname:
|
||||
return "/member/change/nickname"
|
||||
|
||||
case .updateIdfa:
|
||||
return "/member/adid/update"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +110,7 @@ extension UserApi: TargetType {
|
|||
case .searchUser, .getMypage, .getMemberInfo, .getMyProfile, .getChangeNicknamePrice, .checkNickname:
|
||||
return .get
|
||||
|
||||
case .updatePushToken, .profileUpdate, .changeNickname, .updateIdfa:
|
||||
case .updatePushToken, .profileUpdate, .changeNickname:
|
||||
return .put
|
||||
}
|
||||
}
|
||||
|
@ -171,9 +167,6 @@ extension UserApi: TargetType {
|
|||
|
||||
case .changeNickname(let request):
|
||||
return .requestJSONEncodable(request)
|
||||
|
||||
case .updateIdfa(let request):
|
||||
return .requestJSONEncodable(request)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -108,8 +108,4 @@ final class UserRepository {
|
|||
func changeNickname(request: ProfileUpdateRequest) -> AnyPublisher<Response, MoyaError> {
|
||||
return api.requestPublisher(.changeNickname(request: request))
|
||||
}
|
||||
|
||||
func updateIdfa(request: IdfaUpdateRequest) -> AnyPublisher<Response, MoyaError> {
|
||||
return api.requestPublisher(.updateIdfa(request: request))
|
||||
}
|
||||
}
|
||||
|
|