import SwiftUI // ═══════════════════════════════════════════════════════════════════════════════ // NTS Anchor View — 36 Global Atomic Time Servers // Montana Protocol v3.17.0 // // Визуализация NTS-якорей: каждое τ₂ окно привязано к глобальному // атомному времени через TLS 1.3 handshake с 36 серверами (RFC 8915). // ═══════════════════════════════════════════════════════════════════════════════ struct NTSAnchorView: View { @EnvironmentObject var engine: PresenceEngine @Environment(\.dismiss) private var dismiss @State private var showSidebar = false // Data @State private var ntsStatus: [String: Any] = [:] @State private var serverList: [[String: Any]] = [] @State private var regions: [String: Any] = [:] @State private var latestAnchor: [String: Any]? = nil @State private var attestations: [[String: Any]] = [] @State private var isLoading = true @State private var errorText = "" @State private var selectedRegion: String? = nil // Colors private let cyan = Color(red: 0, green: 0.83, blue: 1) private let gold = Color(red: 0.85, green: 0.68, blue: 0.25) private let cardBg = Color(red: 0.09, green: 0.09, blue: 0.12) private let green = Color(red: 0.2, green: 0.85, blue: 0.4) // Region display order private let regionOrder = ["GLOBAL", "EU", "RU", "US", "SA", "ASIA", "OCEANIA", "POLES"] private let regionEmoji: [String: String] = [ "GLOBAL": "\u{1F310}", "EU": "\u{1F1EA}\u{1F1FA}", "RU": "\u{1F1F7}\u{1F1FA}", "US": "\u{1F1FA}\u{1F1F8}", "SA": "\u{1F30E}", "ASIA": "\u{1F30F}", "OCEANIA": "\u{1F30F}", "POLES": "\u{2744}\u{FE0F}" ] private let regionNames: [String: String] = [ "GLOBAL": "Global", "EU": "Europe", "RU": "Russia", "US": "USA", "SA": "South America", "ASIA": "Asia", "OCEANIA": "Oceania", "POLES": "Poles" ] var body: some View { ZStack(alignment: .leading) { VStack(spacing: 0) { // Burger menu HStack { Button(action: { withAnimation(.easeInOut(duration: 0.3)) { showSidebar = true } }) { Image(systemName: "line.3.horizontal") .font(.system(size: 18, weight: .medium)) .foregroundColor(gold) .padding(8) } .buttonStyle(.plain) Spacer() } .padding(.horizontal, 20) .padding(.top, 24) // Header HStack(spacing: 6) { Image(systemName: "shield.checkered") .foregroundColor(cyan) .font(.system(size: 16)) Text("NTS Anchor") .font(.system(size: 14, weight: .bold)) Text("36 Atomic Servers") .font(.system(size: 10)) .foregroundColor(.secondary) Spacer() Button(action: { loadData() }) { Image(systemName: "arrow.clockwise") .foregroundColor(.secondary) } .buttonStyle(.plain) Button(action: { dismiss() }) { Image(systemName: "xmark.circle.fill") .foregroundColor(.secondary) } .buttonStyle(.plain) } .padding(.horizontal, 20) .padding(.top, 4) .padding(.bottom, 8) Divider() if isLoading { Spacer() ProgressView().controlSize(.small) Text("Loading NTS status...") .font(.caption).foregroundColor(.secondary) Spacer() } else if !errorText.isEmpty { Spacer() VStack(spacing: 8) { Image(systemName: "exclamationmark.triangle") .font(.system(size: 24)).foregroundColor(.orange) Text(errorText) .font(.caption).foregroundColor(.red) .multilineTextAlignment(.center) } Spacer() } else { ScrollView { VStack(spacing: 12) { // Status card statusCard // Region grid regionGrid // Latest anchor details if let anchor = latestAnchor { anchorDetailCard(anchor) } // Server list serverListSection } .padding(.horizontal, 20) .padding(.top, 12) .padding(.bottom, 20) } } } .background(Color.black) // Sidebar if showSidebar { SharedSidebar(isVisible: $showSidebar) .environmentObject(engine) } } .onAppear { loadData() } } // ─── Status Card ────────────────────────────────────────────── private var statusCard: some View { VStack(spacing: 8) { HStack { let enabled = ntsStatus["nts_enabled"] as? Bool ?? false Circle() .fill(enabled ? green : .red) .frame(width: 8, height: 8) Text(enabled ? "NTS Active" : "NTS Disabled") .font(.system(size: 11, weight: .bold)) .foregroundColor(enabled ? green : .red) Spacer() let coverage = ntsStatus["coverage"] as? String ?? "0/0" Text(coverage) .font(.system(size: 11, weight: .bold, design: .monospaced)) .foregroundColor(gold) Text("anchored") .font(.system(size: 9)) .foregroundColor(.secondary) } HStack(spacing: 16) { statPill( label: "Servers", value: "\(serverList.count)", icon: "server.rack" ) statPill( label: "Regions", value: "\(regions.count)", icon: "globe" ) let anchors = ntsStatus["total_anchors"] as? Int ?? 0 statPill( label: "Anchors", value: "\(anchors)", icon: "lock.shield" ) } } .padding(12) .background(cardBg) .cornerRadius(10) } private func statPill(label: String, value: String, icon: String) -> some View { HStack(spacing: 4) { Image(systemName: icon) .font(.system(size: 9)) .foregroundColor(cyan) Text(value) .font(.system(size: 12, weight: .bold, design: .monospaced)) Text(label) .font(.system(size: 9)) .foregroundColor(.secondary) } .frame(maxWidth: .infinity) } // ─── Region Grid ────────────────────────────────────────────── private var regionGrid: some View { VStack(alignment: .leading, spacing: 6) { Text("REGIONS") .font(.system(size: 9, weight: .bold)) .foregroundColor(.secondary) LazyVGrid(columns: [ GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible()) ], spacing: 6) { ForEach(regionOrder, id: \.self) { region in let count = serverCountForRegion(region) if count > 0 { regionCell(region: region, count: count) } } } } } private func regionCell(region: String, count: Int) -> some View { let isSelected = selectedRegion == region return VStack(spacing: 2) { Text(regionEmoji[region] ?? "") .font(.system(size: 16)) Text("\(count)") .font(.system(size: 12, weight: .bold, design: .monospaced)) Text(regionNames[region] ?? region) .font(.system(size: 8)) .foregroundColor(.secondary) } .frame(maxWidth: .infinity) .padding(.vertical, 6) .background(isSelected ? cyan.opacity(0.15) : cardBg) .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(isSelected ? cyan : Color.clear, lineWidth: 1) ) .onTapGesture { withAnimation(.easeInOut(duration: 0.2)) { selectedRegion = selectedRegion == region ? nil : region } } } // ─── Anchor Detail Card ─────────────────────────────────────── private func anchorDetailCard(_ anchor: [String: Any]) -> some View { VStack(alignment: .leading, spacing: 8) { HStack { Image(systemName: "lock.shield.fill") .foregroundColor(gold) Text("Latest NTS Anchor") .font(.system(size: 11, weight: .bold)) Spacer() } if let contentHash = anchor["content_hash"] as? String { hashRow(label: "Content Hash", hash: contentHash) } if let anchorHash = anchor["anchor_hash"] as? String { hashRow(label: "Anchor Hash", hash: anchorHash) } HStack(spacing: 12) { if let servers = anchor["server_count"] as? Int { detailPill(icon: "server.rack", value: "\(servers)", label: "servers") } if let regionCount = anchor["region_count"] as? Int { detailPill(icon: "globe", value: "\(regionCount)", label: "regions") } if let spreadNs = anchor["timestamp_spread_ns"] as? Int { let spreadMs = Double(spreadNs) / 1_000_000.0 detailPill(icon: "clock", value: String(format: "%.1fms", spreadMs), label: "spread") } } // Attestation list if let atts = anchor["attestations"] as? [[String: Any]], !atts.isEmpty { VStack(alignment: .leading, spacing: 2) { Text("ATTESTATIONS (\(atts.count))") .font(.system(size: 8, weight: .bold)) .foregroundColor(.secondary) .padding(.top, 4) ForEach(Array(atts.prefix(12).enumerated()), id: \.offset) { _, att in attestationRow(att) } if atts.count > 12 { Text("+ \(atts.count - 12) more...") .font(.system(size: 9)) .foregroundColor(.secondary) } } } } .padding(12) .background(cardBg) .cornerRadius(10) } private func attestationRow(_ att: [String: Any]) -> some View { let host = att["server_host"] as? String ?? "?" let region = att["server_region"] as? String ?? "" let tls = att["tls_version"] as? String ?? "" let cert = att["cert_fingerprint"] as? String ?? "" return HStack(spacing: 4) { Circle().fill(green).frame(width: 4, height: 4) Text(regionEmoji[region] ?? "") .font(.system(size: 8)) Text(host) .font(.system(size: 9, design: .monospaced)) .foregroundColor(.white) .lineLimit(1) Spacer() Text(tls) .font(.system(size: 8)) .foregroundColor(tls.contains("1.3") ? green : .orange) Text(String(cert.prefix(8))) .font(.system(size: 8, design: .monospaced)) .foregroundColor(.secondary) } .padding(.vertical, 1) } private func hashRow(label: String, hash: String) -> some View { HStack { Text(label) .font(.system(size: 9)) .foregroundColor(.secondary) Spacer() Text(String(hash.prefix(16)) + "...") .font(.system(size: 9, design: .monospaced)) .foregroundColor(cyan) } } private func detailPill(icon: String, value: String, label: String) -> some View { HStack(spacing: 3) { Image(systemName: icon) .font(.system(size: 8)) .foregroundColor(gold) Text(value) .font(.system(size: 10, weight: .bold, design: .monospaced)) Text(label) .font(.system(size: 8)) .foregroundColor(.secondary) } } // ─── Server List ────────────────────────────────────────────── private var serverListSection: some View { VStack(alignment: .leading, spacing: 6) { Text("ALL SERVERS (\(filteredServers.count))") .font(.system(size: 9, weight: .bold)) .foregroundColor(.secondary) ForEach(Array(filteredServers.enumerated()), id: \.offset) { idx, server in serverRow(server, index: idx + 1) } } } private var filteredServers: [[String: Any]] { if let region = selectedRegion { return serverList.filter { ($0["region"] as? String) == region } } return serverList } private func serverRow(_ server: [String: Any], index: Int) -> some View { let host = server["host"] as? String ?? "?" let region = server["region"] as? String ?? "" let org = server["org"] as? String ?? "" let country = server["country"] as? String ?? "" let port = server["port"] as? Int ?? 4460 return HStack(spacing: 6) { Text("\(index)") .font(.system(size: 8, design: .monospaced)) .foregroundColor(.secondary) .frame(width: 18, alignment: .trailing) Text(country) .font(.system(size: 10)) VStack(alignment: .leading, spacing: 1) { Text(host) .font(.system(size: 9, weight: .medium, design: .monospaced)) .foregroundColor(.white) .lineLimit(1) Text("\(org) : \(port)") .font(.system(size: 8)) .foregroundColor(.secondary) .lineLimit(1) } Spacer() Text(regionNames[region] ?? region) .font(.system(size: 8)) .foregroundColor(cyan) .padding(.horizontal, 4) .padding(.vertical, 1) .background(cyan.opacity(0.1)) .cornerRadius(3) } .padding(.vertical, 3) .padding(.horizontal, 8) .background(cardBg) .cornerRadius(6) } // ─── Data Loading ───────────────────────────────────────────── private func loadData() { isLoading = true errorText = "" Task { @MainActor in do { async let fetchServers = engine.api.fetchNTSServers() async let fetchStatus = engine.api.fetchNTSStatus() let serversResult = try await fetchServers let statusResult = try await fetchStatus serverList = serversResult["servers"] as? [[String: Any]] ?? [] regions = serversResult["regions"] as? [String: Any] ?? [:] ntsStatus = statusResult latestAnchor = statusResult["latest_anchor"] as? [String: Any] isLoading = false } catch { errorText = "NTS data unavailable" isLoading = false } } } private func serverCountForRegion(_ region: String) -> Int { return serverList.filter { ($0["region"] as? String) == region }.count } }