feat(creator): 스케줄 섹션을 연결한다
This commit is contained in:
@@ -34,6 +34,9 @@
|
|||||||
CC0101172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift */; };
|
CC0101172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift */; };
|
||||||
CC0102172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift */; };
|
CC0102172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift */; };
|
||||||
CC0103172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift */; };
|
CC0103172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift */; };
|
||||||
|
CC0101182FF0100000A5BBA3 /* CreatorChannelScheduleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100182FF0100000A5BBA3 /* CreatorChannelScheduleSection.swift */; };
|
||||||
|
CC0102182FF0100000A5BBA3 /* CreatorChannelScheduleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100182FF0100000A5BBA3 /* CreatorChannelScheduleSection.swift */; };
|
||||||
|
CC0103182FF0100000A5BBA3 /* CreatorChannelScheduleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100182FF0100000A5BBA3 /* CreatorChannelScheduleSection.swift */; };
|
||||||
CC0101082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100082FF0100000A5BBA3 /* CreatorChannelView.swift */; };
|
CC0101082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100082FF0100000A5BBA3 /* CreatorChannelView.swift */; };
|
||||||
CC0102082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100082FF0100000A5BBA3 /* CreatorChannelView.swift */; };
|
CC0102082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100082FF0100000A5BBA3 /* CreatorChannelView.swift */; };
|
||||||
CC0103082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100082FF0100000A5BBA3 /* CreatorChannelView.swift */; };
|
CC0103082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0100082FF0100000A5BBA3 /* CreatorChannelView.swift */; };
|
||||||
@@ -1762,6 +1765,7 @@
|
|||||||
CC0100152FF0100000A5BBA3 /* CreatorChannelLatestAudioSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatorChannelLatestAudioSection.swift; sourceTree = "<group>"; };
|
CC0100152FF0100000A5BBA3 /* CreatorChannelLatestAudioSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatorChannelLatestAudioSection.swift; sourceTree = "<group>"; };
|
||||||
CC0100162FF0100000A5BBA3 /* CreatorChannelDonationSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatorChannelDonationSection.swift; sourceTree = "<group>"; };
|
CC0100162FF0100000A5BBA3 /* CreatorChannelDonationSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatorChannelDonationSection.swift; sourceTree = "<group>"; };
|
||||||
CC0100172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatorChannelNoticeSection.swift; sourceTree = "<group>"; };
|
CC0100172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatorChannelNoticeSection.swift; sourceTree = "<group>"; };
|
||||||
|
CC0100182FF0100000A5BBA3 /* CreatorChannelScheduleSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatorChannelScheduleSection.swift; sourceTree = "<group>"; };
|
||||||
0BBDC3ECBBE049EAA4057D3A /* PostChannelDonationRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PostChannelDonationRequest.swift; sourceTree = "<group>"; };
|
0BBDC3ECBBE049EAA4057D3A /* PostChannelDonationRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PostChannelDonationRequest.swift; sourceTree = "<group>"; };
|
||||||
0EC05859619BDC03056911E7 /* MainTab.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MainTab.swift; sourceTree = "<group>"; };
|
0EC05859619BDC03056911E7 /* MainTab.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MainTab.swift; sourceTree = "<group>"; };
|
||||||
189350EA101CF12EEBA6EC1D /* MainTabBarView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MainTabBarView.swift; sourceTree = "<group>"; };
|
189350EA101CF12EEBA6EC1D /* MainTabBarView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MainTabBarView.swift; sourceTree = "<group>"; };
|
||||||
@@ -2685,6 +2689,7 @@
|
|||||||
CC0100152FF0100000A5BBA3 /* CreatorChannelLatestAudioSection.swift */,
|
CC0100152FF0100000A5BBA3 /* CreatorChannelLatestAudioSection.swift */,
|
||||||
CC0100162FF0100000A5BBA3 /* CreatorChannelDonationSection.swift */,
|
CC0100162FF0100000A5BBA3 /* CreatorChannelDonationSection.swift */,
|
||||||
CC0100172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift */,
|
CC0100172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift */,
|
||||||
|
CC0100182FF0100000A5BBA3 /* CreatorChannelScheduleSection.swift */,
|
||||||
);
|
);
|
||||||
path = Components;
|
path = Components;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -5623,6 +5628,7 @@
|
|||||||
CC0101152FF0100000A5BBA3 /* CreatorChannelLatestAudioSection.swift in Sources */,
|
CC0101152FF0100000A5BBA3 /* CreatorChannelLatestAudioSection.swift in Sources */,
|
||||||
CC0101162FF0100000A5BBA3 /* CreatorChannelDonationSection.swift in Sources */,
|
CC0101162FF0100000A5BBA3 /* CreatorChannelDonationSection.swift in Sources */,
|
||||||
CC0101172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */,
|
CC0101172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */,
|
||||||
|
CC0101182FF0100000A5BBA3 /* CreatorChannelScheduleSection.swift in Sources */,
|
||||||
CC0101082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */,
|
CC0101082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */,
|
||||||
CC0101112FF0100000A5BBA3 /* CreatorChannelTitleBar.swift in Sources */,
|
CC0101112FF0100000A5BBA3 /* CreatorChannelTitleBar.swift in Sources */,
|
||||||
CC0101122FF0100000A5BBA3 /* CreatorChannelHeaderSection.swift in Sources */,
|
CC0101122FF0100000A5BBA3 /* CreatorChannelHeaderSection.swift in Sources */,
|
||||||
@@ -6424,6 +6430,7 @@
|
|||||||
CC0102152FF0100000A5BBA3 /* CreatorChannelLatestAudioSection.swift in Sources */,
|
CC0102152FF0100000A5BBA3 /* CreatorChannelLatestAudioSection.swift in Sources */,
|
||||||
CC0102162FF0100000A5BBA3 /* CreatorChannelDonationSection.swift in Sources */,
|
CC0102162FF0100000A5BBA3 /* CreatorChannelDonationSection.swift in Sources */,
|
||||||
CC0102172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */,
|
CC0102172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */,
|
||||||
|
CC0102182FF0100000A5BBA3 /* CreatorChannelScheduleSection.swift in Sources */,
|
||||||
CC0102082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */,
|
CC0102082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */,
|
||||||
CC0102112FF0100000A5BBA3 /* CreatorChannelTitleBar.swift in Sources */,
|
CC0102112FF0100000A5BBA3 /* CreatorChannelTitleBar.swift in Sources */,
|
||||||
CC0102122FF0100000A5BBA3 /* CreatorChannelHeaderSection.swift in Sources */,
|
CC0102122FF0100000A5BBA3 /* CreatorChannelHeaderSection.swift in Sources */,
|
||||||
@@ -7241,6 +7248,7 @@
|
|||||||
CC0103152FF0100000A5BBA3 /* CreatorChannelLatestAudioSection.swift in Sources */,
|
CC0103152FF0100000A5BBA3 /* CreatorChannelLatestAudioSection.swift in Sources */,
|
||||||
CC0103162FF0100000A5BBA3 /* CreatorChannelDonationSection.swift in Sources */,
|
CC0103162FF0100000A5BBA3 /* CreatorChannelDonationSection.swift in Sources */,
|
||||||
CC0103172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */,
|
CC0103172FF0100000A5BBA3 /* CreatorChannelNoticeSection.swift in Sources */,
|
||||||
|
CC0103182FF0100000A5BBA3 /* CreatorChannelScheduleSection.swift in Sources */,
|
||||||
CC0103082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */,
|
CC0103082FF0100000A5BBA3 /* CreatorChannelView.swift in Sources */,
|
||||||
CC0103112FF0100000A5BBA3 /* CreatorChannelTitleBar.swift in Sources */,
|
CC0103112FF0100000A5BBA3 /* CreatorChannelTitleBar.swift in Sources */,
|
||||||
CC0103122FF0100000A5BBA3 /* CreatorChannelHeaderSection.swift in Sources */,
|
CC0103122FF0100000A5BBA3 /* CreatorChannelHeaderSection.swift in Sources */,
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ struct CreatorChannelView: View {
|
|||||||
selectedTabContent
|
selectedTabContent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoresSafeArea(.container, edges: .vertical)
|
.ignoresSafeArea(.container, edges: .top)
|
||||||
|
|
||||||
if isTabBarSticky {
|
if isTabBarSticky {
|
||||||
tabBar
|
tabBar
|
||||||
@@ -156,8 +156,10 @@ struct CreatorChannelView: View {
|
|||||||
CreatorChannelHomeView(
|
CreatorChannelHomeView(
|
||||||
response: response,
|
response: response,
|
||||||
onSelectTab: selectTab,
|
onSelectTab: selectTab,
|
||||||
|
onTapLive: showLiveDetail,
|
||||||
onTapContent: showContentDetail,
|
onTapContent: showContentDetail,
|
||||||
onTapDonate: showDonationDialog
|
onTapDonate: showDonationDialog,
|
||||||
|
onTapSchedule: handleScheduleTap
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
CreatorChannelPlaceholderTabView(title: viewModel.selectedTab.title)
|
CreatorChannelPlaceholderTabView(title: viewModel.selectedTab.title)
|
||||||
@@ -182,9 +184,43 @@ struct CreatorChannelView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func showContentDetail(_ contentId: Int) {
|
private func showContentDetail(_ contentId: Int) {
|
||||||
|
guard contentId > 0 else { return }
|
||||||
AppState.shared.setAppStep(step: .contentDetail(contentId: contentId))
|
AppState.shared.setAppStep(step: .contentDetail(contentId: contentId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func showLiveDetail(_ roomId: Int) {
|
||||||
|
guard roomId > 0 else { return }
|
||||||
|
AppState.shared.setAppStep(
|
||||||
|
step: .liveDetail(
|
||||||
|
roomId: roomId,
|
||||||
|
onClickParticipant: {},
|
||||||
|
onClickReservation: {},
|
||||||
|
onClickStart: {},
|
||||||
|
onClickCancel: {}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showCommunity(_ creatorId: Int) {
|
||||||
|
guard creatorId > 0 else { return }
|
||||||
|
AppState.shared.setAppStep(step: .creatorCommunityAll(creatorId: creatorId))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleScheduleTap(type: CreatorActivityType, targetId: Int) {
|
||||||
|
guard targetId > 0 else { return }
|
||||||
|
|
||||||
|
switch type {
|
||||||
|
case .live, .liveReplay:
|
||||||
|
showLiveDetail(targetId)
|
||||||
|
case .audio:
|
||||||
|
showContentDetail(targetId)
|
||||||
|
case .community:
|
||||||
|
showCommunity(targetId)
|
||||||
|
case .unknown:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func showDonationDialog() {
|
private func showDonationDialog() {
|
||||||
guard let creatorId = viewModel.response?.creator.creatorId else { return }
|
guard let creatorId = viewModel.response?.creator.creatorId else { return }
|
||||||
channelDonationViewModel.setCreatorId(creatorId, shouldFetch: false)
|
channelDonationViewModel.setCreatorId(creatorId, shouldFetch: false)
|
||||||
|
|||||||
@@ -0,0 +1,239 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct CreatorChannelScheduleSection: View {
|
||||||
|
let schedules: [CreatorChannelScheduleResponse]
|
||||||
|
let onTapSchedule: (CreatorActivityType, Int) -> Void
|
||||||
|
|
||||||
|
init(
|
||||||
|
schedules: [CreatorChannelScheduleResponse],
|
||||||
|
onTapSchedule: @escaping (CreatorActivityType, Int) -> Void = { _, _ in }
|
||||||
|
) {
|
||||||
|
self.schedules = schedules
|
||||||
|
self.onTapSchedule = onTapSchedule
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
if !schedules.isEmpty {
|
||||||
|
VStack(alignment: .leading, spacing: SodaSpacing.s14) {
|
||||||
|
SectionTitle(title: I18n.CreatorChannelHome.schedule)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: SodaSpacing.s4) {
|
||||||
|
ForEach(Array(schedules.enumerated()), id: \.offset) { _, schedule in
|
||||||
|
CreatorChannelScheduleItemView(schedule: schedule) {
|
||||||
|
guard case .unknown = schedule.type else {
|
||||||
|
onTapSchedule(schedule.type, schedule.targetId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background(alignment: .leading) {
|
||||||
|
ScheduleTimelineLine()
|
||||||
|
.padding(.leading, 3)
|
||||||
|
.padding(.vertical, 52)
|
||||||
|
.opacity(schedules.count > 1 ? 1 : 0)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, SodaSpacing.s14)
|
||||||
|
}
|
||||||
|
.padding(.top, SodaSpacing.s20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct CreatorChannelScheduleItemView: View {
|
||||||
|
let schedule: CreatorChannelScheduleResponse
|
||||||
|
let action: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(alignment: .center, spacing: SodaSpacing.s14) {
|
||||||
|
TimelineDotView(isHighlighted: schedule.isToday)
|
||||||
|
|
||||||
|
Button(action: action) {
|
||||||
|
HStack(alignment: .center, spacing: SodaSpacing.s12) {
|
||||||
|
DateBoxView(schedule: schedule)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: SodaSpacing.s8) {
|
||||||
|
HStack(alignment: .center, spacing: SodaSpacing.s8) {
|
||||||
|
Text(schedule.title)
|
||||||
|
.appFont(size: 16, weight: .medium)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.lineLimit(1)
|
||||||
|
.truncationMode(.tail)
|
||||||
|
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
HStack(alignment: .center, spacing: SodaSpacing.s8) {
|
||||||
|
ActivityTypeTagView(type: schedule.type)
|
||||||
|
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
|
||||||
|
Text(schedule.timeText)
|
||||||
|
.appFont(size: 16, weight: .regular)
|
||||||
|
.foregroundColor(Color.gray500)
|
||||||
|
.lineLimit(1)
|
||||||
|
.multilineTextAlignment(.trailing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
.padding(SodaSpacing.s14)
|
||||||
|
.background(Color.gray900)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct ScheduleTimelineLine: View {
|
||||||
|
var body: some View {
|
||||||
|
Rectangle()
|
||||||
|
.fill(Color.gray700)
|
||||||
|
.frame(width: 2)
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct TimelineDotView: View {
|
||||||
|
let isHighlighted: Bool
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Circle()
|
||||||
|
.fill(isHighlighted ? Color.soda400 : Color.gray500)
|
||||||
|
.frame(width: SodaSpacing.s8, height: SodaSpacing.s8)
|
||||||
|
.frame(width: SodaSpacing.s8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct DateBoxView: View {
|
||||||
|
let schedule: CreatorChannelScheduleResponse
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .center, spacing: SodaSpacing.s4) {
|
||||||
|
if schedule.isToday {
|
||||||
|
Text(I18n.HomeFollowing.today)
|
||||||
|
.appFont(size: 22, weight: .bold)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.lineLimit(1)
|
||||||
|
} else {
|
||||||
|
Text(schedule.dayText)
|
||||||
|
.appFont(size: 22, weight: .bold)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.lineLimit(1)
|
||||||
|
|
||||||
|
Text(schedule.weekdayText)
|
||||||
|
.appFont(size: 12, weight: .medium)
|
||||||
|
.foregroundColor(Color.gray500)
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(width: 53, height: 53)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct ActivityTypeTagView: View {
|
||||||
|
let type: CreatorActivityType
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Text(type.scheduleTagTitle)
|
||||||
|
.appFont(size: 14, weight: .regular)
|
||||||
|
.foregroundColor(Color.gray100)
|
||||||
|
.padding(.horizontal, SodaSpacing.s4)
|
||||||
|
.padding(.vertical, 2)
|
||||||
|
.background(Color.gray700)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: SodaSpacing.s4, style: .continuous))
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension CreatorChannelScheduleResponse {
|
||||||
|
var scheduledDate: Date? {
|
||||||
|
DateParser.parse(scheduledAtUtc)
|
||||||
|
}
|
||||||
|
|
||||||
|
var isToday: Bool {
|
||||||
|
guard let scheduledDate else { return false }
|
||||||
|
return Calendar.current.isDateInToday(scheduledDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dayText: String {
|
||||||
|
guard let scheduledDate else { return scheduledAtUtc }
|
||||||
|
return ScheduleDateFormatter.day.string(from: scheduledDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
var weekdayText: String {
|
||||||
|
guard let scheduledDate else { return "" }
|
||||||
|
return ScheduleDateFormatter.weekday.string(from: scheduledDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeText: String {
|
||||||
|
guard let scheduledDate else { return scheduledAtUtc }
|
||||||
|
return ScheduleDateFormatter.time.string(from: scheduledDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension CreatorActivityType {
|
||||||
|
var scheduleTagTitle: String {
|
||||||
|
switch self {
|
||||||
|
case .live:
|
||||||
|
return I18n.HomeFollowing.liveScheduleTag
|
||||||
|
case .liveReplay:
|
||||||
|
return I18n.HomeFollowing.liveReplayScheduleTag
|
||||||
|
case .audio:
|
||||||
|
return I18n.HomeFollowing.audioScheduleTag
|
||||||
|
case .community:
|
||||||
|
return I18n.HomeFollowing.communityScheduleTag
|
||||||
|
case .unknown(let rawValue):
|
||||||
|
return rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ScheduleDateFormatter {
|
||||||
|
static let day: DateFormatter = {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.locale = Locale(identifier: "ko_KR")
|
||||||
|
formatter.dateFormat = "d"
|
||||||
|
return formatter
|
||||||
|
}()
|
||||||
|
|
||||||
|
static let weekday: DateFormatter = {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.locale = Locale(identifier: "ko_KR")
|
||||||
|
formatter.dateFormat = "E"
|
||||||
|
return formatter
|
||||||
|
}()
|
||||||
|
|
||||||
|
static let time: DateFormatter = {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.locale = Locale(identifier: "ko_KR")
|
||||||
|
formatter.dateFormat = "a hh:mm"
|
||||||
|
return formatter
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CreatorChannelScheduleSection_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
CreatorChannelScheduleSection(
|
||||||
|
schedules: [
|
||||||
|
CreatorChannelScheduleResponse(
|
||||||
|
scheduledAtUtc: ISO8601DateFormatter().string(from: Date()),
|
||||||
|
title: "오늘 밤 라이브 방송",
|
||||||
|
type: .live,
|
||||||
|
targetId: 100
|
||||||
|
),
|
||||||
|
CreatorChannelScheduleResponse(
|
||||||
|
scheduledAtUtc: "2026-07-12T12:30:00Z",
|
||||||
|
title: "신규 오디오 콘텐츠 공개",
|
||||||
|
type: .audio,
|
||||||
|
targetId: 200
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
.padding(.vertical, SodaSpacing.s20)
|
||||||
|
.background(Color.black)
|
||||||
|
.previewLayout(.sizeThatFits)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,19 +6,22 @@ struct CreatorChannelHomeView: View {
|
|||||||
let onTapLive: (Int) -> Void
|
let onTapLive: (Int) -> Void
|
||||||
let onTapContent: (Int) -> Void
|
let onTapContent: (Int) -> Void
|
||||||
let onTapDonate: () -> Void
|
let onTapDonate: () -> Void
|
||||||
|
let onTapSchedule: (CreatorActivityType, Int) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
response: CreatorChannelHomeResponse,
|
response: CreatorChannelHomeResponse,
|
||||||
onSelectTab: @escaping (CreatorChannelTab) -> Void,
|
onSelectTab: @escaping (CreatorChannelTab) -> Void,
|
||||||
onTapLive: @escaping (Int) -> Void = { _ in },
|
onTapLive: @escaping (Int) -> Void = { _ in },
|
||||||
onTapContent: @escaping (Int) -> Void = { _ in },
|
onTapContent: @escaping (Int) -> Void = { _ in },
|
||||||
onTapDonate: @escaping () -> Void = {}
|
onTapDonate: @escaping () -> Void = {},
|
||||||
|
onTapSchedule: @escaping (CreatorActivityType, Int) -> Void = { _, _ in }
|
||||||
) {
|
) {
|
||||||
self.response = response
|
self.response = response
|
||||||
self.onSelectTab = onSelectTab
|
self.onSelectTab = onSelectTab
|
||||||
self.onTapLive = onTapLive
|
self.onTapLive = onTapLive
|
||||||
self.onTapContent = onTapContent
|
self.onTapContent = onTapContent
|
||||||
self.onTapDonate = onTapDonate
|
self.onTapDonate = onTapDonate
|
||||||
|
self.onTapSchedule = onTapSchedule
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -40,6 +43,11 @@ struct CreatorChannelHomeView: View {
|
|||||||
)
|
)
|
||||||
|
|
||||||
CreatorChannelNoticeSection(notices: response.notices)
|
CreatorChannelNoticeSection(notices: response.notices)
|
||||||
|
|
||||||
|
CreatorChannelScheduleSection(
|
||||||
|
schedules: response.schedules,
|
||||||
|
onTapSchedule: onTapSchedule
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.background(Color.black)
|
.background(Color.black)
|
||||||
|
|||||||
@@ -417,7 +417,7 @@
|
|||||||
|
|
||||||
### Phase 9: 스케줄 섹션
|
### Phase 9: 스케줄 섹션
|
||||||
|
|
||||||
- [ ] **Task 9.1: 스케줄 섹션 구현**
|
- [x] **Task 9.1: 스케줄 섹션 구현**
|
||||||
- 대상 파일:
|
- 대상 파일:
|
||||||
- 생성: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelScheduleSection.swift`
|
- 생성: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelScheduleSection.swift`
|
||||||
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
|
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
|
||||||
|
|||||||
Reference in New Issue
Block a user