05/17 Fix bugs 4

This commit is contained in:
Nguyen Ngo
2026-05-17 11:21:16 -04:00
parent f436ac2ef2
commit 437ed913cd
@@ -986,17 +986,17 @@ struct SettingsView: View {
@EnvironmentObject private var auth: AuthManager @EnvironmentObject private var auth: AuthManager
@EnvironmentObject private var sync: SyncManager @EnvironmentObject private var sync: SyncManager
@Environment(\.modelContext) private var context @Environment(\.modelContext) private var context
@State private var showClearCacheAlert = false @State private var showClearCacheAlert = false
@State private var cacheCleared = false @State private var cacheCleared = false
var body: some View { var body: some View {
List { List {
Section("Account") { Section("Account") {
LabeledContent("Username", value: auth.currentUsername) LabeledContent("Username", value: auth.currentUsername)
LabeledContent("Role", value: auth.currentUserRole.capitalized) LabeledContent("Role", value: auth.currentUserRole.capitalized)
} }
Section("Sync") { Section("Sync") {
Button { Button {
Task { await sync.triggerSync() } Task { await sync.triggerSync() }
@@ -1004,17 +1004,17 @@ struct SettingsView: View {
Label("Sync Now", systemImage: "arrow.clockwise") Label("Sync Now", systemImage: "arrow.clockwise")
} }
.disabled(!sync.isOnline || sync.isSyncing) .disabled(!sync.isOnline || sync.isSyncing)
if let error = sync.syncError { if let error = sync.syncError {
Text(error).font(.caption).foregroundStyle(.red) Text(error).font(.caption).foregroundStyle(.red)
} }
if let lastSync = sync.lastSyncAt { if let lastSync = sync.lastSyncAt {
LabeledContent("Last Sync", LabeledContent("Last Sync",
value: lastSync.formatted(date: .abbreviated, time: .shortened)) value: lastSync.formatted(date: .abbreviated, time: .shortened))
} }
} }
Section("Cache") { Section("Cache") {
Button { Button {
showClearCacheAlert = true showClearCacheAlert = true
@@ -1025,22 +1025,29 @@ struct SettingsView: View {
Text("Clears locally cached facilities, areas, and templates. Your pending inspections are not affected. Data will re-sync on the next connection.") Text("Clears locally cached facilities, areas, and templates. Your pending inspections are not affected. Data will re-sync on the next connection.")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
if cacheCleared { if cacheCleared {
Label("Cache cleared.", systemImage: "checkmark.circle.fill") Label("Cache cleared.", systemImage: "checkmark.circle.fill")
.foregroundStyle(.green) .foregroundStyle(.green)
.font(.callout) .font(.callout)
} }
} }
Section { Section {
Button(role: .destructive) { Button(role: .destructive) {
Task { await auth.logout() } Task {
// Clear server-pulled issues before logging out so stale
// records from a previous server/domain don't persist into
// the next session. Device-created pending issues are preserved.
clearServerPulledData()
sync.resetNotificationPoller()
await auth.logout()
}
} label: { } label: {
Label("Log Out", systemImage: "rectangle.portrait.and.arrow.right") Label("Log Out", systemImage: "rectangle.portrait.and.arrow.right")
} }
} }
Section("App Info") { Section("App Info") {
LabeledContent("Version", value: "\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0") (\(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1"))") LabeledContent("Version", value: "\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0") (\(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1"))")
LabeledContent("Server", value: Constants.baseURL) LabeledContent("Server", value: Constants.baseURL)
@@ -1054,23 +1061,36 @@ struct SettingsView: View {
Text("Facilities, areas, and templates will be removed from local storage and re-downloaded on the next sync. Pending inspections are not affected.") Text("Facilities, areas, and templates will be removed from local storage and re-downloaded on the next sync. Pending inspections are not affected.")
} }
} }
private func clearCache() { private func clearCache() {
// Delete only reference data never touch LocalInspection, LocalIssue, PendingPhoto // Delete only reference data never touch LocalInspection, LocalIssue, PendingPhoto
let facilities = (try? context.fetch(FetchDescriptor<LocalFacility>())) ?? [] let facilities = (try? context.fetch(FetchDescriptor<LocalFacility>())) ?? []
let templates = (try? context.fetch(FetchDescriptor<LocalTemplate>())) ?? [] let templates = (try? context.fetch(FetchDescriptor<LocalTemplate>())) ?? []
let areas = (try? context.fetch(FetchDescriptor<LocalArea>())) ?? [] let areas = (try? context.fetch(FetchDescriptor<LocalArea>())) ?? []
facilities.forEach { context.delete($0) } facilities.forEach { context.delete($0) }
templates.forEach { context.delete($0) } templates.forEach { context.delete($0) }
areas.forEach { context.delete($0) } areas.forEach { context.delete($0) }
try? context.save() try? context.save()
cacheCleared = true cacheCleared = true
// Re-pull immediately if online // Re-pull immediately if online
if sync.isOnline { if sync.isOnline {
Task { await sync.pullReferenceData() } Task { await sync.pullReferenceData() }
} }
} }
/// Delete all server-pulled LocalIssue records (syncStatus == "synced" and
/// inspectionLocalId == ""). These are issues fetched from the server and
/// reconciled by pullAssignedIssues they must be cleared on logout so
/// stale records from a previous server domain or user session don't persist.
/// Device-created issues (inspectionLocalId != "") are never touched.
private func clearServerPulledData() {
let allIssues = (try? context.fetch(FetchDescriptor<LocalIssue>())) ?? []
allIssues
.filter { $0.syncStatus == "synced" && $0.inspectionLocalId == "" }
.forEach { context.delete($0) }
try? context.save()
}
} }