Owners can list/revoke devices and approve TV codes in the iOS app; TV shows a vmc:// QR, and Films/Series get visible chips, titles, and a non-overlapping A–Z index. Co-authored-by: Cursor <cursoragent@cursor.com>
71 lines
2 KiB
Swift
71 lines
2 KiB
Swift
import SwiftUI
|
|
|
|
struct RootView: View {
|
|
@EnvironmentObject private var app: AppModel
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
McColors.bg.ignoresSafeArea()
|
|
switch app.phase {
|
|
case .boot:
|
|
SplashView()
|
|
case .server:
|
|
ServerChoiceScreen()
|
|
case .auth:
|
|
AuthChoiceScreen()
|
|
case .password:
|
|
PasswordLoginScreen()
|
|
case .link:
|
|
LinkScreen()
|
|
case .profiles:
|
|
ProfilesScreen()
|
|
case .main:
|
|
MainShell()
|
|
}
|
|
}
|
|
.animation(.easeInOut(duration: 0.28), value: app.phase)
|
|
.sheet(item: approveSheetBinding) { item in
|
|
DeviceApproveSheet(code: item.code)
|
|
.environmentObject(app)
|
|
.presentationDetents([.medium, .large])
|
|
}
|
|
}
|
|
|
|
private var approveSheetBinding: Binding<ApproveCodeItem?> {
|
|
Binding(
|
|
get: {
|
|
guard let code = app.pendingApproveCode, app.phase == .main else { return nil }
|
|
return ApproveCodeItem(code: code)
|
|
},
|
|
set: { newValue in
|
|
if newValue == nil { app.clearPendingApprove() }
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
private struct ApproveCodeItem: Identifiable {
|
|
let code: String
|
|
var id: String { code }
|
|
}
|
|
|
|
struct SplashView: View {
|
|
@State private var pulse = false
|
|
|
|
var body: some View {
|
|
VStack(spacing: 18) {
|
|
Text("VMC")
|
|
.font(McFonts.brand)
|
|
.foregroundStyle(McColors.accent)
|
|
.scaleEffect(pulse ? 1.04 : 0.96)
|
|
.shadow(color: McColors.accent.opacity(0.45), radius: pulse ? 24 : 8)
|
|
ProgressView()
|
|
.tint(McColors.textDim)
|
|
}
|
|
.onAppear {
|
|
withAnimation(.easeInOut(duration: 1.1).repeatForever(autoreverses: true)) {
|
|
pulse = true
|
|
}
|
|
}
|
|
}
|
|
}
|