Add task completion animations, subscription trials, and quiet debug console

- Completion animations: play user-selected animation on task card after completing,
  with DataManager guard to prevent race condition during animation playback.
  Works in both AllTasksView and ResidenceDetailView. Animation preference
  persisted via @AppStorage and configurable from Settings.
- Subscription: add trial fields (trialStart, trialEnd, trialActive) and
  subscriptionSource to model, cross-platform purchase guard, trial banner
  in upgrade prompt, and platform-aware subscription management in profile.
- Analytics: disable PostHog SDK debug logging and remove console print
  statements to reduce debug console noise.
- Documents: remove redundant nested do-catch blocks in ViewModel wrapper.
- Widgets: add debounced timeline reloads and thread-safe file I/O queue.
- Onboarding: fix animation leak on disappear, remove unused state vars.
- Remove unused files (ContentView, StateFlowExtensions, CustomView).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-03-05 11:35:08 -06:00
parent c5f2bee83f
commit 98dbacdea0
73 changed files with 1770 additions and 529 deletions

View File

@@ -124,20 +124,16 @@ class DocumentViewModelWrapper: ObservableObject {
forceRefresh: false
)
do {
if let success = result as? ApiResultSuccess<NSArray> {
let documents = success.data as? [Document] ?? []
self.documentsState = DocumentStateSuccess(documents: documents)
} else if let error = ApiResultBridge.error(from: result) {
self.documentsState = DocumentStateError(message: error.message)
} else {
self.documentsState = DocumentStateError(message: "Failed to load documents")
}
if let success = result as? ApiResultSuccess<NSArray> {
let documents = success.data as? [Document] ?? []
self.documentsState = DocumentStateSuccess(documents: documents)
} else if let error = ApiResultBridge.error(from: result) {
self.documentsState = DocumentStateError(message: error.message)
} else {
self.documentsState = DocumentStateError(message: "Failed to load documents")
}
} catch {
do {
self.documentsState = DocumentStateError(message: error.localizedDescription)
}
self.documentsState = DocumentStateError(message: error.localizedDescription)
}
}
}
@@ -150,19 +146,15 @@ class DocumentViewModelWrapper: ObservableObject {
do {
let result = try await APILayer.shared.getDocument(id: id, forceRefresh: false)
do {
if let success = result as? ApiResultSuccess<Document>, let document = success.data {
self.documentDetailState = DocumentDetailStateSuccess(document: document)
} else if let error = ApiResultBridge.error(from: result) {
self.documentDetailState = DocumentDetailStateError(message: error.message)
} else {
self.documentDetailState = DocumentDetailStateError(message: "Failed to load document")
}
if let success = result as? ApiResultSuccess<Document>, let document = success.data {
self.documentDetailState = DocumentDetailStateSuccess(document: document)
} else if let error = ApiResultBridge.error(from: result) {
self.documentDetailState = DocumentDetailStateError(message: error.message)
} else {
self.documentDetailState = DocumentDetailStateError(message: "Failed to load document")
}
} catch {
do {
self.documentDetailState = DocumentDetailStateError(message: error.localizedDescription)
}
self.documentDetailState = DocumentDetailStateError(message: error.localizedDescription)
}
}
}
@@ -215,21 +207,17 @@ class DocumentViewModelWrapper: ObservableObject {
endDate: endDate
)
do {
if let success = result as? ApiResultSuccess<Document>, let document = success.data {
self.updateState = UpdateStateSuccess(document: document)
// Also refresh the detail state
self.documentDetailState = DocumentDetailStateSuccess(document: document)
} else if let error = ApiResultBridge.error(from: result) {
self.updateState = UpdateStateError(message: error.message)
} else {
self.updateState = UpdateStateError(message: "Failed to update document")
}
if let success = result as? ApiResultSuccess<Document>, let document = success.data {
self.updateState = UpdateStateSuccess(document: document)
// Also refresh the detail state
self.documentDetailState = DocumentDetailStateSuccess(document: document)
} else if let error = ApiResultBridge.error(from: result) {
self.updateState = UpdateStateError(message: error.message)
} else {
self.updateState = UpdateStateError(message: "Failed to update document")
}
} catch {
do {
self.updateState = UpdateStateError(message: error.localizedDescription)
}
self.updateState = UpdateStateError(message: error.localizedDescription)
}
}
}
@@ -241,19 +229,15 @@ class DocumentViewModelWrapper: ObservableObject {
do {
let result = try await APILayer.shared.deleteDocument(id: id)
do {
if result is ApiResultSuccess<KotlinUnit> {
self.deleteState = DeleteStateSuccess()
} else if let error = ApiResultBridge.error(from: result) {
self.deleteState = DeleteStateError(message: error.message)
} else {
self.deleteState = DeleteStateError(message: "Failed to delete document")
}
if result is ApiResultSuccess<KotlinUnit> {
self.deleteState = DeleteStateSuccess()
} else if let error = ApiResultBridge.error(from: result) {
self.deleteState = DeleteStateError(message: error.message)
} else {
self.deleteState = DeleteStateError(message: "Failed to delete document")
}
} catch {
do {
self.deleteState = DeleteStateError(message: error.localizedDescription)
}
self.deleteState = DeleteStateError(message: error.localizedDescription)
}
}
}
@@ -273,21 +257,17 @@ class DocumentViewModelWrapper: ObservableObject {
do {
let result = try await APILayer.shared.deleteDocumentImage(documentId: documentId, imageId: imageId)
do {
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 {
self.deleteImageState = DeleteImageStateError(message: "Failed to delete image")
}
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 {
self.deleteImageState = DeleteImageStateError(message: "Failed to delete image")
}
} catch {
do {
self.deleteImageState = DeleteImageStateError(message: error.localizedDescription)
}
self.deleteImageState = DeleteImageStateError(message: error.localizedDescription)
}
}
}