Files
ProxyIOS/App/ContentView.swift
Trey t 148bc3887c Add iPad support, auto-pinning, and comprehensive logging
- Adaptive iPhone/iPad layout with NavigationSplitView sidebar
- Auto-detect SSL-pinned domains, fall back to passthrough
- Certificate install via local HTTP server (Safari profile flow)
- App Group-backed CA, per-domain leaf cert LRU cache
- DB-backed config repository, Darwin notification throttling
- Rules engine, breakpoint rules, pinned domain tracking
- os.Logger instrumentation across tunnel/proxy/mitm/capture/cert/rules/db/ipc/ui
- Fix dyld framework embed, race conditions, thread safety
2026-04-11 12:52:18 -05:00

133 lines
3.9 KiB
Swift

import SwiftUI
struct ContentView: View {
@Environment(AppState.self) private var appState
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
enum Tab: String, Hashable, CaseIterable {
case home = "Home"
case pin = "Pin"
case compose = "Compose"
case more = "More"
var icon: String {
switch self {
case .home: return "house.fill"
case .pin: return "pin.fill"
case .compose: return "square.and.pencil"
case .more: return "ellipsis.circle.fill"
}
}
}
@State private var selectedTab: Tab = .home
var body: some View {
Group {
if horizontalSizeClass == .regular {
iPadLayout
} else {
iPhoneLayout
}
}
.overlay {
if appState.isLocked {
lockScreen
}
}
}
private var iPhoneLayout: some View {
TabView(selection: $selectedTab) {
NavigationStack { HomeView() }
.tabItem { Label("Home", systemImage: "house.fill") }
.tag(Tab.home)
NavigationStack { PinView() }
.tabItem { Label("Pin", systemImage: "pin.fill") }
.tag(Tab.pin)
NavigationStack { ComposeListView() }
.tabItem { Label("Compose", systemImage: "square.and.pencil") }
.tag(Tab.compose)
NavigationStack { MoreView() }
.tabItem { Label("More", systemImage: "ellipsis.circle.fill") }
.tag(Tab.more)
}
}
private var iPadLayout: some View {
NavigationSplitView {
iPadSidebar
} detail: {
iPadDetail
}
}
private var iPadSidebar: some View {
VStack(spacing: 4) {
ForEach(Tab.allCases, id: \.self) { tab in
iPadSidebarButton(tab: tab)
}
Spacer()
}
.padding(.top, 8)
.navigationTitle("Proxy")
}
private func iPadSidebarButton(tab: Tab) -> some View {
Button {
selectedTab = tab
} label: {
Label(tab.rawValue, systemImage: tab.icon)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 20)
.padding(.vertical, 14)
.background(
selectedTab == tab ? Color.accentColor.opacity(0.12) : Color.clear,
in: RoundedRectangle(cornerRadius: 10)
)
.foregroundStyle(selectedTab == tab ? Color.accentColor : Color.primary)
}
.buttonStyle(.plain)
.padding(.horizontal, 12)
}
@ViewBuilder
private var iPadDetail: some View {
NavigationStack {
switch selectedTab {
case .home: HomeView()
case .pin: PinView()
case .compose: ComposeListView()
case .more: MoreView()
}
}
}
private var lockScreen: some View {
ZStack {
Color(.systemBackground).ignoresSafeArea()
VStack(spacing: 24) {
Image(systemName: "lock.fill")
.font(.system(size: 48))
.foregroundStyle(.secondary)
Text("Unlock Proxy")
.font(.title2.weight(.semibold))
Button {
appState.authenticate()
} label: {
Text("Unlock")
.font(.headline)
.frame(maxWidth: 300)
.padding()
.background(.blue, in: RoundedRectangle(cornerRadius: 12))
.foregroundStyle(.white)
}
}
}
.onAppear { appState.authenticate() }
}
}