burrow/Apple/UI/NetworkCarouselView.swift
JettChenT d1638726ca Add proxy subscription runtime support
Add daemon RPCs, Apple and GTK import flows, packet proxy runtime support, diagnostics, and BEPs for proxy subscription handling.

Redact subscription URL secrets from fetch errors before they reach logs or UI surfaces.
2026-06-01 15:23:17 +08:00

106 lines
3.7 KiB
Swift

import SwiftUI
struct NetworkCarouselView: View {
var networks: [NetworkCardModel]
var selectedNetworkID: Int32?
var onSelect: ((NetworkCardModel) -> Void)?
init(
networks: [NetworkCardModel],
selectedNetworkID: Int32? = nil,
onSelect: ((NetworkCardModel) -> Void)? = nil
) {
self.networks = networks
self.selectedNetworkID = selectedNetworkID
self.onSelect = onSelect
}
var body: some View {
Group {
if networks.isEmpty {
#if os(iOS)
VStack(alignment: .leading, spacing: 6) {
Text("No stored networks yet")
.font(.headline)
Text("WireGuard and Tailnet networks show up here as soon as you add one.")
.font(.footnote)
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(
RoundedRectangle(cornerRadius: 18)
.fill(.thinMaterial)
)
#else
ContentUnavailableView(
"No Networks Yet",
systemImage: "network.slash",
description: Text("Add a WireGuard network, or save a Tailnet account so Burrow can store a managed network when the daemon is reachable.")
)
.frame(maxWidth: .infinity, minHeight: 175)
#endif
} else {
#if os(macOS)
LazyVStack(alignment: .leading, spacing: 12) {
ForEach(networks) { network in
networkCard(network)
.frame(maxWidth: 560)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
#else
ScrollView(.horizontal) {
LazyHStack {
ForEach(networks) { network in
networkCard(network)
.containerRelativeFrame(.horizontal, count: 10, span: 7, spacing: 0, alignment: .center)
.scrollTransition(.interactive, axis: .horizontal) { content, phase in
content
.scaleEffect(1.0 - abs(phase.value) * 0.1)
}
}
}
}
.scrollTargetLayout()
.scrollClipDisabled()
.scrollIndicators(.hidden)
.defaultScrollAnchor(.center)
.scrollTargetBehavior(.viewAligned)
.containerRelativeFrame(.horizontal)
#endif
}
}
}
@ViewBuilder
private func networkCard(_ network: NetworkCardModel) -> some View {
if let onSelect {
Button {
onSelect(network)
} label: {
NetworkView(network: network)
.overlay(selectionOverlay(for: network))
}
.buttonStyle(.plain)
} else {
NetworkView(network: network)
}
}
private func selectionOverlay(for network: NetworkCardModel) -> some View {
RoundedRectangle(cornerRadius: 10)
.stroke(
selectedNetworkID == network.id ? Color.accentColor : Color.clear,
lineWidth: 3
)
}
}
#if DEBUG
struct NetworkCarouselView_Previews: PreviewProvider {
static var previews: some View {
NetworkCarouselView(networks: [WireGuardCard(id: 1, detail: "10.13.13.2/24 · wg.burrow.rs:51820").card])
}
}
#endif