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.
106 lines
3.7 KiB
Swift
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
|