diff --git a/SodaLive/Sources/Explorer/Profile/GetCreatorProfileResponse.swift b/SodaLive/Sources/Explorer/Profile/GetCreatorProfileResponse.swift index f87b105..bfc812f 100644 --- a/SodaLive/Sources/Explorer/Profile/GetCreatorProfileResponse.swift +++ b/SodaLive/Sources/Explorer/Profile/GetCreatorProfileResponse.swift @@ -61,6 +61,7 @@ struct LiveRoomResponse: Decodable { let content: String let isPaid: Bool let beginDateTime: String + let beginDateTimeUtc: String let coverImageUrl: String let isAdult: Bool let price: Int @@ -68,6 +69,7 @@ struct LiveRoomResponse: Decodable { let managerNickname: String let isReservation: Bool let isActive: Bool + let isPrivateRoom: Bool } struct GetAudioContentListResponse: Decodable { diff --git a/SodaLive/Sources/Explorer/Profile/UserProfileLiveView.swift b/SodaLive/Sources/Explorer/Profile/UserProfileLiveView.swift index ccc7750..0cec001 100644 --- a/SodaLive/Sources/Explorer/Profile/UserProfileLiveView.swift +++ b/SodaLive/Sources/Explorer/Profile/UserProfileLiveView.swift @@ -18,163 +18,136 @@ struct UserProfileLiveView: View { var body: some View { VStack(spacing: 13.3) { ForEach(0.. 0 { + HStack(spacing: 2) { + Image("ic_can") + .resizable() + .scaledToFit() + .frame(width: 12) - Spacer() - - if liveRoom.isActive { - if liveRoom.channelName != nil { - Text("Live") - .font(.custom(Font.medium.rawValue, size: 11.3)) - .foregroundColor(Color.mainRed) - .padding(.horizontal, 7) - .padding(.vertical, 4) - .overlay( - RoundedRectangle(cornerRadius: 3.3) - .stroke(Color.mainRed, lineWidth: 1) - ) - } else { - Text("예정") - .font(.custom(Font.medium.rawValue, size: 11.3)) - .foregroundColor(Color(hex: "fdca2f")) - .padding(.horizontal, 9) - .padding(.vertical, 4) - .overlay( - RoundedRectangle(cornerRadius: 3.3) - .stroke(Color(hex: "fdca2f"), lineWidth: 1) - ) - } - } else { - Text("종료") - .font(.custom(Font.medium.rawValue, size: 11.3)) - .foregroundColor(Color.gray77) - .padding(.horizontal, 9) - .padding(.vertical, 4) - .overlay( - RoundedRectangle(cornerRadius: 3.3) - .stroke(Color.gray77, lineWidth: 1) - ) - } - } - - Spacer() - - if liveRoom.isActive { - if liveRoom.channelName != nil { - if liveRoom.isPaid || liveRoom.price <= 0 { - Text("지금 참여하기") - .font(.custom(Font.bold.rawValue, size: 13.3)) - .foregroundColor(Color.white) - .frame( - width: screenSize().width - 26.7 - 100, - height: 36.7 - ) - .background(Color.mainRed3) - .cornerRadius(5.3) - .onTapGesture { - onClickParticipant(liveRoom) - } - } else { - Text("\(liveRoom.price)캔으로 지금 참여하기") - .font(.custom(Font.bold.rawValue, size: 13.3)) - .foregroundColor(Color.white) - .frame( - width: screenSize().width - 26.7 - 100, - height: 36.7 - ) - .background(Color.mainRed3) - .cornerRadius(5.3) - .onTapGesture { - onClickParticipant(liveRoom) - } - } - } else { - if liveRoom.isReservation { - Text("예약완료") - .font(.custom(Font.bold.rawValue, size: 13.3)) - .foregroundColor(Color.gray77) - .frame( - width: screenSize().width - 26.7 - 100, - height: 36.7 - ) - .background(Color.gray52) - .cornerRadius(5.3) - } else { - Text("\(liveRoom.price > 0 ? "\(liveRoom.price)캔으로 " : "")예약하기") - .font(.custom(Font.bold.rawValue, size: 13.3)) - .foregroundColor(Color.black) - .frame( - width: screenSize().width - 26.7 - 100, - height: 36.7 - ) - .background(Color(hex: "fdca2f")) - .cornerRadius(5.3) - .onTapGesture { - onClickReservation(liveRoom) - } - } - } - } else { - Text("다시듣기를 지원하지 않습니다") - .font(.custom(Font.bold.rawValue, size: 13.3)) - .foregroundColor(Color.gray77) - .frame( - width: screenSize().width - 26.7 - 100, - height: 36.7 - ) - .background(Color.gray52) - .cornerRadius(5.3) + Text("\(item.price)") + .font(.custom(Font.preRegular.rawValue, size: 12)) + .foregroundColor(.white) } + .padding(4) + .frame(maxWidth: .infinity) + .background(Color(hex: "3b5ff1")) + .cornerRadius(4) + } else { + Text("무료") + .font(.custom(Font.preRegular.rawValue, size: 14)) + .foregroundColor(Color(hex: "#263238")) + .padding(4) + .frame(maxWidth: .infinity) + .background(Color.white) + .cornerRadius(4) + } + } + .frame(width: 55) + } + .padding(10) + .background(Color(hex: "263238")) + .cornerRadius(16) + .contentShape(Rectangle()) + .onTapGesture { + if item.isActive && !item.channelName.isNullOrBlank() { + onClickParticipant(item) + } else { + if !item.isReservation { + onClickReservation(item) } } - .frame(height: 116.7) - - Rectangle() - .frame(height: 1) - .foregroundColor(Color.gray90.opacity(0.5)) } } } diff --git a/SodaLive/Sources/Explorer/Profile/UserProfileView.swift b/SodaLive/Sources/Explorer/Profile/UserProfileView.swift index e00ed72..ea45951 100644 --- a/SodaLive/Sources/Explorer/Profile/UserProfileView.swift +++ b/SodaLive/Sources/Explorer/Profile/UserProfileView.swift @@ -141,9 +141,13 @@ struct UserProfileView: View { if creatorProfile.creator.creatorId == UserDefaults.int(forKey: .userId) || creatorProfile.liveRoomList.count > 0 { VStack(alignment: .leading, spacing: 14) { - Text("라이브") - .font(.custom(Font.preBold.rawValue, size: 26)) - .foregroundColor(Color.white) + HStack(spacing: 0) { + Text("라이브") + .font(.custom(Font.preBold.rawValue, size: 26)) + .foregroundColor(Color.white) + + Spacer() + } if creatorProfile.creator.creatorId == UserDefaults.int(forKey: .userId) { HStack(spacing: 8) { @@ -193,6 +197,7 @@ struct UserProfileView: View { ) } } + .padding(.horizontal, 24) } if creatorProfile.creator.creatorId == UserDefaults.int(forKey: .userId) || creatorProfile.contentList.count > 0 { @@ -355,6 +360,7 @@ struct UserProfileView: View { startDateTime: viewModel.liveStartDate, nowDateTime: viewModel.nowDate ) + } if viewModel.isShowPasswordDialog { @@ -460,10 +466,18 @@ struct UserProfileView: View { viewModel.errorMessage = message viewModel.isShowPopup = true } + .padding(.top, proxy.safeAreaInsets.top) + .padding(.bottom, proxy.safeAreaInsets.bottom) + .padding(.trailing, proxy.safeAreaInsets.trailing) + .padding(.leading, proxy.safeAreaInsets.leading) } if isShowMenuSettings { MenuSettingsView(isShowing: $isShowMenuSettings) + .padding(.top, proxy.safeAreaInsets.top) + .padding(.bottom, proxy.safeAreaInsets.bottom) + .padding(.trailing, proxy.safeAreaInsets.trailing) + .padding(.leading, proxy.safeAreaInsets.leading) } } } diff --git a/SodaLive/Sources/Extensions/StringExtension.swift b/SodaLive/Sources/Extensions/StringExtension.swift index 099b5e1..b1229f4 100644 --- a/SodaLive/Sources/Extensions/StringExtension.swift +++ b/SodaLive/Sources/Extensions/StringExtension.swift @@ -54,4 +54,45 @@ extension String { let dec = NSDecimalNumber(string: self) return formatter.string(from: dec) ?? "\(currencyCode) \(self)" } + + func parseUtcIsoLocalDateTime() -> [String: String] { + // 1. 서버에서 내려온 포맷: "yyyy-MM-dd'T'HH:mm:ss" + let utcFormatter = DateFormatter() + utcFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + utcFormatter.locale = Locale.current + utcFormatter.timeZone = TimeZone(abbreviation: "UTC") + + // 2. 문자열 → Date 객체 + guard let date = utcFormatter.date(from: self) else { + return [:] // 파싱 실패 시 빈 딕셔너리 반환 + } + + // 3~6. 출력용 Formatter (로컬 시간 기준) + let localFormatter = DateFormatter() + localFormatter.locale = Locale.autoupdatingCurrent + localFormatter.timeZone = TimeZone.current + + // 3. 월 (1~12) + localFormatter.dateFormat = "M" + let month = localFormatter.string(from: date) + + // 4. 일 (1~31) + localFormatter.dateFormat = "d" + let day = localFormatter.string(from: date) + + // 5. 요일 (예: "Mon", "목") + localFormatter.dateFormat = "E" + let dayOfWeek = localFormatter.string(from: date) + + // 6. 시간 (예: "AM 05:00") + localFormatter.dateFormat = "a hh:mm" + let time = localFormatter.string(from: date) + + return [ + "month": month, + "day": day, + "dayOfWeek": dayOfWeek, + "time": time + ] + } } diff --git a/SodaLive/Sources/Live/Reservation/LiveReservationItemView.swift b/SodaLive/Sources/Live/Reservation/LiveReservationItemView.swift index bc99a1f..f4a5a5b 100644 --- a/SodaLive/Sources/Live/Reservation/LiveReservationItemView.swift +++ b/SodaLive/Sources/Live/Reservation/LiveReservationItemView.swift @@ -117,50 +117,9 @@ struct LiveReservationItemView: View { .background(Color(hex: "263238")) .cornerRadius(16) .onAppear { - self.dateDic = parseUtcIsoLocalDateTime(item.beginDateTimeUtc) + self.dateDic = item.beginDateTimeUtc.parseUtcIsoLocalDateTime() } } - - func parseUtcIsoLocalDateTime(_ utcString: String) -> [String: String] { - // 1. 서버에서 내려온 포맷: "yyyy-MM-dd'T'HH:mm:ss" - let utcFormatter = DateFormatter() - utcFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" - utcFormatter.locale = Locale.current - utcFormatter.timeZone = TimeZone(abbreviation: "UTC") - - // 2. 문자열 → Date 객체 - guard let date = utcFormatter.date(from: utcString) else { - return [:] // 파싱 실패 시 빈 딕셔너리 반환 - } - - // 3~6. 출력용 Formatter (로컬 시간 기준) - let localFormatter = DateFormatter() - localFormatter.locale = Locale.autoupdatingCurrent - localFormatter.timeZone = TimeZone.current - - // 3. 월 (1~12) - localFormatter.dateFormat = "M" - let month = localFormatter.string(from: date) - - // 4. 일 (1~31) - localFormatter.dateFormat = "d" - let day = localFormatter.string(from: date) - - // 5. 요일 (예: "Mon", "목") - localFormatter.dateFormat = "E" - let dayOfWeek = localFormatter.string(from: date) - - // 6. 시간 (예: "AM 05:00") - localFormatter.dateFormat = "a hh:mm" - let time = localFormatter.string(from: date) - - return [ - "month": month, - "day": day, - "dayOfWeek": dayOfWeek, - "time": time - ] - } } struct LiveReservationItemView_Previews: PreviewProvider {