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 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 359 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
360 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 360 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
361 CODE_SIGN_STYLE = Automatic; 361 CODE_SIGN_STYLE = Automatic;
362 CURRENT_PROJECT_VERSION = 5; 362 CURRENT_PROJECT_VERSION = 6;
363 DEVELOPMENT_TEAM = ZCNAX3VL9D; 363 DEVELOPMENT_TEAM = ZCNAX3VL9D;
364 ENABLE_PREVIEWS = YES; 364 ENABLE_PREVIEWS = YES;
365 GENERATE_INFOPLIST_FILE = YES; 365 GENERATE_INFOPLIST_FILE = YES;
@@ -376,7 +376,7 @@
376 "$(inherited)", 376 "$(inherited)",
377 "@executable_path/Frameworks", 377 "@executable_path/Frameworks",
378 ); 378 );
379 MARKETING_VERSION = 2.0; 379 MARKETING_VERSION = 2.1;
380 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; 380 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
381 PRODUCT_NAME = "$(TARGET_NAME)"; 381 PRODUCT_NAME = "$(TARGET_NAME)";
382 STRING_CATALOG_GENERATE_SYMBOLS = YES; 382 STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -395,7 +395,7 @@
395 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 395 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
396 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 396 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
397 CODE_SIGN_STYLE = Automatic; 397 CODE_SIGN_STYLE = Automatic;
398 CURRENT_PROJECT_VERSION = 5; 398 CURRENT_PROJECT_VERSION = 6;
399 DEVELOPMENT_TEAM = ZCNAX3VL9D; 399 DEVELOPMENT_TEAM = ZCNAX3VL9D;
400 ENABLE_PREVIEWS = YES; 400 ENABLE_PREVIEWS = YES;
401 GENERATE_INFOPLIST_FILE = YES; 401 GENERATE_INFOPLIST_FILE = YES;
@@ -412,7 +412,7 @@
412 "$(inherited)", 412 "$(inherited)",
413 "@executable_path/Frameworks", 413 "@executable_path/Frameworks",
414 ); 414 );
415 MARKETING_VERSION = 2.0; 415 MARKETING_VERSION = 2.1;
416 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; 416 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
417 PRODUCT_NAME = "$(TARGET_NAME)"; 417 PRODUCT_NAME = "$(TARGET_NAME)";
418 STRING_CATALOG_GENERATE_SYMBOLS = YES; 418 STRING_CATALOG_GENERATE_SYMBOLS = YES;
Hutch/Views/Repositories/HgRepositorySettingsView.swift +29 −2
@@ -9,6 +9,7 @@ struct HgRepositorySettingsView: View {
9 @State private var viewModel: HgRepositorySettingsViewModel? 9 @State private var viewModel: HgRepositorySettingsViewModel?
10 @State private var showDeleteConfirmation = false 10 @State private var showDeleteConfirmation = false
11 @State private var pendingACLDeletion: HgACLEntry? 11 @State private var pendingACLDeletion: HgACLEntry?
12 @State private var saveResultAlert: SaveResultAlert?
12 13
13 var body: some View { 14 var body: some View {
14 NavigationStack { 15 NavigationStack {
@@ -88,6 +89,13 @@ struct HgRepositorySettingsView: View {
88 Text("\(entry.entity.canonicalName) will lose \(entry.mode) access to this repository.") 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 @ViewBuilder 101 @ViewBuilder
@@ -108,7 +116,13 @@ struct HgRepositorySettingsView: View {
108 } 116 }
109 117
110 Button { 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 } label: { 126 } label: {
113 if viewModel.isSavingInfo { 127 if viewModel.isSavingInfo {
114 ProgressView() 128 ProgressView()
@@ -188,7 +202,13 @@ struct HgRepositorySettingsView: View {
188 Toggle("Hide this repository from public listings", isOn: Bindable(viewModel).editedNonPublishing) 202 Toggle("Hide this repository from public listings", isOn: Bindable(viewModel).editedNonPublishing)
189 203
190 Button { 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 } label: { 212 } label: {
193 if viewModel.isSavingInfo { 213 if viewModel.isSavingInfo {
194 ProgressView() 214 ProgressView()
@@ -236,4 +256,11 @@ struct HgRepositorySettingsView: View {
236 .disabled(viewModel.isDeleting) 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 isSavingInfo = true 172 isSavingInfo = true
173 defer { isSavingInfo = false } 173 defer { isSavingInfo = false }
174 error = nil 174 error = nil
@@ -185,8 +185,10 @@ final class HgRepositorySettingsViewModel {
185 variables: ["id": repositoryId, "input": input], 185 variables: ["id": repositoryId, "input": input],
186 responseType: HgUpdateRepositoryResponse.self 186 responseType: HgUpdateRepositoryResponse.self
187 ) 187 )
188 return true
188 } catch { 189 } catch {
189 self.error = error.userFacingMessage 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 if let cachedHeight = HTMLWebViewCoordinator.heightCache.object(forKey: wrapped as NSString)?.doubleValue { 1108 if let cachedHeight = HTMLWebViewCoordinator.heightCache.object(forKey: wrapped as NSString)?.doubleValue {
1109 let height = CGFloat(cachedHeight) 1109 let height = CGFloat(cachedHeight)
1110 if abs(dynamicHeight - height) > 0.5 { 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 @State private var viewModel: RepositorySettingsViewModel? 11 @State private var viewModel: RepositorySettingsViewModel?
12 @State private var showDeleteConfirmation = false 12 @State private var showDeleteConfirmation = false
13 @State private var pendingACLDeletion: ACLEntry? 13 @State private var pendingACLDeletion: ACLEntry?
14 @State private var saveResultAlert: SaveResultAlert?
14 15
15 var body: some View { 16 var body: some View {
16 NavigationStack { 17 NavigationStack {
@@ -91,6 +92,13 @@ struct RepositorySettingsView: View {
91 Text("\(entry.entity.canonicalName) will lose \(entry.mode) access to this repository.") 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 // MARK: - Info Section 104 // MARK: - Info Section
@@ -122,7 +130,13 @@ struct RepositorySettingsView: View {
122 } 130 }
123 131
124 Button { 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 } label: { 140 } label: {
127 if viewModel.isSavingInfo { 141 if viewModel.isSavingInfo {
128 ProgressView() 142 ProgressView()
@@ -256,4 +270,11 @@ struct RepositorySettingsView: View {
256 .disabled(viewModel.isDeleting) 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 isSavingInfo = true 157 isSavingInfo = true
158 defer { isSavingInfo = false } 158 defer { isSavingInfo = false }
159 error = nil 159 error = nil
@@ -172,8 +172,10 @@ final class RepositorySettingsViewModel {
172 variables: ["id": repositoryId, "input": input], 172 variables: ["id": repositoryId, "input": input],
173 responseType: UpdateRepoInfoResponse.self 173 responseType: UpdateRepoInfoResponse.self
174 ) 174 )
175 return true
175 } catch { 176 } catch {
176 self.error = error.userFacingMessage 177 self.error = error.userFacingMessage
178 return false
177 } 179 }
178 } 180 }
179 181