Move diagnostics into settings, remove the app-local notification module, unify the global theme switcher, refine privacy copy and the instance detail experience, and rebuild the app icon.
105 lines
2.6 KiB
Swift
105 lines
2.6 KiB
Swift
import SwiftUI
|
|
|
|
@main
|
|
struct MultiSimAdminApp: App {
|
|
@StateObject private var themeStore = ThemeStore.shared
|
|
@State private var tab: AppTab
|
|
|
|
init() {
|
|
let arguments = ProcessInfo.processInfo.arguments
|
|
if arguments.contains("--tab-messages") {
|
|
_tab = State(initialValue: .messages)
|
|
} else if arguments.contains("--tab-settings") {
|
|
_tab = State(initialValue: .settings)
|
|
} else {
|
|
_tab = State(initialValue: .fleet)
|
|
}
|
|
}
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
VStack(spacing: 0) {
|
|
switch tab {
|
|
case .fleet:
|
|
NavigationStack { FleetView().aiInlineTitle() }
|
|
case .messages:
|
|
NavigationStack { MessagesView().aiInlineTitle() }
|
|
case .settings:
|
|
NavigationStack { SettingsView().aiInlineTitle() }
|
|
}
|
|
AITabBar(selection: $tab)
|
|
}
|
|
.tint(AITheme.primary)
|
|
.preferredColorScheme(themeStore.mode.colorScheme)
|
|
#if os(iOS)
|
|
.toolbarBackground(.ultraThinMaterial, for: .navigationBar)
|
|
.toolbarBackground(.visible, for: .navigationBar)
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#if os(iOS)
|
|
extension View {
|
|
fileprivate func aiInlineTitle() -> some View {
|
|
navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
#else
|
|
extension View {
|
|
fileprivate func aiInlineTitle() -> some View { self }
|
|
}
|
|
#endif
|
|
|
|
enum AppTab: Hashable {
|
|
case fleet
|
|
case messages
|
|
case settings
|
|
}
|
|
|
|
struct AITabBar: View {
|
|
@Binding var selection: AppTab
|
|
|
|
var body: some View {
|
|
HStack(spacing: 0) {
|
|
tab(.fleet, "节点", "server.rack")
|
|
tab(.messages, "消息", "message")
|
|
tab(.settings, "设置", "gearshape")
|
|
}
|
|
.frame(height: 62)
|
|
.background {
|
|
ZStack {
|
|
Rectangle()
|
|
.fill(.ultraThinMaterial)
|
|
Rectangle()
|
|
.fill(AITheme.chrome)
|
|
}
|
|
.ignoresSafeArea(edges: .bottom)
|
|
}
|
|
.overlay(alignment: .top) {
|
|
Rectangle()
|
|
.fill(AITheme.line)
|
|
.frame(height: 1)
|
|
}
|
|
}
|
|
|
|
private func tab(_ value: AppTab, _ title: String, _ systemImage: String) -> some View {
|
|
let isSelected = selection == value
|
|
return Button {
|
|
selection = value
|
|
} label: {
|
|
VStack(spacing: 4) {
|
|
Image(systemName: systemImage)
|
|
.font(.system(size: 18, weight: .semibold))
|
|
Text(title)
|
|
.font(.caption2.weight(.semibold))
|
|
}
|
|
.foregroundStyle(isSelected ? AITheme.primary : AITheme.muted)
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
}
|
|
.buttonStyle(.plain)
|
|
.accessibilityLabel(title)
|
|
.accessibilityAddTraits(isSelected ? [.isSelected] : [])
|
|
}
|
|
}
|