Add swiftlint & Minor fixes

This commit is contained in:
世界
2025-11-27 16:10:55 +08:00
parent 8855e0eef9
commit 0bfb6d2516
29 changed files with 382 additions and 196 deletions
@@ -111,22 +111,25 @@ public struct ActiveDashboardView: View {
#if os(iOS) || os(tvOS)
private func updateButtonVisibility() {
buttonState.update(profile: profile, commandClient: environments.commandClient)
buttonState.update(profile: profile, commandClient: environments.commandClient, requireAnyConnection: true)
}
#if os(iOS)
private var isTabViewBottomAccessoryAvailable: Bool {
if #available(iOS 26.0, *), !Variant.debugNoIOS26 {
return true
}
return false
}
#endif
@ToolbarContentBuilder
private var toolbar: some ToolbarContent {
ToolbarItem(placement: .topBarLeading) {
#if os(iOS)
if #available(iOS 26.0, *), !Variant.debugNoIOS26 {
EmptyView()
} else {
navigationButtons
}
#else
#if os(tvOS)
ToolbarItem(placement: .topBarLeading) {
navigationButtons
#endif
}
}
#endif
ToolbarItem(placement: .topBarTrailing) {
if #available(iOS 16.0, tvOS 17.0, *) {
cardManagementButton
@@ -137,10 +140,7 @@ public struct ActiveDashboardView: View {
if #available(iOS 26.0, *), !Variant.debugNoIOS26 {
EmptyView()
} else {
HStack(spacing: 12) {
Divider()
StartStopButton()
}
StartStopButton()
}
#else
HStack(spacing: 12) {
@@ -151,16 +151,18 @@ public struct ActiveDashboardView: View {
}
}
private var navigationButtons: some View {
NavigationButtonsView(
showGroupsButton: buttonState.showGroupsButton,
showConnectionsButton: buttonState.showConnectionsButton,
groupsCount: buttonState.groupsCount,
connectionsCount: buttonState.connectionsCount,
onGroupsTap: { showGroups = true },
onConnectionsTap: { showConnections = true }
)
}
#if os(tvOS)
private var navigationButtons: some View {
NavigationButtonsView(
showGroupsButton: buttonState.showGroupsButton,
showConnectionsButton: buttonState.showConnectionsButton,
groupsCount: buttonState.groupsCount,
connectionsCount: buttonState.connectionsCount,
onGroupsTap: { showGroups = true },
onConnectionsTap: { showConnections = true }
)
}
#endif
#endif
#if os(iOS) || os(tvOS)
@@ -176,19 +178,42 @@ public struct ActiveDashboardView: View {
@ViewBuilder
private var cardManagementButton: some View {
Menu {
#if os(iOS)
if !isTabViewBottomAccessoryAvailable {
if buttonState.showGroupsButton {
Button {
showGroups = true
} label: {
Label("Groups (\(buttonState.groupsCount))", systemImage: "rectangle.3.group.fill")
}
}
if buttonState.showConnectionsButton {
Button {
showConnections = true
} label: {
Label("Connections (\(buttonState.connectionsCount))", systemImage: "list.bullet.rectangle.portrait.fill")
}
}
if buttonState.showGroupsButton || buttonState.showConnectionsButton {
Divider()
}
}
#endif
Button {
showCardManagement = true
} label: {
Label("Dashboard Items", systemImage: "square.grid.2x2")
}
} label: {
Label("Others", systemImage: "ellipsis.circle")
Label("Others", systemImage: "line.3.horizontal.circle")
}
.sheet(isPresented: $showCardManagement) {
CardManagementSheet(configurationVersion: $cardConfigurationVersion)
.sheet(isPresented: $showCardManagement, onDismiss: {
cardConfigurationVersion += 1
}, content: {
CardManagementSheet()
.presentationDetents([.large])
.presentationDragIndicator(.visible)
}
})
}
#endif
}
@@ -4,11 +4,8 @@ import SwiftUI
@MainActor public struct CardManagementSheet: View {
@Environment(\.dismiss) private var dismiss
@StateObject private var configuration = DashboardCardConfiguration()
@Binding private var configurationVersion: Int
public init(configurationVersion: Binding<Int>) {
_configurationVersion = configurationVersion
}
public init() {}
public var body: some View {
#if os(macOS)
@@ -41,7 +38,6 @@ import SwiftUI
Button("Reset", role: .destructive) {
Task {
await configuration.resetToDefault()
configurationVersion += 1
}
}
}
@@ -49,11 +45,9 @@ import SwiftUI
Button("Done") {
dismiss()
}
.keyboardShortcut(.escape, modifiers: [])
}
}
.onExitCommand {
dismiss()
}
}
#else
private var iOSBody: some View {
@@ -74,7 +68,6 @@ import SwiftUI
Button("Reset", role: .destructive) {
Task {
await configuration.resetToDefault()
configurationVersion += 1
}
}
}
@@ -91,13 +84,13 @@ import SwiftUI
isEnabled: configuration.isEnabled(card),
onToggle: {
configuration.toggleCard(card)
configurationVersion += 1
}
)
}
.onMove { source, destination in
configuration.moveCard(from: source, to: destination)
configurationVersion += 1
Task {
await configuration.moveCard(from: source, to: destination)
}
}
}
.applyContentMargins()
@@ -40,13 +40,9 @@ public final class DashboardCardConfiguration: ObservableObject {
}
}
public func moveCard(from source: IndexSet, to destination: Int) {
public func moveCard(from source: IndexSet, to destination: Int) async {
cardOrder.move(fromOffsets: source, toOffset: destination)
// Save asynchronously in background
Task {
await saveCardOrder()
}
await saveCardOrder()
}
public func resetToDefault() async {
@@ -19,12 +19,17 @@ public struct HTTPProxyCard: View {
public var body: some View {
DashboardCardView(title: "", isHalfWidth: false) {
Toggle("System HTTP Proxy", isOn: $systemProxyEnabled)
.onChangeCompat(of: systemProxyEnabled) { newValue in
Task {
await onToggle(newValue)
HStack {
Text("System HTTP Proxy")
Spacer()
Toggle("", isOn: $systemProxyEnabled)
.labelsHidden()
.onChangeCompat(of: systemProxyEnabled) { newValue in
Task {
await onToggle(newValue)
}
}
}
}
}
}
}
@@ -34,10 +34,10 @@ public struct ProfileCard: View {
.disabled(viewModel.isUpdating)
.sheet(isPresented: $viewModel.showNewProfile, onDismiss: {
environments.profileUpdate.send()
}) {
}, content: {
NewProfileNavigationView()
.environmentObject(environments)
}
})
.sheet(isPresented: $viewModel.showManageProfiles) {
manageProfilesSheet
}
@@ -218,11 +218,12 @@ public struct ProfileCard: View {
NavigationSheet(
title: String(localized: "Manage profiles"),
showDoneButton: true,
onDismiss: { viewModel.showManageProfiles = false }
) {
ManageProfilesView()
.environmentObject(environments)
}
onDismiss: { viewModel.showManageProfiles = false },
content: {
ManageProfilesView()
.environmentObject(environments)
}
)
}
@ViewBuilder
@@ -390,6 +391,7 @@ extension ProfileCard {
@ObservedObject private var viewModel: ProfileViewModel
@State private var profile: ProfilePreview
@State private var shareLinkPresented = false
@State private var isUpdating = false
init(_ viewModel: ProfileViewModel, _ profile: ProfilePreview) {
self.viewModel = viewModel
@@ -416,25 +418,26 @@ extension ProfileCard {
HStack(spacing: 8) {
if profile.type == .remote {
Button {
viewModel.isUpdating = true
isUpdating = true
Task {
await viewModel.updateProfile(profile.origin)
profile = ProfilePreview(profile.origin)
isUpdating = false
}
} label: {
Image(systemName: "arrow.clockwise")
.font(.system(size: 16))
.rotationEffect(.degrees(viewModel.isUpdating ? 360 : 0))
.rotationEffect(.degrees(isUpdating ? 360 : 0))
.animation(
viewModel.isUpdating
isUpdating
? .linear(duration: 1).repeatForever(autoreverses: false)
: .default,
value: viewModel.isUpdating
value: isUpdating
)
}
.buttonStyle(.plain)
.actionButtonStyle()
.disabled(viewModel.isUpdating)
.disabled(isUpdating)
Button {
shareLinkPresented = true
@@ -83,10 +83,8 @@ struct ProfileSelectorButton: View {
private func updateButtonContent(_ button: MenuAttachmentButton) {
// Remove existing subviews
for subview in button.subviews {
if subview is UIStackView {
subview.removeFromSuperview()
}
for subview in button.subviews where subview is UIStackView {
subview.removeFromSuperview()
}
// Create content stack
@@ -42,35 +42,72 @@ public struct StartStopButton: View {
await switchProfile(!profile.status.isConnected)
}
} label: {
HStack(spacing: 8) {
if profile.status.isConnectedStrict, let duration = runtimeDuration {
Text(duration)
.font(.caption)
.foregroundStyle(.secondary)
.monospacedDigit()
.transition(.asymmetric(
insertion: .move(edge: .trailing).combined(with: .opacity),
removal: .move(edge: .trailing).combined(with: .opacity)
))
}
#if os(iOS)
HStack(spacing: 8) {
if showRuntimeDuration, profile.status.isConnectedStrict, let duration = runtimeDuration {
Text(duration)
.font(.caption)
.foregroundStyle(.secondary)
.monospacedDigit()
.fixedSize()
.transition(.asymmetric(
insertion: .move(edge: .trailing).combined(with: .opacity),
removal: .move(edge: .trailing).combined(with: .opacity)
))
}
if !profile.status.isConnected {
Label("Start", systemImage: "play.fill")
} else {
Label("Stop", systemImage: "stop.fill")
if !profile.status.isConnected {
Label("Start", systemImage: "play.fill")
} else {
Label("Stop", systemImage: "stop.fill")
}
}
}
.animation(.spring(response: 0.35, dampingFraction: 0.75), value: profile.status.isConnectedStrict)
.animation(.spring(response: 0.35, dampingFraction: 0.75), value: profile.status.isConnectedStrict)
#else
HStack(spacing: 8) {
if profile.status.isConnectedStrict, let duration = runtimeDuration {
Text(duration)
.font(.caption)
.foregroundStyle(.secondary)
.monospacedDigit()
.fixedSize()
.transition(.asymmetric(
insertion: .move(edge: .trailing).combined(with: .opacity),
removal: .move(edge: .trailing).combined(with: .opacity)
))
}
if !profile.status.isConnected {
Label("Start", systemImage: "play.fill")
} else {
Label("Stop", systemImage: "stop.fill")
}
}
.animation(.spring(response: 0.35, dampingFraction: 0.75), value: profile.status.isConnectedStrict)
#endif
}
.labelStyle(.iconOnly)
.tint(.primary)
.disabled(!profile.status.isEnabled)
.alertBinding($alert)
.onReceive(timer) { _ in
currentTime = Date()
}
#if os(iOS)
.modifier(PrimaryTintModifier())
#else
.tint(.primary)
#endif
.disabled(!profile.status.isEnabled)
.alertBinding($alert)
.onReceive(timer) { _ in
currentTime = Date()
}
}
#if os(iOS)
private var showRuntimeDuration: Bool {
if #available(iOS 26.0, *), !Variant.debugNoIOS26 {
return true
}
return false
}
#endif
private var runtimeDuration: String? {
guard let connectedDate = profile.connectedDate else { return nil }
let interval = currentTime.timeIntervalSince(connectedDate)
@@ -103,3 +140,15 @@ public struct StartStopButton: View {
}
}
}
#if os(iOS)
private struct PrimaryTintModifier: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 26.0, *), !Variant.debugNoIOS26 {
content.tint(.primary)
} else {
content
}
}
}
#endif
@@ -85,10 +85,10 @@ public struct DashboardView: View {
private func importRemoteProfileSheet(for request: NewProfileView.ImportRequest) -> some View {
NavigationSheet(title: "Import Profile", onDismiss: {
environments.profileUpdate.send()
}) {
}, content: {
NewProfileView(request)
.environmentObject(environments)
}
})
}
@ViewBuilder
@@ -111,11 +111,11 @@ public struct DashboardView: View {
@ViewBuilder
private var mainContent: some View {
if ApplicationLibrary.inPreview {
ActiveDashboardView(externalCardConfigurationVersion: cardConfigurationVersion)
activeDashboardView
} else if environments.extensionProfileLoading {
ProgressView()
} else if let profile = environments.extensionProfile {
ActiveDashboardView(externalCardConfigurationVersion: cardConfigurationVersion)
activeDashboardView
.environmentObject(profile)
.alertBinding($coordinator.alert)
.onChangeCompat(of: profile.status) { status in
@@ -129,4 +129,13 @@ public struct DashboardView: View {
}
}
}
@ViewBuilder
private var activeDashboardView: some View {
#if os(macOS)
ActiveDashboardView(externalCardConfigurationVersion: cardConfigurationVersion)
#else
ActiveDashboardView()
#endif
}
}