clean up and fix the broken ui

This commit is contained in:
se2crid
2025-12-12 15:21:11 +01:00
parent 0774986e79
commit 249d82eda1
6 changed files with 75 additions and 106 deletions

View File

@@ -685,12 +685,13 @@ struct ContentView: View {
var body: some View {
NBNavigationStack {
ScrollView(showsIndicators: false) {
VStack(spacing: 20) {
ScrollView {
VStack(spacing: 16) {
TitleWithSettingsRow(showSettings: $showSettings)
StatusOverviewCard()
ConnectivityControlsCard(
autoConnect: $autoConnect,
action: {
tunnelManager.tunnelStatus == .connected ? tunnelManager.stopVPN() : tunnelManager.startVPN()
}
@@ -701,24 +702,16 @@ struct ContentView: View {
}
}
.padding(.horizontal)
.padding(.vertical, 24)
.padding(.vertical, 12)
.frame(maxWidth: .infinity, alignment: .top)
}
.applyAdaptiveBounce()
.background(backgroundColor.ignoresSafeArea())
.navigationTitle("LocalDevVPN")
.navigationTitle("")
#if os(iOS)
.navigationBarTitleDisplayMode(.large)
.navigationBarTitleDisplayMode(.inline)
#endif
.tvOSNavigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
showSettings = true
} label: {
Image(systemName: "gear")
.foregroundColor(.primary)
}
}
}
.onChange(of: tunnelManager.waitingOnSettings) { finished in
if tunnelManager.tunnelStatus != .connected && autoConnect && finished {
tunnelManager.startVPN()
@@ -742,6 +735,31 @@ struct ContentView: View {
}
}
struct TitleWithSettingsRow: View {
@Binding var showSettings: Bool
var body: some View {
HStack(spacing: 10) {
Text("LocalDevVPN")
.font(.largeTitle)
.fontWeight(.bold)
.accessibilityAddTraits(.isHeader)
Spacer()
Button {
showSettings = true
} label: {
Image(systemName: "gear")
.font(.title2.weight(.semibold))
.foregroundColor(.primary)
}
.accessibilityLabel(Text("settings"))
}
.padding(.top, 4)
}
}
extension View {
@ViewBuilder
func tvOSNavigationBarTitleDisplayMode(_ displayMode: NavigationBarItem.TitleDisplayMode) -> some View {
@@ -751,10 +769,20 @@ extension View {
self
#endif
}
@ViewBuilder
func applyAdaptiveBounce() -> some View {
if #available(iOS 16.4, tvOS 16.4, *) {
scrollBounceBehavior(.basedOnSize)
} else {
self
}
}
}
struct StatusOverviewCard: View {
@StateObject private var tunnelManager = TunnelManager.shared
@AppStorage("TunnelDeviceIP") private var deviceIP = "10.7.0.0"
var body: some View {
DashboardCard {
@@ -785,9 +813,12 @@ struct StatusOverviewCard: View {
Spacer()
Text(Date(), style: .time)
.font(.caption)
.foregroundColor(.secondary)
HStack(spacing: 4) {
Text("connected_at")
Text(Date(), style: .time)
}
.font(.caption)
.foregroundColor(.secondary)
}
}
}
@@ -796,9 +827,9 @@ struct StatusOverviewCard: View {
private var statusTip: String {
switch tunnelManager.tunnelStatus {
case .connected:
return NSLocalizedString("connected_to_10.7.0.1", comment: "")
return String(format: NSLocalizedString("connected_to_ip", comment: ""), deviceIP)
case .connecting:
return NSLocalizedString("macos_might_ask_you_to_approve_the_vpn", comment: "")
return NSLocalizedString("ios_might_ask_you_to_allow_the_vpn", comment: "")
case .disconnecting:
return NSLocalizedString("disconnecting_safely", comment: "")
case .error:
@@ -812,13 +843,13 @@ struct StatusOverviewCard: View {
struct StatusGlyphView: View {
@StateObject private var tunnelManager = TunnelManager.shared
@State private var ringScale: CGFloat = 1.0
@Environment(\.accessibilityReduceMotion) private var reduceMotion
var body: some View {
ZStack {
Circle()
.stroke(tunnelManager.tunnelStatus.color.opacity(0.25), lineWidth: 6)
.scaleEffect(reduceMotion ? 1 : ringScale, anchor: .center)
.scaleEffect(ringScale, anchor: .center)
.animation(.easeInOut(duration: 1.2).repeatForever(autoreverses: true), value: ringScale)
Circle()
.fill(tunnelManager.tunnelStatus.color.opacity(0.15))
@@ -828,30 +859,17 @@ struct StatusGlyphView: View {
.foregroundColor(tunnelManager.tunnelStatus.color)
}
.frame(width: 92, height: 92)
.onAppear(perform: restartPulse)
.onChange(of: tunnelManager.tunnelStatus) { _ in
restartPulse()
}
.onChange(of: reduceMotion) { _ in
restartPulse()
}
.onAppear(perform: startPulse)
}
private func restartPulse() {
guard !reduceMotion else {
ringScale = 1
return
}
ringScale = 1.0
withAnimation(.easeInOut(duration: 1.2).repeatForever(autoreverses: true)) {
private func startPulse() {
DispatchQueue.main.async {
ringScale = 1.08
}
}
}
struct ConnectivityControlsCard: View {
@Binding var autoConnect: Bool
let action: () -> Void
var body: some View {
@@ -866,24 +884,13 @@ struct ConnectivityControlsCard: View {
}
ConnectionButton(action: action)
Toggle(isOn: $autoConnect) {
VStack(alignment: .leading, spacing: 2) {
Text("auto-connect_on_launch")
.fontWeight(.semibold)
Text("resume_your_last_state_automatically")
.font(.caption)
.foregroundColor(.secondary)
}
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
}
}
}
struct ConnectionInfoRow: View {
let title: String
let title: LocalizedStringKey
let value: String
let icon: String
@@ -972,16 +979,14 @@ struct ConnectionButton: View {
struct ConnectionStatsView: View {
@StateObject private var tunnelManager = TunnelManager.shared
@State private var time = 0
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
@AppStorage("TunnelDeviceIP") private var deviceIP = "10.7.0.0"
@AppStorage("TunnelFakeIP") private var fakeIP = "10.7.0.1"
@AppStorage("TunnelSubnetMask") private var subnetMask = "255.255.255.0"
var body: some View {
DashboardCard {
VStack(alignment: .leading, spacing: 18) {
VStack(alignment: .leading, spacing: 4) {
VStack(alignment: .leading, spacing: 12) {
VStack(alignment: .leading, spacing: 3) {
Text("session_details")
.font(.headline)
Text("live_stats_while_the_tunnel_is_connected")
@@ -989,19 +994,6 @@ struct ConnectionStatsView: View {
.foregroundColor(.secondary)
}
HStack(spacing: 16) {
StatItemView(
title: "time_connected",
value: formattedTime,
icon: "clock.fill"
)
StatItemView(
title: "status",
value: statusValue,
icon: tunnelManager.tunnelStatus.systemImage
)
}
Divider()
Text("network_configuration")
@@ -1027,36 +1019,8 @@ struct ConnectionStatsView: View {
)
}
}
.onReceive(timer) { _ in
time += 1
}
}
var formattedTime: String {
let minutes = (time / 60) % 60
let hours = time / 3600
let seconds = time % 60
if hours > 0 {
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
} else {
return String(format: "%02d:%02d", minutes, seconds)
}
}
private var statusValue: String {
switch tunnelManager.tunnelStatus {
case .connected:
return NSLocalizedString("Active", comment: "")
case .connecting:
return NSLocalizedString("Connecting", comment: "")
case .disconnecting:
return NSLocalizedString("Disconnecting", comment: "")
case .error:
return NSLocalizedString("Error", comment: "")
default:
return NSLocalizedString("Idle", comment: "")
}
}
}
struct StatItemView: View {

View File

@@ -11,8 +11,9 @@
"local_tunnel_active" = "Local Tunnel Active";
"local_tunnel_inactive" = "Local Tunnel Inactive";
"connected_to_10.7.0.1" = "Connected to 10.7.0.1";
"macos_might_ask_you_to_approve_the_vpn" = "macOS might ask you to approve the VPN";
"connected_to_ip" = "Connected to %@";
"connected_at" = "Connected at";
"ios_might_ask_you_to_allow_the_vpn" = "iOS might ask you to allow the VPN";
"disconnecting_safely" = "Disconnecting safely…";
"open_settings_to_review_details" = "Open Settings to review details";
"tap_connect_to_create_the_tunnel" = "Tap connect to create the tunnel";

View File

@@ -11,8 +11,9 @@
"local_tunnel_active" = "Túnel local activo";
"local_tunnel_inactive" = "Túnel local inactivo";
"connected_to_10.7.0.1" = "Conectado a 10.7.0.1";
"macos_might_ask_you_to_approve_the_vpn" = "macOS podría pedirte que apruebes la VPN";
"connected_to_ip" = "Conectado a %@";
"connected_at" = "Connected at";
"ios_might_ask_you_to_allow_the_vpn" = "iOS podría pedirte que permitas la VPN";
"disconnecting_safely" = "Desconectando de forma segura…";
"open_settings_to_review_details" = "Abre Configuración para revisar los detalles";
"tap_connect_to_create_the_tunnel" = "Toca conectar para crear el túnel";

View File

@@ -11,8 +11,9 @@
"local_tunnel_active" = "Tunnel locale attivo";
"local_tunnel_inactive" = "Tunnel locale inattivo";
"connected_to_10.7.0.1" = "Connesso a 10.7.0.1";
"macos_might_ask_you_to_approve_the_vpn" = "macOS potrebbe chiederti di approvare la VPN";
"connected_to_ip" = "Connesso a %@";
"connected_at" = "Connected at";
"ios_might_ask_you_to_allow_the_vpn" = "iOS potrebbe chiederti di consentire la VPN";
"disconnecting_safely" = "Disconnessione in corso in modo sicuro…";
"open_settings_to_review_details" = "Apri Impostazioni per visualizzare i dettagli";
"tap_connect_to_create_the_tunnel" = "Tocca Connetti per creare il tunnel";

View File

@@ -11,8 +11,9 @@
"local_tunnel_active" = "로컬 터널 활성화됨";
"local_tunnel_inactive" = "로컬 터널 비활성화됨";
"connected_to_10.7.0.1" = "10.7.0.1에 연결됨";
"macos_might_ask_you_to_approve_the_vpn" = "macOS에서 VPN 승인 요청이 나타날 수 있습니다";
"connected_to_ip" = "%@에 연결됨";
"connected_at" = "Connected at";
"ios_might_ask_you_to_allow_the_vpn" = "iOS에서 VPN 허용 요청이 나타날 수 있습니다";
"disconnecting_safely" = "안전하게 연결 해제 중…";
"open_settings_to_review_details" = "자세한 내용을 보려면 설정을 여세요";
"tap_connect_to_create_the_tunnel" = "터널을 생성하려면 연결을 누르세요";

View File

@@ -11,8 +11,9 @@
"local_tunnel_active" = "Lokalny tunel aktywny";
"local_tunnel_inactive" = "Lokalny tunel nieaktywny";
"connected_to_10.7.0.1" = "Połączono z 10.7.0.1";
"macos_might_ask_you_to_approve_the_vpn" = "macOS może poprosić Cię o zatwierdzenie VPN";
"connected_to_ip" = "Połączono z %@";
"connected_at" = "Connected at";
"ios_might_ask_you_to_allow_the_vpn" = "iOS może poprosić Cię o pozwolenie na VPN";
"disconnecting_safely" = "Bezpieczne rozłączanie…";
"open_settings_to_review_details" = "Otwórz Ustawienia, aby zobaczyć szczegóły";
"tap_connect_to_create_the_tunnel" = "Stuknij Połącz, aby utworzyć tunel";