Skip to content

Commit 2cf1809

Browse files
committed
Show blocking progress while saving database changes
1 parent b67a08c commit 2cf1809

3 files changed

Lines changed: 72 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
### Fixes
1616
- Autosave entry create/edit/delete changes immediately after they are staged, remove the confusing unlocked-screen save button, and show retry-only save UI when an autosave fails
17+
- Show a blocking saving-progress overlay while database changes are being written so users get clear feedback and cannot interact with the unlocked UI mid-save
1718
- Remove the baked white background from the Dropbox provider glyph so it renders cleanly in dark mode
1819
- Show provider-specific cloud sync status during unlock, and add focused unlock coverage for cloud sync success, fallback, and failure paths
1920
- Fix Xcode Cloud post-clone bootstrap so clean CI machines no longer require a developer team xcconfig value just to generate the project

KeeForge/App/KeeForgeApp.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,12 @@ struct DatabaseNavigationView: View {
299299
}
300300
}
301301
}
302+
.disabled(viewModel.isSaving)
303+
.overlay {
304+
if viewModel.isSaving {
305+
DatabaseSavingOverlay()
306+
}
307+
}
302308
.saveConflictAlert(viewModel: viewModel)
303309
.onChange(of: viewModel.saveError) { _, newValue in
304310
if let newValue {
@@ -368,6 +374,39 @@ struct DatabaseNavigationView: View {
368374
}
369375
}
370376

377+
private struct DatabaseSavingOverlay: View {
378+
var body: some View {
379+
ZStack {
380+
Color.black.opacity(0.16)
381+
.ignoresSafeArea()
382+
383+
VStack(spacing: 14) {
384+
ProgressView()
385+
.controlSize(.large)
386+
387+
Text("Saving changes...")
388+
.font(.headline)
389+
390+
Text("KeeForge is writing the updated database securely.")
391+
.font(.subheadline)
392+
.foregroundStyle(.secondary)
393+
.multilineTextAlignment(.center)
394+
}
395+
.padding(.horizontal, 24)
396+
.padding(.vertical, 22)
397+
.frame(maxWidth: 280)
398+
.background(
399+
RoundedRectangle(cornerRadius: 24, style: .continuous)
400+
.fill(.regularMaterial)
401+
)
402+
.shadow(color: .black.opacity(0.08), radius: 24, y: 10)
403+
}
404+
.accessibilityElement(children: .combine)
405+
.accessibilityLabel("Saving changes")
406+
.accessibilityIdentifier("database.saving-overlay")
407+
}
408+
}
409+
371410
private struct ReadOnlyRibbon: View {
372411
var body: some View {
373412
Text("Read-only mode — toggle in the database list to enable editing.")

KeeForge/Views/EntryEditView.swift

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ struct EntryEditView: View {
1616
@State private var editingErrorMessage: String?
1717
@State private var isSubmitting = false
1818

19+
private var isSavingInProgress: Bool {
20+
isSubmitting || databaseViewModel.isSaving
21+
}
22+
1923
init(
2024
formViewModel: EntryEditViewModel,
2125
databaseViewModel: DatabaseViewModel,
@@ -113,23 +117,49 @@ struct EntryEditView: View {
113117
.navigationTitle(navigationTitle)
114118
.navigationBarTitleDisplayMode(.inline)
115119
.navigationBarBackButtonHidden(true)
120+
.disabled(isSavingInProgress)
116121
.toolbar {
117122
ToolbarItem(placement: .cancellationAction) {
118123
Button("Cancel") {
119124
cancelTapped()
120125
}
121-
.disabled(isSubmitting)
126+
.disabled(isSavingInProgress)
122127
.accessibilityIdentifier("entry-edit.cancel")
123128
}
124129

125130
ToolbarItem(placement: .confirmationAction) {
126131
Button("Save") {
127132
saveTapped()
128133
}
129-
.disabled(formViewModel.canSave == false || isSubmitting)
134+
.disabled(formViewModel.canSave == false || isSavingInProgress)
130135
.accessibilityIdentifier("entry-edit.save")
131136
}
132137
}
138+
.overlay {
139+
if isSubmitting && databaseViewModel.isSaving == false {
140+
ZStack {
141+
Color.black.opacity(0.14)
142+
.ignoresSafeArea()
143+
144+
VStack(spacing: 12) {
145+
ProgressView()
146+
.controlSize(.large)
147+
Text("Saving changes...")
148+
.font(.headline)
149+
}
150+
.padding(.horizontal, 24)
151+
.padding(.vertical, 20)
152+
.background(
153+
RoundedRectangle(cornerRadius: 22, style: .continuous)
154+
.fill(.regularMaterial)
155+
)
156+
.shadow(color: .black.opacity(0.08), radius: 20, y: 8)
157+
}
158+
.accessibilityElement(children: .combine)
159+
.accessibilityLabel("Saving changes")
160+
.accessibilityIdentifier("entry-edit.saving-overlay")
161+
}
162+
}
133163
.sheet(isPresented: $showPasswordGenerator) {
134164
PasswordGeneratorSheet { password in
135165
formViewModel.password = password

0 commit comments

Comments
 (0)