Add iOS/iPad target with platform-adaptive UI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,14 +11,14 @@ private func logDashboard(_ message: String) {
|
||||
enum SpecialPlaybackChannelConfig {
|
||||
static let werkoutNSFWStreamID = "WKNSFW"
|
||||
static let werkoutNSFWTitle = "Werkout NSFW"
|
||||
static let werkoutNSFWSubtitle = "Authenticated private HLS feed"
|
||||
static let werkoutNSFWFeedURLString = "https://dev.werkout.fitness/videos/nsfw_videos/"
|
||||
static let werkoutNSFWAuthToken = "15d7565cde9e8c904ae934f8235f68f6a24b4a03"
|
||||
static let werkoutNSFWSubtitle = "Authenticated OF media feed"
|
||||
static let werkoutNSFWFeedURLString = "https://ofapp.treytartt.com/api/media?users=tabatachang,werkout&type=video"
|
||||
static let werkoutNSFWCookie = "ofapp_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImlhdCI6MTc3NDY0MjEyNiwiZXhwIjoxNzc1MjQ2OTI2fQ.rDvZzLQ70MM9drHwA8hRxmqcTTgBGBHXxv1Cc55HSqc"
|
||||
static let werkoutNSFWTeamCode = "WK"
|
||||
|
||||
static var werkoutNSFWHeaders: [String: String] {
|
||||
[
|
||||
"authorization": "Token \(werkoutNSFWAuthToken)",
|
||||
"Cookie": werkoutNSFWCookie,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -50,15 +50,48 @@ enum SpecialPlaybackChannelConfig {
|
||||
|
||||
struct DashboardView: View {
|
||||
@Environment(GamesViewModel.self) private var viewModel
|
||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||
@State private var selectedGame: Game?
|
||||
@State private var fullScreenBroadcast: BroadcastSelection?
|
||||
@State private var pendingFullScreenBroadcast: BroadcastSelection?
|
||||
@State private var showMLBNetworkSheet = false
|
||||
@State private var showWerkoutNSFWSheet = false
|
||||
|
||||
private var horizontalPadding: CGFloat {
|
||||
#if os(iOS)
|
||||
horizontalSizeClass == .compact ? 20 : 32
|
||||
#else
|
||||
60
|
||||
#endif
|
||||
}
|
||||
|
||||
private var verticalPadding: CGFloat {
|
||||
#if os(iOS)
|
||||
horizontalSizeClass == .compact ? 24 : 32
|
||||
#else
|
||||
40
|
||||
#endif
|
||||
}
|
||||
|
||||
private var contentSpacing: CGFloat {
|
||||
#if os(iOS)
|
||||
horizontalSizeClass == .compact ? 32 : 42
|
||||
#else
|
||||
50
|
||||
#endif
|
||||
}
|
||||
|
||||
private var shelfCardWidth: CGFloat {
|
||||
#if os(iOS)
|
||||
horizontalSizeClass == .compact ? 300 : 360
|
||||
#else
|
||||
400
|
||||
#endif
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 50) {
|
||||
VStack(alignment: .leading, spacing: contentSpacing) {
|
||||
headerSection
|
||||
|
||||
if viewModel.isLoading {
|
||||
@@ -111,8 +144,8 @@ struct DashboardView: View {
|
||||
multiViewStatus
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 60)
|
||||
.padding(.vertical, 40)
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
.padding(.vertical, verticalPadding)
|
||||
}
|
||||
.onAppear {
|
||||
logDashboard("DashboardView appeared")
|
||||
@@ -210,7 +243,8 @@ struct DashboardView: View {
|
||||
}
|
||||
return SingleStreamPlaybackSource(
|
||||
url: url,
|
||||
httpHeaders: SpecialPlaybackChannelConfig.werkoutNSFWHeaders
|
||||
httpHeaders: SpecialPlaybackChannelConfig.werkoutNSFWHeaders,
|
||||
forceMuteAudio: true
|
||||
)
|
||||
}
|
||||
let stream = ActiveStream(
|
||||
@@ -240,7 +274,8 @@ struct DashboardView: View {
|
||||
}
|
||||
return SingleStreamPlaybackSource(
|
||||
url: nextURL,
|
||||
httpHeaders: SpecialPlaybackChannelConfig.werkoutNSFWHeaders
|
||||
httpHeaders: SpecialPlaybackChannelConfig.werkoutNSFWHeaders,
|
||||
forceMuteAudio: true
|
||||
)
|
||||
}
|
||||
|
||||
@@ -303,10 +338,10 @@ struct DashboardView: View {
|
||||
GameCardView(game: game) {
|
||||
selectedGame = game
|
||||
}
|
||||
.frame(width: 400)
|
||||
.frame(width: shelfCardWidth)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 20)
|
||||
.padding(.vertical, 12)
|
||||
}
|
||||
.scrollClipDisabled()
|
||||
}
|
||||
@@ -392,12 +427,19 @@ struct DashboardView: View {
|
||||
|
||||
@ViewBuilder
|
||||
private var featuredChannelsSection: some View {
|
||||
HStack(alignment: .top, spacing: 24) {
|
||||
mlbNetworkCard
|
||||
.frame(maxWidth: .infinity)
|
||||
ViewThatFits {
|
||||
HStack(alignment: .top, spacing: 24) {
|
||||
mlbNetworkCard
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
nsfwVideosCard
|
||||
.frame(maxWidth: .infinity)
|
||||
nsfwVideosCard
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
mlbNetworkCard
|
||||
nsfwVideosCard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,7 +478,7 @@ struct DashboardView: View {
|
||||
.background(.regularMaterial)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||
}
|
||||
.buttonStyle(.card)
|
||||
.platformCardStyle()
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
@@ -478,7 +520,7 @@ struct DashboardView: View {
|
||||
.background(.regularMaterial)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||
}
|
||||
.buttonStyle(.card)
|
||||
.platformCardStyle()
|
||||
}
|
||||
|
||||
// MARK: - Multi-View Status
|
||||
@@ -528,6 +570,7 @@ struct BroadcastSelection: Identifiable {
|
||||
struct WerkoutNSFWSheet: View {
|
||||
var onWatchFullScreen: () -> Void
|
||||
@Environment(GamesViewModel.self) private var viewModel
|
||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var isResolvingMultiViewSource = false
|
||||
@State private var multiViewErrorMessage: String?
|
||||
@@ -540,17 +583,41 @@ struct WerkoutNSFWSheet: View {
|
||||
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()
|
||||
|
||||
HStack(alignment: .top, spacing: 32) {
|
||||
overviewColumn
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
ViewThatFits {
|
||||
HStack(alignment: .top, spacing: 32) {
|
||||
overviewColumn
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
actionColumn
|
||||
.frame(width: 360, alignment: .leading)
|
||||
actionColumn
|
||||
.frame(width: 360, alignment: .leading)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 24) {
|
||||
overviewColumn
|
||||
actionColumn
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
.padding(38)
|
||||
.background(
|
||||
@@ -561,8 +628,8 @@ struct WerkoutNSFWSheet: View {
|
||||
.strokeBorder(.white.opacity(0.08), lineWidth: 1)
|
||||
}
|
||||
)
|
||||
.padding(.horizontal, 94)
|
||||
.padding(.vertical, 70)
|
||||
.padding(.horizontal, outerHorizontalPadding)
|
||||
.padding(.vertical, outerVerticalPadding)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,7 +778,8 @@ struct WerkoutNSFWSheet: View {
|
||||
label: SpecialPlaybackChannelConfig.werkoutNSFWTitle,
|
||||
game: SpecialPlaybackChannelConfig.werkoutNSFWGame,
|
||||
feedURL: SpecialPlaybackChannelConfig.werkoutNSFWFeedURL,
|
||||
headers: SpecialPlaybackChannelConfig.werkoutNSFWHeaders
|
||||
headers: SpecialPlaybackChannelConfig.werkoutNSFWHeaders,
|
||||
forceMuteAudio: true
|
||||
)
|
||||
isResolvingMultiViewSource = false
|
||||
if didAddStream {
|
||||
@@ -770,7 +838,7 @@ struct WerkoutNSFWSheet: View {
|
||||
.fill(fill)
|
||||
)
|
||||
}
|
||||
.buttonStyle(.card)
|
||||
.platformCardStyle()
|
||||
.disabled(disabled)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user