Commit 77df444b20
77df444b20bc0e380a17abffc9f5688452c7c721
parent: fa8188260f
Unsigned
cmc <hello@cleberg.net> · 2026-03-19 22:05 UTC
fix README height update and settings save alerts
Layout: unified · split
Hutch.xcodeproj/project.pbxproj
+4 −4
| @@ -359,7 +359,7 @@ |
| 359 | 359 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; |
| 360 | 360 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; |
| 361 | 361 | CODE_SIGN_STYLE = Automatic; |
| 362 | | CURRENT_PROJECT_VERSION = 5; |
| 362 | CURRENT_PROJECT_VERSION = 6; |
| 363 | 363 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 364 | 364 | ENABLE_PREVIEWS = YES; |
| 365 | 365 | GENERATE_INFOPLIST_FILE = YES; |
| @@ -376,7 +376,7 @@ |
| 376 | 376 | "$(inherited)", |
| 377 | 377 | "@executable_path/Frameworks", |
| 378 | 378 | ); |
| 379 | | MARKETING_VERSION = 2.0; |
| 379 | MARKETING_VERSION = 2.1; |
| 380 | 380 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; |
| 381 | 381 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 382 | 382 | STRING_CATALOG_GENERATE_SYMBOLS = YES; |
| @@ -395,7 +395,7 @@ |
| 395 | 395 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; |
| 396 | 396 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; |
| 397 | 397 | CODE_SIGN_STYLE = Automatic; |
| 398 | | CURRENT_PROJECT_VERSION = 5; |
| 398 | CURRENT_PROJECT_VERSION = 6; |
| 399 | 399 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 400 | 400 | ENABLE_PREVIEWS = YES; |
| 401 | 401 | GENERATE_INFOPLIST_FILE = YES; |
| @@ -412,7 +412,7 @@ |
| 412 | 412 | "$(inherited)", |
| 413 | 413 | "@executable_path/Frameworks", |
| 414 | 414 | ); |
| 415 | | MARKETING_VERSION = 2.0; |
| 415 | MARKETING_VERSION = 2.1; |
| 416 | 416 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; |
| 417 | 417 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 418 | 418 | STRING_CATALOG_GENERATE_SYMBOLS = YES; |
Hutch/Views/Repositories/HgRepositorySettingsView.swift
+29 −2
| @@ -9,6 +9,7 @@ struct HgRepositorySettingsView: View { |
| 9 | 9 | @State private var viewModel: HgRepositorySettingsViewModel? |
| 10 | 10 | @State private var showDeleteConfirmation = false |
| 11 | 11 | @State private var pendingACLDeletion: HgACLEntry? |
| 12 | @State private var saveResultAlert: SaveResultAlert? |
| 12 | 13 | |
| 13 | 14 | var body: some View { |
| 14 | 15 | NavigationStack { |
| @@ -88,6 +89,13 @@ struct HgRepositorySettingsView: View { |
| 88 | 89 | Text("\(entry.entity.canonicalName) will lose \(entry.mode) access to this repository.") |
| 89 | 90 | } |
| 90 | 91 | } |
| 92 | .alert(item: $saveResultAlert) { alert in |
| 93 | Alert( |
| 94 | title: Text(alert.title), |
| 95 | message: Text(alert.message), |
| 96 | dismissButton: .default(Text("OK")) |
| 97 | ) |
| 98 | } |
| 91 | 99 | } |
| 92 | 100 | |
| 93 | 101 | @ViewBuilder |
| @@ -108,7 +116,13 @@ struct HgRepositorySettingsView: View { |
| 108 | 116 | } |
| 109 | 117 | |
| 110 | 118 | Button { |
| 111 | | Task { await viewModel.saveInfo() } |
| 119 | Task { |
| 120 | let didSave = await viewModel.saveInfo() |
| 121 | saveResultAlert = SaveResultAlert( |
| 122 | title: didSave ? "Settings Updated" : "Couldn't Update Settings", |
| 123 | message: didSave ? "Repository settings were saved." : (viewModel.error ?? "Please try again.") |
| 124 | ) |
| 125 | } |
| 112 | 126 | } label: { |
| 113 | 127 | if viewModel.isSavingInfo { |
| 114 | 128 | ProgressView() |
| @@ -188,7 +202,13 @@ struct HgRepositorySettingsView: View { |
| 188 | 202 | Toggle("Hide this repository from public listings", isOn: Bindable(viewModel).editedNonPublishing) |
| 189 | 203 | |
| 190 | 204 | Button { |
| 191 | | Task { await viewModel.saveInfo() } |
| 205 | Task { |
| 206 | let didSave = await viewModel.saveInfo() |
| 207 | saveResultAlert = SaveResultAlert( |
| 208 | title: didSave ? "Settings Updated" : "Couldn't Update Settings", |
| 209 | message: didSave ? "Repository settings were saved." : (viewModel.error ?? "Please try again.") |
| 210 | ) |
| 211 | } |
| 192 | 212 | } label: { |
| 193 | 213 | if viewModel.isSavingInfo { |
| 194 | 214 | ProgressView() |
| @@ -236,4 +256,11 @@ struct HgRepositorySettingsView: View { |
| 236 | 256 | .disabled(viewModel.isDeleting) |
| 237 | 257 | } |
| 238 | 258 | } |
| 259 | |
| 260 | private struct SaveResultAlert: Identifiable { |
| 261 | let title: String |
| 262 | let message: String |
| 263 | |
| 264 | var id: String { "\(title)-\(message)" } |
| 265 | } |
| 239 | 266 | } |
Hutch/Views/Repositories/HgRepositorySettingsViewModel.swift
+3 −1
| @@ -168,7 +168,7 @@ final class HgRepositorySettingsViewModel { |
| 168 | 168 | } |
| 169 | 169 | } |
| 170 | 170 | |
| 171 | | func saveInfo() async { |
| 171 | func saveInfo() async -> Bool { |
| 172 | 172 | isSavingInfo = true |
| 173 | 173 | defer { isSavingInfo = false } |
| 174 | 174 | error = nil |
| @@ -185,8 +185,10 @@ final class HgRepositorySettingsViewModel { |
| 185 | 185 | variables: ["id": repositoryId, "input": input], |
| 186 | 186 | responseType: HgUpdateRepositoryResponse.self |
| 187 | 187 | ) |
| 188 | return true |
| 188 | 189 | } catch { |
| 189 | 190 | self.error = error.userFacingMessage |
| 191 | return false |
| 190 | 192 | } |
| 191 | 193 | } |
| 192 | 194 | |
Hutch/Views/Repositories/ReadmeView.swift
+5 −1
| @@ -1108,7 +1108,11 @@ private struct HTMLWebViewRepresentable: UIViewRepresentable { |
| 1108 | 1108 | if let cachedHeight = HTMLWebViewCoordinator.heightCache.object(forKey: wrapped as NSString)?.doubleValue { |
| 1109 | 1109 | let height = CGFloat(cachedHeight) |
| 1110 | 1110 | if abs(dynamicHeight - height) > 0.5 { |
| 1111 | | dynamicHeight = height |
| 1111 | DispatchQueue.main.async { |
| 1112 | if abs(self.dynamicHeight - height) > 0.5 { |
| 1113 | self.dynamicHeight = height |
| 1114 | } |
| 1115 | } |
| 1112 | 1116 | } |
| 1113 | 1117 | } |
| 1114 | 1118 | |
Hutch/Views/Repositories/RepositorySettingsView.swift
+22 −1
| @@ -11,6 +11,7 @@ struct RepositorySettingsView: View { |
| 11 | 11 | @State private var viewModel: RepositorySettingsViewModel? |
| 12 | 12 | @State private var showDeleteConfirmation = false |
| 13 | 13 | @State private var pendingACLDeletion: ACLEntry? |
| 14 | @State private var saveResultAlert: SaveResultAlert? |
| 14 | 15 | |
| 15 | 16 | var body: some View { |
| 16 | 17 | NavigationStack { |
| @@ -91,6 +92,13 @@ struct RepositorySettingsView: View { |
| 91 | 92 | Text("\(entry.entity.canonicalName) will lose \(entry.mode) access to this repository.") |
| 92 | 93 | } |
| 93 | 94 | } |
| 95 | .alert(item: $saveResultAlert) { alert in |
| 96 | Alert( |
| 97 | title: Text(alert.title), |
| 98 | message: Text(alert.message), |
| 99 | dismissButton: .default(Text("OK")) |
| 100 | ) |
| 101 | } |
| 94 | 102 | } |
| 95 | 103 | |
| 96 | 104 | // MARK: - Info Section |
| @@ -122,7 +130,13 @@ struct RepositorySettingsView: View { |
| 122 | 130 | } |
| 123 | 131 | |
| 124 | 132 | Button { |
| 125 | | Task { await viewModel.saveInfo() } |
| 133 | Task { |
| 134 | let didSave = await viewModel.saveInfo() |
| 135 | saveResultAlert = SaveResultAlert( |
| 136 | title: didSave ? "Settings Updated" : "Couldn't Update Settings", |
| 137 | message: didSave ? "Repository settings were saved." : (viewModel.error ?? "Please try again.") |
| 138 | ) |
| 139 | } |
| 126 | 140 | } label: { |
| 127 | 141 | if viewModel.isSavingInfo { |
| 128 | 142 | ProgressView() |
| @@ -256,4 +270,11 @@ struct RepositorySettingsView: View { |
| 256 | 270 | .disabled(viewModel.isDeleting) |
| 257 | 271 | } |
| 258 | 272 | } |
| 273 | |
| 274 | private struct SaveResultAlert: Identifiable { |
| 275 | let title: String |
| 276 | let message: String |
| 277 | |
| 278 | var id: String { "\(title)-\(message)" } |
| 279 | } |
| 259 | 280 | } |
Hutch/Views/Repositories/RepositorySettingsViewModel.swift
+3 −1
| @@ -153,7 +153,7 @@ final class RepositorySettingsViewModel { |
| 153 | 153 | } |
| 154 | 154 | """ |
| 155 | 155 | |
| 156 | | func saveInfo() async { |
| 156 | func saveInfo() async -> Bool { |
| 157 | 157 | isSavingInfo = true |
| 158 | 158 | defer { isSavingInfo = false } |
| 159 | 159 | error = nil |
| @@ -172,8 +172,10 @@ final class RepositorySettingsViewModel { |
| 172 | 172 | variables: ["id": repositoryId, "input": input], |
| 173 | 173 | responseType: UpdateRepoInfoResponse.self |
| 174 | 174 | ) |
| 175 | return true |
| 175 | 176 | } catch { |
| 176 | 177 | self.error = error.userFacingMessage |
| 178 | return false |
| 177 | 179 | } |
| 178 | 180 | } |
| 179 | 181 | |