fix: pin Connect button to bottom, dynamic protocol badge

- safeAreaInset pins Connect/Continue button to screen bottom,
  eliminating dead space below the form
- UGreenCompatibilityBadge now takes selectedProtocol param:
  green dot + green SMB label when SMB selected,
  grey dot + grey SFTP label when SFTP selected

Co-Authored-By: Kutesir <kutesir@provoc.ug>
Co-Authored-By: Sentry <sentry@provoc.ug>
This commit is contained in:
Robin Kutesa
2026-05-16 19:19:37 +03:00
parent 2b6e2d145d
commit 2bdf41502e
2 changed files with 29 additions and 31 deletions

View File

@@ -35,8 +35,8 @@ struct ConnectView: View {
} }
.shadow(color: AppTheme.ink.opacity(0.18), radius: 12, x: 0, y: 4) .shadow(color: AppTheme.ink.opacity(0.18), radius: 12, x: 0, y: 4)
// UGreen NAS compatibility badge // UGreen NAS compatibility badge reacts to selected protocol
UGreenCompatibilityBadge() UGreenCompatibilityBadge(selectedProtocol: vm.selectedProtocol)
} }
.padding(.bottom, 24) .padding(.bottom, 24)
.padding(.horizontal, AppTheme.hPad) .padding(.horizontal, AppTheme.hPad)
@@ -236,7 +236,10 @@ struct ConnectView: View {
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: vm.isConnected) .animation(.spring(response: 0.3, dampingFraction: 0.8), value: vm.isConnected)
.padding(.bottom, 16) .padding(.bottom, 16)
// Connect / Continue button }
}
.safeAreaInset(edge: .bottom) {
VStack(spacing: 0) {
Button(action: { Button(action: {
if vm.isConnected { navigateToDashboard = true } if vm.isConnected { navigateToDashboard = true }
else { Task { await vm.verify() } } else { Task { await vm.verify() } }
@@ -253,11 +256,12 @@ struct ConnectView: View {
color: vm.isConnected ? AppTheme.positive : AppTheme.ink color: vm.isConnected ? AppTheme.positive : AppTheme.ink
)) ))
.padding(.horizontal, AppTheme.hPad) .padding(.horizontal, AppTheme.hPad)
.padding(.top, 12)
.padding(.bottom, 32)
.disabled(!vm.canConnect) .disabled(!vm.canConnect)
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: vm.isConnected) .animation(.spring(response: 0.3, dampingFraction: 0.8), value: vm.isConnected)
Spacer().frame(height: 48)
} }
.background(AppTheme.background)
} }
} }
.navigationBarHidden(true) .navigationBarHidden(true)

View File

@@ -1,31 +1,24 @@
import SwiftUI import SwiftUI
import UIKit
// Displays the UGreen NAS compatibility indicator.
// If you add an image named "ugreen_logo" to Assets.xcassets it will be used automatically.
// Otherwise a brand-styled text badge is shown.
struct UGreenCompatibilityBadge: View { struct UGreenCompatibilityBadge: View {
private static let ugreenGreen = Color(hex: "#00B388") var selectedProtocol: NASProtocol = .smb
private let hasLogo = UIImage(named: "ugreen_logo") != nil
private var dotColor: Color {
selectedProtocol == .smb ? Color(hex: "#00B388") : AppTheme.inkQuaternary
}
var body: some View { var body: some View {
HStack(spacing: 8) { HStack(spacing: 8) {
// UGreen logo or fallback wordmark // Status dot + UGreen NAS label
if hasLogo { HStack(spacing: 4) {
Image("ugreen_logo") Circle()
.resizable() .fill(dotColor)
.scaledToFit() .frame(width: 6, height: 6)
.frame(height: 14) .animation(.spring(response: 0.25, dampingFraction: 0.8), value: selectedProtocol)
} else { Text("UGreen NAS")
HStack(spacing: 4) { .font(.system(size: 11, weight: .semibold))
// Brand dot .foregroundStyle(dotColor)
Circle() .animation(.spring(response: 0.25, dampingFraction: 0.8), value: selectedProtocol)
.fill(Self.ugreenGreen)
.frame(width: 6, height: 6)
Text("UGreen NAS")
.font(.system(size: 11, weight: .semibold))
.foregroundStyle(Self.ugreenGreen)
}
} }
// Separator // Separator
@@ -33,10 +26,11 @@ struct UGreenCompatibilityBadge: View {
.fill(AppTheme.inkQuaternary) .fill(AppTheme.inkQuaternary)
.frame(width: 0.5, height: 11) .frame(width: 0.5, height: 11)
// Protocol hint // Active protocol label
Text("SMB · SFTP") Text(selectedProtocol.rawValue)
.font(.system(size: 11, weight: .regular)) .font(.system(size: 11, weight: .medium))
.foregroundStyle(AppTheme.inkTertiary) .foregroundStyle(selectedProtocol == .smb ? dotColor : AppTheme.inkTertiary)
.contentTransition(.identity)
} }
.padding(.horizontal, 12) .padding(.horizontal, 12)
.padding(.vertical, 6) .padding(.vertical, 6)