import SwiftUI struct MainContentAllView: View { @ObservedObject var viewModel: MainContentAllViewModel let onTapContent: (Int) -> Void let onTapSeries: (Int) -> Void @State private var isSortPopupPresented = false private let columns = [ GridItem(.flexible(), spacing: SodaSpacing.s4), GridItem(.flexible(), spacing: SodaSpacing.s4), GridItem(.flexible(), spacing: SodaSpacing.s4) ] var body: some View { ZStack(alignment: .topTrailing) { Color.black .ignoresSafeArea() content if isSortPopupPresented { Color.black .opacity(0.001) .frame(maxWidth: .infinity, maxHeight: .infinity) .onTapGesture { isSortPopupPresented = false } CreatorChannelSortContextPopup( selectedSort: viewModel.selectedSort, sorts: viewModel.allowedSorts ) { sort in viewModel.selectSort(sort) isSortPopupPresented = false } .padding(.top, viewModel.selectedType == .series ? 156 : 104) .padding(.trailing, SodaSpacing.s14) .zIndex(1) } } .onAppear { viewModel.fetchFirstPageIfNeeded() } .onChange(of: viewModel.selectedSort) { _ in isSortPopupPresented = false } .onChange(of: viewModel.selectedType) { _ in isSortPopupPresented = false } .onDisappear { isSortPopupPresented = false } .sodaToast( isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage, autohideIn: 2 ) } private var content: some View { VStack(spacing: 0) { typeFilter if viewModel.selectedType == .series { dayOfWeekFilter } sortBar if viewModel.isLoading && viewModel.hasLoaded == false { ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: .white)) .frame(maxWidth: .infinity, maxHeight: .infinity) } else if viewModel.shouldShowEmptyState { MainContentAudioEmptyStateView(message: I18n.MainContentAll.emptyStateMessage) } else { ScrollView(showsIndicators: false) { gridContent .padding(.horizontal, SodaSpacing.s14) .padding(.top, SodaSpacing.s8) .padding(.bottom, SodaSpacing.s20) if viewModel.isLoadingNextPage { ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: .white)) .padding(.vertical, SodaSpacing.s20) } } } } } private var typeFilter: some View { CapsuleTabBar( items: MainContentAllType.allCases, selectedItem: Binding( get: { viewModel.selectedType }, set: { viewModel.selectType($0) } ), title: { $0.title } ) } private var dayOfWeekFilter: some View { HStack(spacing: 0) { ForEach(SeriesPublishedDaysOfWeek.allFilterCases, id: \.self) { dayOfWeek in Button { viewModel.selectDayOfWeek(dayOfWeek) } label: { Text(dayOfWeek.shortTitle) .appFont(size: 14, weight: .medium) .foregroundColor(viewModel.selectedDayOfWeek == dayOfWeek ? Color.black : Color.white) .lineLimit(1) .frame(minWidth: 20, minHeight: 20) .padding(SodaSpacing.s8) .background(viewModel.selectedDayOfWeek == dayOfWeek ? Color.white : Color.gray900) .clipShape(RoundedRectangle(cornerRadius: SodaSpacing.s8, style: .continuous)) } .buttonStyle(.plain) } } .background(Color.gray900) .clipShape(RoundedRectangle(cornerRadius: SodaSpacing.s8, style: .continuous)) .padding(.vertical, SodaSpacing.s8) .frame(maxWidth: .infinity) .frame(height: 52) .background(Color.black) } private var sortBar: some View { CreatorChannelSortBar( selectedSort: viewModel.selectedSort, totalCount: nil, onTapSort: { isSortPopupPresented.toggle() } ) } @ViewBuilder private var gridContent: some View { if viewModel.selectedType.usesAudioItems { LazyVGrid(columns: columns, alignment: .leading, spacing: SodaSpacing.s20) { ForEach(viewModel.audios) { audio in Button { onTapContent(audio.audioContentId) } label: { GeometryReader { proxy in AudioContentThumbnailCard( item: AudioContentThumbnailCardItem(mainContentAudio: audio), width: proxy.size.width, titleTypography: .body1, subtitleTypography: .caption2, labelHorizontalPadding: SodaSpacing.s4 ) } .aspectRatio(0.78, contentMode: .fit) } .buttonStyle(.plain) .onAppear { viewModel.fetchNextPageIfNeeded(currentAudio: audio) } } } } else { LazyVGrid(columns: columns, alignment: .leading, spacing: SodaSpacing.s20) { ForEach(viewModel.series) { series in Button { onTapSeries(series.seriesId) } label: { GeometryReader { proxy in MainContentSeriesThumbnailCard( item: series, width: proxy.size.width ) } .aspectRatio(0.55, contentMode: .fit) } .buttonStyle(.plain) .onAppear { viewModel.fetchNextPageIfNeeded(currentSeries: series) } } } } } } struct MainContentAllView_Previews: PreviewProvider { static var previews: some View { MainContentAllView( viewModel: MainContentAllViewModel(), onTapContent: { _ in }, onTapSeries: { _ in } ) } }