import SwiftUI struct MLBNetworkSheet: View { var onWatchFullScreen: () -> Void @Environment(GamesViewModel.self) private var viewModel @Environment(\.horizontalSizeClass) private var horizontalSizeClass @Environment(\.dismiss) private var dismiss private var added: Bool { viewModel.activeStreams.contains(where: { $0.id == "MLBN" }) } private var canToggleIntoMultiview: Bool { added || viewModel.activeStreams.count < 4 } private var usesStackedLayout: Bool { #if os(iOS) horizontalSizeClass == .compact #else false #endif } private var outerHorizontalPadding: CGFloat { usesStackedLayout ? 20 : 94 } private var outerVerticalPadding: CGFloat { usesStackedLayout ? 24 : 70 } var body: some View { ZStack { sheetBackground .ignoresSafeArea() ViewThatFits { HStack(alignment: .top, spacing: 32) { networkOverview .frame(maxWidth: .infinity, alignment: .leading) actionColumn .frame(width: 360, alignment: .leading) } VStack(alignment: .leading, spacing: 24) { networkOverview actionColumn .frame(maxWidth: .infinity, alignment: .leading) } } .padding(38) .background( RoundedRectangle(cornerRadius: 34, style: .continuous) .fill(.black.opacity(0.46)) .overlay { RoundedRectangle(cornerRadius: 34, style: .continuous) .strokeBorder(.white.opacity(0.08), lineWidth: 1) } ) .padding(.horizontal, outerHorizontalPadding) .padding(.vertical, outerVerticalPadding) } } @ViewBuilder private var networkOverview: some View { VStack(alignment: .leading, spacing: 28) { VStack(alignment: .leading, spacing: 18) { HStack(spacing: 12) { statusPill(title: "LIVE CHANNEL", color: .blue) if added { statusPill(title: "IN MULTI-VIEW", color: .green) } } HStack(alignment: .center, spacing: 22) { ZStack { RoundedRectangle(cornerRadius: 26, style: .continuous) .fill(.blue.opacity(0.16)) .frame(width: 110, height: 110) Image(systemName: "tv.fill") .font(.system(size: 46, weight: .bold)) .foregroundStyle( LinearGradient( colors: [.white, .blue.opacity(0.88)], startPoint: .top, endPoint: .bottom ) ) } VStack(alignment: .leading, spacing: 10) { Text("MLB Network") .font(.system(size: 46, weight: .black, design: .rounded)) .foregroundStyle(.white) Text("Live coverage, studio hits, whip-around looks, analysis, and highlights all day.") .font(.system(size: 20, weight: .medium)) .foregroundStyle(.white.opacity(0.72)) .fixedSize(horizontal: false, vertical: true) } } } HStack(spacing: 14) { featurePill(title: "Live Look-Ins", systemImage: "dot.radiowaves.left.and.right") featurePill(title: "Studio Analysis", systemImage: "waveform.path.ecg.rectangle") featurePill(title: "Highlights", systemImage: "sparkles.tv") } VStack(alignment: .leading, spacing: 14) { Text("Best For") .font(.system(size: 16, weight: .bold, design: .rounded)) .foregroundStyle(.white.opacity(0.48)) .kerning(1.2) VStack(alignment: .leading, spacing: 14) { overviewLine( icon: "rectangle.stack.badge.play.fill", title: "Background baseball TV", detail: "Keep a league-wide live channel running while you browse or build Multi-View." ) overviewLine( icon: "sportscourt.fill", title: "Between-game coverage", detail: "Use it when you want updates, reactions, and cut-ins instead of one locked game feed." ) overviewLine( icon: "square.grid.2x2.fill", title: "Multi-View filler stream", detail: "Drop it into an open tile to keep the board full when only one or two games matter." ) } } .padding(24) .background(panelBackground) if !canToggleIntoMultiview { Label( "Multi-View is full. Remove a stream before adding MLB Network.", systemImage: "rectangle.split.2x2.fill" ) .font(.system(size: 16, weight: .semibold)) .foregroundStyle(.orange) .padding(.horizontal, 18) .padding(.vertical, 16) .background(panelBackground) } } } @ViewBuilder private var actionColumn: some View { VStack(alignment: .leading, spacing: 18) { VStack(alignment: .leading, spacing: 8) { Text("Actions") .font(.system(size: 28, weight: .bold, design: .rounded)) .foregroundStyle(.white) Text("Launch MLB Network full screen or place it in your Multi-View lineup.") .font(.system(size: 16, weight: .medium)) .foregroundStyle(.white.opacity(0.68)) .fixedSize(horizontal: false, vertical: true) } .padding(24) .background(panelBackground) actionButton( title: "Watch Full Screen", subtitle: "Open the channel in the main player", systemImage: "play.fill", fill: .blue.opacity(0.18) ) { onWatchFullScreen() } actionButton( title: added ? "Remove From Multi-View" : "Add to Multi-View", subtitle: added ? "Take MLB Network out of your active grid" : "Send MLB Network into an open tile", systemImage: added ? "minus.circle.fill" : "plus.circle.fill", fill: added ? .red.opacity(0.16) : .white.opacity(0.08), foreground: added ? .red : .white, disabled: !canToggleIntoMultiview ) { if added { viewModel.removeStream(id: "MLBN") } else { Task { await viewModel.addMLBNetwork() } } dismiss() } Spacer(minLength: 0) } } @ViewBuilder private func actionButton( title: String, subtitle: String, systemImage: String, fill: Color, foreground: Color = .white, disabled: Bool = false, action: @escaping () -> Void ) -> some View { Button(action: action) { HStack(alignment: .center, spacing: 16) { Image(systemName: systemImage) .font(.system(size: 22, weight: .bold)) .frame(width: 32) VStack(alignment: .leading, spacing: 5) { Text(title) .font(.system(size: 21, weight: .bold, design: .rounded)) Text(subtitle) .font(.system(size: 14, weight: .medium)) .foregroundStyle(foreground.opacity(0.68)) .fixedSize(horizontal: false, vertical: true) } Spacer(minLength: 0) } .foregroundStyle(foreground) .frame(maxWidth: .infinity, minHeight: 88, alignment: .leading) .padding(.horizontal, 22) .background( RoundedRectangle(cornerRadius: 24, style: .continuous) .fill(fill) ) } .platformCardStyle() .disabled(disabled) } @ViewBuilder private func overviewLine(icon: String, title: String, detail: String) -> some View { HStack(alignment: .top, spacing: 14) { Image(systemName: icon) .font(.system(size: 18, weight: .bold)) .foregroundStyle(.blue.opacity(0.9)) .frame(width: 26) VStack(alignment: .leading, spacing: 4) { Text(title) .font(.system(size: 18, weight: .bold)) .foregroundStyle(.white) Text(detail) .font(.system(size: 15, weight: .medium)) .foregroundStyle(.white.opacity(0.66)) .fixedSize(horizontal: false, vertical: true) } } } @ViewBuilder private func statusPill(title: String, color: Color) -> some View { Text(title) .font(.system(size: 13, weight: .black, design: .rounded)) .foregroundStyle(color) .padding(.horizontal, 13) .padding(.vertical, 9) .background(color.opacity(0.14)) .clipShape(Capsule()) } @ViewBuilder private func featurePill(title: String, systemImage: String) -> some View { Label(title, systemImage: systemImage) .font(.system(size: 15, weight: .semibold)) .foregroundStyle(.white.opacity(0.84)) .padding(.horizontal, 16) .padding(.vertical, 12) .background(.white.opacity(0.06)) .clipShape(Capsule()) } @ViewBuilder private var panelBackground: some View { RoundedRectangle(cornerRadius: 28, style: .continuous) .fill(.black.opacity(0.24)) .overlay { RoundedRectangle(cornerRadius: 28, style: .continuous) .strokeBorder(.white.opacity(0.08), lineWidth: 1) } } @ViewBuilder private var sheetBackground: some View { ZStack { LinearGradient( colors: [ Color(red: 0.04, green: 0.05, blue: 0.08), Color(red: 0.05, green: 0.07, blue: 0.11) ], startPoint: .topLeading, endPoint: .bottomTrailing ) Circle() .fill(.blue.opacity(0.22)) .frame(width: 540, height: 540) .blur(radius: 100) .offset(x: -280, y: -210) Circle() .fill(.cyan.opacity(0.14)) .frame(width: 460, height: 460) .blur(radius: 100) .offset(x: 350, y: 180) } } }