diff options
| author | Christian Cleberg <[email protected]> | 2026-04-13 19:44:44 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-04-13 19:44:44 -0500 |
| commit | fe1ccc69661603d419a642a450e4ac9e1258adb0 (patch) | |
| tree | 386455b6b5468abdd9dfa46dd4802b10da8b77a2 /Hutch/Views/More/ProfileView.swift | |
| parent | 6ec9754ce34f331641b000eddef3d44bca2631a5 (diff) | |
| download | hutch-3.1.3.tar.gz hutch-3.1.3.tar.bz2 hutch-3.1.3.zip | |
fix: centralize URLs, tighten models, and polish SwiftUI bindingsv3.1.3
- Add shared HutchDeepLinkURL constants for app, widgets, and tests.
- Bump marketing version to 3.1.2 and build to 70 (app + widget extension).
- Point Hutch stats default base URL at HutchStatsAPI; add SRHTWebURL status
incident feed and reuse it from SystemStatusService.
- Group Project into Metadata and Resources; split ContributionStatsResponse
into StatsWindow and StatsTotals with updated decoding and tests.
- Replace @Bindable usage with explicit Bindings in Profile, Projects list,
and repository ACL flows; simplify Home pinned-item helper; add no-op
bodies on cancel alert buttons where the compiler requires a statement.
- Move repository row build-status indicator next to the relative-updated
caption and reserve a fixed 8×8 slot so the row does not jump when status
loads.
- Use NSString.lastPathComponent for build artifact filenames; collapse
duplicate ACL error branches; minor HutchStats HTTP and XMLParserDelegate
cleanups.
- Point widgets at HutchDeepLinkURL helpers; align tests with the new
response and URL types.
Diffstat (limited to 'Hutch/Views/More/ProfileView.swift')
| -rw-r--r-- | Hutch/Views/More/ProfileView.swift | 47 |
1 files changed, 30 insertions, 17 deletions
diff --git a/Hutch/Views/More/ProfileView.swift b/Hutch/Views/More/ProfileView.swift index c53a89d..8f353ee 100644 --- a/Hutch/Views/More/ProfileView.swift +++ b/Hutch/Views/More/ProfileView.swift @@ -61,8 +61,6 @@ struct ProfileView: View { @ViewBuilder private func profileContent(_ viewModel: SettingsViewModel) -> some View { - @Bindable var vm = viewModel - Form { if let profile = viewModel.profile { profileSection(profile, viewModel: viewModel) @@ -100,7 +98,10 @@ struct ProfileView: View { ) } } - .sheet(isPresented: $vm.isEditingProfile) { + .sheet(isPresented: Binding( + get: { viewModel.isEditingProfile }, + set: { viewModel.isEditingProfile = $0 } + )) { if let profile = viewModel.profile { EditProfileSheet(profile: profile, viewModel: viewModel) } @@ -130,7 +131,9 @@ struct ProfileView: View { } ) ) { - Button("Cancel", role: .cancel) {} + Button("Cancel", role: .cancel) { + /* Dismiss only; destructive action is separate. */ + } Button(pendingDestructiveAction?.confirmationLabel ?? "Confirm", role: .destructive) { guard let action = pendingDestructiveAction else { return } pendingDestructiveAction = nil @@ -158,12 +161,11 @@ struct ProfileView: View { Section("Profile") { HStack(spacing: 12) { AsyncImage(url: profile.avatar.flatMap { URL(string: $0) }) { phase in - switch phase { - case .success(let image): + if case .success(let image) = phase { image .resizable() .scaledToFill() - default: + } else { Image(systemName: "person.crop.circle.fill") .resizable() .foregroundStyle(.secondary) @@ -238,8 +240,6 @@ struct ProfileView: View { @ViewBuilder private func sshKeysSection(_ viewModel: SettingsViewModel) -> some View { - @Bindable var vm = viewModel - Section { ForEach(viewModel.sshKeys) { key in VStack(alignment: .leading, spacing: 2) { @@ -275,7 +275,14 @@ struct ProfileView: View { .themedRow() if viewModel.isAddingSSHKey { - TextField("Paste SSH public key", text: $vm.newSSHKey, axis: .vertical) + TextField( + "Paste SSH public key", + text: Binding( + get: { viewModel.newSSHKey }, + set: { viewModel.newSSHKey = $0 } + ), + axis: .vertical + ) .font(.caption.monospaced()) .lineLimit(3...6) .themedRow() @@ -310,8 +317,6 @@ struct ProfileView: View { @ViewBuilder private func pgpKeysSection(_ viewModel: SettingsViewModel) -> some View { - @Bindable var vm = viewModel - Section { ForEach(viewModel.pgpKeys) { key in VStack(alignment: .leading, spacing: 2) { @@ -333,7 +338,14 @@ struct ProfileView: View { .themedRow() if viewModel.isAddingPGPKey { - TextField("Paste PGP public key", text: $vm.newPGPKey, axis: .vertical) + TextField( + "Paste PGP public key", + text: Binding( + get: { viewModel.newPGPKey }, + set: { viewModel.newPGPKey = $0 } + ), + axis: .vertical + ) .font(.caption.monospaced()) .lineLimit(3...6) .themedRow() @@ -482,12 +494,11 @@ private struct EditProfileSheet: View { .scaledToFill() } else { AsyncImage(url: profile.avatar.flatMap { URL(string: $0) }) { phase in - switch phase { - case .success(let image): + if case .success(let image) = phase { image .resizable() .scaledToFill() - default: + } else { Image(systemName: "person.crop.circle.fill") .resizable() .foregroundStyle(.secondary) @@ -592,7 +603,9 @@ private struct EditProfileSheet: View { } } .alert("Remove Avatar?", isPresented: $isShowingRemoveAvatarConfirmation) { - Button("Cancel", role: .cancel) {} + Button("Cancel", role: .cancel) { + /* Dismiss only; removal uses the destructive button. */ + } Button("Remove Avatar", role: .destructive) { Task { await viewModel.removeAvatar() |
