import SwiftUI import Combine enum AppScreen { case auth, landing, practice } final class AppState: ObservableObject { @Published var token: String? @Published var screen: AppScreen = .auth @Published var showScriptsOnAppear: Bool = false init() { token = Keychain.load(key: "mimio_token") if let t = token, !t.isEmpty { screen = .landing } } func login(token: String) { self.token = token screen = .landing } func logout() { Keychain.delete(key: "mimio_token") UserDefaults.standard.removeObject(forKey: "mimio_user_id") token = nil screen = .auth showScriptsOnAppear = false } } @main struct MimioApp: App { @StateObject private var appState = AppState() var body: some Scene { WindowGroup { screenView } } @ViewBuilder private var screenView: some View { switch appState.screen { case .auth: AuthView { t in appState.login(token: t) } case .landing: LandingView().environmentObject(appState) case .practice: ContentView(token: appState.token ?? "").environmentObject(appState) } } } struct LandingView: View { @EnvironmentObject var appState: AppState @State private var doorsOpen = false @State private var logoOpacity = 0.0 @State private var taglineOpacity = 0.0 @State private var buttonOpacity = 0.0 @State private var animationDone = false private let goldColor = Color(red: 0.788, green: 0.643, blue: 0.298) private let navyColor = Color(red: 0.039, green: 0.059, blue: 0.118) private let stoneColor = Color(red: 0.769, green: 0.659, blue: 0.510) private let screenW = UIScreen.main.bounds.width var body: some View { ZStack { navyColor.ignoresSafeArea() if !animationDone { ZStack { if let img = UIImage(named: "mountain_door") { Image(uiImage: img) .resizable().scaledToFill().ignoresSafeArea() } Color.black.opacity(0.4).ignoresSafeArea() VStack(spacing: 16) { Text("MIMIO") .font(.system(size: 52, weight: .black, design: .serif)) .foregroundColor(.white).tracking(10) .shadow(color: goldColor.opacity(0.6), radius: 20) .opacity(logoOpacity) Text("hear yourself, clearer") .font(.system(size: 14, weight: .light)) .foregroundColor(stoneColor.opacity(0.8)).tracking(3) .opacity(taglineOpacity) } HStack(spacing: 0) { if let left = UIImage(named: "door_left") { Image(uiImage: left).resizable().scaledToFill() .frame(width: screenW / 2).clipped() .offset(x: doorsOpen ? -(screenW / 2) : 0) .animation(.easeInOut(duration: 1.8), value: doorsOpen) } if let right = UIImage(named: "door_right") { Image(uiImage: right).resizable().scaledToFill() .frame(width: screenW / 2).clipped() .offset(x: doorsOpen ? (screenW / 2) : 0) .animation(.easeInOut(duration: 1.8), value: doorsOpen) } }.ignoresSafeArea() } } if animationDone { VStack(spacing: 0) { Spacer() VStack(spacing: 22) { Text("MIMIO") .font(.system(size: 52, weight: .black, design: .serif)) .foregroundColor(.white).tracking(10) .shadow(color: goldColor.opacity(0.6), radius: 20) Text("hear yourself, clearer") .font(.system(size: 14, weight: .light)) .foregroundColor(stoneColor.opacity(0.8)).tracking(3) } Spacer() Button { appState.showScriptsOnAppear = true appState.screen = .practice } label: { Text("START LEARNING") .font(.system(size: 16, weight: .bold, design: .monospaced)) .foregroundColor(.white).frame(maxWidth: .infinity) .padding(.vertical, 18) .overlay(Capsule().stroke(goldColor, lineWidth: 1.5)) } .padding(.horizontal, 40).padding(.bottom, 60).opacity(buttonOpacity) } .transition(.opacity) } } .onAppear { withAnimation(.easeIn(duration: 1.2)) { logoOpacity = 1.0 } withAnimation(.easeIn(duration: 1.2).delay(0.4)) { taglineOpacity = 1.0 } DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) { doorsOpen = true } DispatchQueue.main.asyncAfter(deadline: .now() + 4.3) { withAnimation(.easeIn(duration: 0.6)) { animationDone = true } DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { withAnimation(.easeIn(duration: 0.8)) { buttonOpacity = 1.0 } } } } } }