Close all 25 codex audit findings across KMP, iOS, and Android
Remediate all P0-S priority findings from cross-platform architecture audit: - Harden token storage with EncryptedSharedPreferences (Android) and Keychain (iOS) - Add SSL pinning and certificate validation to API clients - Fix subscription cache race conditions and add thread-safe access - Add input validation for document uploads and file type restrictions - Refactor DocumentApi to use proper multipart upload flow - Add rate limiting awareness and retry logic to API layer - Harden subscription tier enforcement in SubscriptionHelper - Add biometric prompt for sensitive actions (Login, Onboarding) - Fix notification permission handling and device registration - Add UI test infrastructure (page objects, fixtures, smoke tests) - Add CI workflow for mobile builds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,27 @@ import Foundation
|
||||
import ComposeApp
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Architecture Note
|
||||
//
|
||||
// Two document ViewModels coexist with distinct responsibilities:
|
||||
//
|
||||
// DocumentViewModel (DocumentViewModel.swift):
|
||||
// - Used by list views (DocumentsView, DocumentListView)
|
||||
// - Observes DataManager via DataManagerObservable for reactive list updates
|
||||
// - Handles CRUD operations that update DataManager cache (create, update, delete)
|
||||
// - Supports image upload workflows
|
||||
// - Uses @MainActor for thread safety
|
||||
//
|
||||
// DocumentViewModelWrapper (this file):
|
||||
// - Used by detail views (DocumentDetailView, EditDocumentView)
|
||||
// - Manages explicit state types (Loading/Success/Error) for single-document operations
|
||||
// - Loads individual document detail, handles update and delete with state feedback
|
||||
// - Does NOT observe DataManager -- loads fresh data per-request via APILayer
|
||||
// - Uses protocol-based state enums for SwiftUI view branching
|
||||
//
|
||||
// Both call through APILayer (which updates DataManager), so list views
|
||||
// auto-refresh when detail views perform mutations.
|
||||
|
||||
// State wrappers for SwiftUI
|
||||
protocol DocumentState {}
|
||||
struct DocumentStateIdle: DocumentState {}
|
||||
@@ -235,18 +256,20 @@ class DocumentViewModelWrapper: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func deleteDocumentImage(imageId: Int32) {
|
||||
func deleteDocumentImage(documentId: Int32, imageId: Int32) {
|
||||
DispatchQueue.main.async {
|
||||
self.deleteImageState = DeleteImageStateLoading()
|
||||
}
|
||||
|
||||
Task {
|
||||
do {
|
||||
let result = try await APILayer.shared.deleteDocumentImage(imageId: imageId)
|
||||
let result = try await APILayer.shared.deleteDocumentImage(documentId: documentId, imageId: imageId)
|
||||
|
||||
await MainActor.run {
|
||||
if result is ApiResultSuccess<KotlinUnit> {
|
||||
if let success = result as? ApiResultSuccess<Document>, let document = success.data {
|
||||
self.deleteImageState = DeleteImageStateSuccess()
|
||||
// Refresh detail state with updated document (image removed)
|
||||
self.documentDetailState = DocumentDetailStateSuccess(document: document)
|
||||
} else if let error = ApiResultBridge.error(from: result) {
|
||||
self.deleteImageState = DeleteImageStateError(message: error.message)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user