Implement unified network layer with APILayer and migrate iOS ViewModels
Major architectural improvements: - Created APILayer as single entry point for all network operations - Integrated cache-first reads with automatic cache updates on mutations - Migrated all shared Kotlin ViewModels to use APILayer instead of direct API calls - Migrated iOS ViewModels to wrap shared Kotlin ViewModels with StateFlow observation - Replaced LookupsManager with DataCache for centralized lookup data management - Added password reset methods to AuthViewModel - Added task completion and update methods to APILayer - Added residence user management methods to APILayer iOS specific changes: - Updated LoginViewModel, RegisterViewModel, ProfileViewModel to use shared AuthViewModel - Updated ContractorViewModel, DocumentViewModel to use shared ViewModels - Updated ResidenceViewModel to use shared ViewModel and APILayer - Updated TaskViewModel to wrap shared ViewModel with callback-based interface - Migrated PasswordResetViewModel and VerifyEmailViewModel to shared AuthViewModel - Migrated AllTasksView, CompleteTaskView, EditTaskView to use APILayer - Migrated ManageUsersView, ResidenceDetailView to use APILayer - Migrated JoinResidenceView to use async/await pattern with APILayer - Removed LookupsManager.swift in favor of DataCache - Fixed PushNotificationManager @MainActor issue - Converted all direct API calls to use async/await with proper error handling Benefits: - Reduced code duplication between iOS and Android - Consistent error handling across platforms - Automatic cache management for better performance - Centralized network layer for easier testing and maintenance - Net reduction of ~700 lines of code through shared logic 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,13 +3,10 @@ import ComposeApp
|
||||
|
||||
struct JoinResidenceView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@StateObject private var viewModel = ResidenceViewModel()
|
||||
let onJoined: () -> Void
|
||||
|
||||
@State private var shareCode: String = ""
|
||||
@State private var isJoining = false
|
||||
@State private var errorMessage: String?
|
||||
|
||||
private let residenceApi = ResidenceApi(client: ApiClient_iosKt.createHttpClient())
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
@@ -24,9 +21,9 @@ struct JoinResidenceView: View {
|
||||
shareCode = String(newValue.prefix(6))
|
||||
}
|
||||
shareCode = shareCode.uppercased()
|
||||
errorMessage = nil
|
||||
viewModel.clearError()
|
||||
}
|
||||
.disabled(isJoining)
|
||||
.disabled(viewModel.isLoading)
|
||||
} header: {
|
||||
Text("Enter Share Code")
|
||||
} footer: {
|
||||
@@ -34,7 +31,7 @@ struct JoinResidenceView: View {
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
if let error = errorMessage {
|
||||
if let error = viewModel.errorMessage {
|
||||
Section {
|
||||
Text(error)
|
||||
.foregroundColor(.red)
|
||||
@@ -45,7 +42,7 @@ struct JoinResidenceView: View {
|
||||
Button(action: joinResidence) {
|
||||
HStack {
|
||||
Spacer()
|
||||
if isJoining {
|
||||
if viewModel.isLoading {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
} else {
|
||||
@@ -55,7 +52,7 @@ struct JoinResidenceView: View {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.disabled(shareCode.count != 6 || isJoining)
|
||||
.disabled(shareCode.count != 6 || viewModel.isLoading)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Join Residence")
|
||||
@@ -65,7 +62,7 @@ struct JoinResidenceView: View {
|
||||
Button("Cancel") {
|
||||
dismiss()
|
||||
}
|
||||
.disabled(isJoining)
|
||||
.disabled(viewModel.isLoading)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,29 +70,30 @@ struct JoinResidenceView: View {
|
||||
|
||||
private func joinResidence() {
|
||||
guard shareCode.count == 6 else {
|
||||
errorMessage = "Share code must be 6 characters"
|
||||
viewModel.errorMessage = "Share code must be 6 characters"
|
||||
return
|
||||
}
|
||||
|
||||
guard let token = TokenStorage.shared.getToken() else {
|
||||
errorMessage = "Not authenticated"
|
||||
return
|
||||
}
|
||||
Task {
|
||||
// Call the shared ViewModel which uses APILayer
|
||||
await viewModel.sharedViewModel.joinWithCode(code: shareCode)
|
||||
|
||||
isJoining = true
|
||||
errorMessage = nil
|
||||
|
||||
residenceApi.joinWithCode(token: token, code: shareCode) { result, error in
|
||||
if result is ApiResultSuccess<JoinResidenceResponse> {
|
||||
self.isJoining = false
|
||||
self.onJoined()
|
||||
self.dismiss()
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.isJoining = false
|
||||
} else if let error = error {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.isJoining = false
|
||||
// Observe the result
|
||||
for await state in viewModel.sharedViewModel.joinResidenceState {
|
||||
if state is ApiResultSuccess<JoinResidenceResponse> {
|
||||
await MainActor.run {
|
||||
viewModel.sharedViewModel.resetJoinResidenceState()
|
||||
onJoined()
|
||||
dismiss()
|
||||
}
|
||||
break
|
||||
} else if let error = state as? ApiResultError {
|
||||
await MainActor.run {
|
||||
viewModel.errorMessage = error.message
|
||||
viewModel.sharedViewModel.resetJoinResidenceState()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ struct ManageUsersView: View {
|
||||
@State private var errorMessage: String?
|
||||
@State private var isGeneratingCode = false
|
||||
|
||||
private let residenceApi = ResidenceApi(client: ApiClient_iosKt.createHttpClient())
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
ZStack {
|
||||
@@ -83,7 +81,7 @@ struct ManageUsersView: View {
|
||||
}
|
||||
|
||||
private func loadUsers() {
|
||||
guard let token = TokenStorage.shared.getToken() else {
|
||||
guard TokenStorage.shared.getToken() != nil else {
|
||||
errorMessage = "Not authenticated"
|
||||
return
|
||||
}
|
||||
@@ -91,65 +89,103 @@ struct ManageUsersView: View {
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
|
||||
residenceApi.getResidenceUsers(token: token, residenceId: residenceId) { result, error in
|
||||
if let successResult = result as? ApiResultSuccess<ResidenceUsersResponse>,
|
||||
let responseData = successResult.data as? ResidenceUsersResponse {
|
||||
self.users = Array(responseData.users)
|
||||
self.ownerId = responseData.ownerId as? Int32
|
||||
self.isLoading = false
|
||||
Task {
|
||||
do {
|
||||
let result = try await APILayer.shared.getResidenceUsers(residenceId: Int32(Int(residenceId)))
|
||||
|
||||
// Don't auto-load share code - user must generate it explicitly
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.isLoading = false
|
||||
} else if let error = error {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.isLoading = false
|
||||
await MainActor.run {
|
||||
if let successResult = result as? ApiResultSuccess<ResidenceUsersResponse>,
|
||||
let responseData = successResult.data as? ResidenceUsersResponse {
|
||||
self.users = Array(responseData.users)
|
||||
self.ownerId = responseData.ownerId as? Int32
|
||||
self.isLoading = false
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.isLoading = false
|
||||
} else {
|
||||
self.errorMessage = "Failed to load users"
|
||||
self.isLoading = false
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadShareCode() {
|
||||
guard let token = TokenStorage.shared.getToken() else { return }
|
||||
guard TokenStorage.shared.getToken() != nil else { return }
|
||||
|
||||
residenceApi.getShareCode(token: token, residenceId: residenceId) { result, error in
|
||||
if let successResult = result as? ApiResultSuccess<ResidenceShareCode> {
|
||||
self.shareCode = successResult.data
|
||||
Task {
|
||||
do {
|
||||
let result = try await APILayer.shared.getShareCode(residenceId: Int32(Int(residenceId)))
|
||||
|
||||
await MainActor.run {
|
||||
if let successResult = result as? ApiResultSuccess<ResidenceShareCode> {
|
||||
self.shareCode = successResult.data
|
||||
}
|
||||
// It's okay if there's no active share code
|
||||
}
|
||||
} catch {
|
||||
// It's okay if there's no active share code
|
||||
}
|
||||
// It's okay if there's no active share code
|
||||
}
|
||||
}
|
||||
|
||||
private func generateShareCode() {
|
||||
guard let token = TokenStorage.shared.getToken() else { return }
|
||||
guard TokenStorage.shared.getToken() != nil else { return }
|
||||
|
||||
isGeneratingCode = true
|
||||
|
||||
residenceApi.generateShareCode(token: token, residenceId: residenceId) { result, error in
|
||||
if let successResult = result as? ApiResultSuccess<ResidenceShareCode> {
|
||||
self.shareCode = successResult.data
|
||||
self.isGeneratingCode = false
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.isGeneratingCode = false
|
||||
} else if let error = error {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.isGeneratingCode = false
|
||||
Task {
|
||||
do {
|
||||
let result = try await APILayer.shared.generateShareCode(residenceId: Int32(Int(residenceId)))
|
||||
|
||||
await MainActor.run {
|
||||
if let successResult = result as? ApiResultSuccess<ResidenceShareCode> {
|
||||
self.shareCode = successResult.data
|
||||
self.isGeneratingCode = false
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.isGeneratingCode = false
|
||||
} else {
|
||||
self.errorMessage = "Failed to generate share code"
|
||||
self.isGeneratingCode = false
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.isGeneratingCode = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func removeUser(userId: Int32) {
|
||||
guard let token = TokenStorage.shared.getToken() else { return }
|
||||
guard TokenStorage.shared.getToken() != nil else { return }
|
||||
|
||||
residenceApi.removeUser(token: token, residenceId: residenceId, userId: userId) { result, error in
|
||||
if result is ApiResultSuccess<RemoveUserResponse> {
|
||||
// Remove user from local list
|
||||
self.users.removeAll { $0.id == userId }
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
} else if let error = error {
|
||||
self.errorMessage = error.localizedDescription
|
||||
Task {
|
||||
do {
|
||||
let result = try await APILayer.shared.removeUser(residenceId: Int32(Int(residenceId)), userId: Int32(Int(userId)))
|
||||
|
||||
await MainActor.run {
|
||||
if result is ApiResultSuccess<RemoveUserResponse> {
|
||||
// Remove user from local list
|
||||
self.users.removeAll { $0.id == userId }
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
} else {
|
||||
self.errorMessage = "Failed to remove user"
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
self.errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,43 +226,61 @@ struct ResidenceDetailView: View {
|
||||
}
|
||||
|
||||
private func loadResidenceTasks() {
|
||||
guard let token = TokenStorage.shared.getToken() else { return }
|
||||
guard TokenStorage.shared.getToken() != nil else { return }
|
||||
|
||||
isLoadingTasks = true
|
||||
tasksError = nil
|
||||
|
||||
let taskApi = TaskApi(client: ApiClient_iosKt.createHttpClient())
|
||||
taskApi.getTasksByResidence(token: token, residenceId: residenceId, days: 30) { result, error in
|
||||
if let successResult = result as? ApiResultSuccess<TaskColumnsResponse> {
|
||||
self.tasksResponse = successResult.data
|
||||
self.isLoadingTasks = false
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.tasksError = errorResult.message
|
||||
self.isLoadingTasks = false
|
||||
} else if let error = error {
|
||||
self.tasksError = error.localizedDescription
|
||||
self.isLoadingTasks = false
|
||||
Task {
|
||||
do {
|
||||
let result = try await APILayer.shared.getTasksByResidence(residenceId: Int32(Int(residenceId)), forceRefresh: false)
|
||||
|
||||
await MainActor.run {
|
||||
if let successResult = result as? ApiResultSuccess<TaskColumnsResponse> {
|
||||
self.tasksResponse = successResult.data
|
||||
self.isLoadingTasks = false
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.tasksError = errorResult.message
|
||||
self.isLoadingTasks = false
|
||||
} else {
|
||||
self.tasksError = "Failed to load tasks"
|
||||
self.isLoadingTasks = false
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
self.tasksError = error.localizedDescription
|
||||
self.isLoadingTasks = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func deleteResidence() {
|
||||
guard let token = TokenStorage.shared.getToken() else { return }
|
||||
guard TokenStorage.shared.getToken() != nil else { return }
|
||||
|
||||
isDeleting = true
|
||||
|
||||
let residenceApi = ResidenceApi(client: ApiClient_iosKt.createHttpClient())
|
||||
residenceApi.deleteResidence(token: token, id: residenceId) { result, error in
|
||||
DispatchQueue.main.async {
|
||||
self.isDeleting = false
|
||||
Task {
|
||||
do {
|
||||
let result = try await APILayer.shared.deleteResidence(id: Int32(Int(residenceId)))
|
||||
|
||||
if result is ApiResultSuccess<KotlinUnit> {
|
||||
// Navigate back to residence list
|
||||
self.dismiss()
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
// Show error message
|
||||
self.viewModel.errorMessage = errorResult.message
|
||||
} else if let error = error {
|
||||
await MainActor.run {
|
||||
self.isDeleting = false
|
||||
|
||||
if result is ApiResultSuccess<KotlinUnit> {
|
||||
// Navigate back to residence list
|
||||
self.dismiss()
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
// Show error message
|
||||
self.viewModel.errorMessage = errorResult.message
|
||||
} else {
|
||||
self.viewModel.errorMessage = "Failed to delete residence"
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
self.isDeleting = false
|
||||
self.viewModel.errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,159 +14,191 @@ class ResidenceViewModel: ObservableObject {
|
||||
@Published var reportMessage: String?
|
||||
|
||||
// MARK: - Private Properties
|
||||
private let residenceApi: ResidenceApi
|
||||
private let tokenStorage: TokenStorage
|
||||
public let sharedViewModel: ComposeApp.ResidenceViewModel
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
// MARK: - Initialization
|
||||
init() {
|
||||
self.residenceApi = ResidenceApi(client: ApiClient_iosKt.createHttpClient())
|
||||
self.tokenStorage = TokenStorage.shared
|
||||
self.sharedViewModel = ComposeApp.ResidenceViewModel()
|
||||
}
|
||||
|
||||
// MARK: - Public Methods
|
||||
func loadResidenceSummary() {
|
||||
guard let token = tokenStorage.getToken() else {
|
||||
errorMessage = "Not authenticated"
|
||||
return
|
||||
}
|
||||
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
|
||||
residenceApi.getResidenceSummary(token: token) { result, error in
|
||||
if let successResult = result as? ApiResultSuccess<ResidenceSummaryResponse> {
|
||||
self.residenceSummary = successResult.data
|
||||
self.isLoading = false
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.isLoading = false
|
||||
} else if let error = error {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.isLoading = false
|
||||
sharedViewModel.loadResidenceSummary()
|
||||
|
||||
// Observe the state
|
||||
Task {
|
||||
for await state in sharedViewModel.residenceSummaryState {
|
||||
if state is ApiResultLoading {
|
||||
await MainActor.run {
|
||||
self.isLoading = true
|
||||
}
|
||||
} else if let success = state as? ApiResultSuccess<ResidenceSummaryResponse> {
|
||||
await MainActor.run {
|
||||
self.residenceSummary = success.data
|
||||
self.isLoading = false
|
||||
}
|
||||
break
|
||||
} else if let error = state as? ApiResultError {
|
||||
await MainActor.run {
|
||||
self.errorMessage = error.message
|
||||
self.isLoading = false
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadMyResidences() {
|
||||
guard let token = tokenStorage.getToken() else {
|
||||
errorMessage = "Not authenticated"
|
||||
return
|
||||
}
|
||||
|
||||
func loadMyResidences(forceRefresh: Bool = false) {
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
|
||||
residenceApi.getMyResidences(token: token) { result, error in
|
||||
if let successResult = result as? ApiResultSuccess<MyResidencesResponse> {
|
||||
self.myResidences = successResult.data
|
||||
self.isLoading = false
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.isLoading = false
|
||||
} else if let error = error {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.isLoading = false
|
||||
sharedViewModel.loadMyResidences(forceRefresh: forceRefresh)
|
||||
|
||||
// Observe the state
|
||||
Task {
|
||||
for await state in sharedViewModel.myResidencesState {
|
||||
if state is ApiResultLoading {
|
||||
await MainActor.run {
|
||||
self.isLoading = true
|
||||
}
|
||||
} else if let success = state as? ApiResultSuccess<MyResidencesResponse> {
|
||||
await MainActor.run {
|
||||
self.myResidences = success.data
|
||||
self.isLoading = false
|
||||
}
|
||||
break
|
||||
} else if let error = state as? ApiResultError {
|
||||
await MainActor.run {
|
||||
self.errorMessage = error.message
|
||||
self.isLoading = false
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getResidence(id: Int32) {
|
||||
guard let token = tokenStorage.getToken() else {
|
||||
errorMessage = "Not authenticated"
|
||||
return
|
||||
}
|
||||
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
|
||||
residenceApi.getResidence(token: token, id: id) { result, error in
|
||||
if let successResult = result as? ApiResultSuccess<Residence> {
|
||||
self.selectedResidence = successResult.data
|
||||
self.isLoading = false
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.isLoading = false
|
||||
} else if let error = error {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.isLoading = false
|
||||
sharedViewModel.getResidence(id: id) { result in
|
||||
Task { @MainActor in
|
||||
if let success = result as? ApiResultSuccess<Residence> {
|
||||
self.selectedResidence = success.data
|
||||
self.isLoading = false
|
||||
} else if let error = result as? ApiResultError {
|
||||
self.errorMessage = error.message
|
||||
self.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createResidence(request: ResidenceCreateRequest, completion: @escaping (Bool) -> Void) {
|
||||
guard let token = tokenStorage.getToken() else {
|
||||
errorMessage = "Not authenticated"
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
|
||||
residenceApi.createResidence(token: token, request: request) { result, error in
|
||||
if result is ApiResultSuccess<Residence> {
|
||||
self.isLoading = false
|
||||
completion(true)
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.isLoading = false
|
||||
completion(false)
|
||||
} else if let error = error {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.isLoading = false
|
||||
completion(false)
|
||||
sharedViewModel.createResidence(request: request)
|
||||
|
||||
// Observe the state
|
||||
Task {
|
||||
for await state in sharedViewModel.createResidenceState {
|
||||
if state is ApiResultLoading {
|
||||
await MainActor.run {
|
||||
self.isLoading = true
|
||||
}
|
||||
} else if state is ApiResultSuccess<Residence> {
|
||||
await MainActor.run {
|
||||
self.isLoading = false
|
||||
}
|
||||
sharedViewModel.resetCreateState()
|
||||
completion(true)
|
||||
break
|
||||
} else if let error = state as? ApiResultError {
|
||||
await MainActor.run {
|
||||
self.errorMessage = error.message
|
||||
self.isLoading = false
|
||||
}
|
||||
sharedViewModel.resetCreateState()
|
||||
completion(false)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateResidence(id: Int32, request: ResidenceCreateRequest, completion: @escaping (Bool) -> Void) {
|
||||
guard let token = tokenStorage.getToken() else {
|
||||
errorMessage = "Not authenticated"
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
|
||||
residenceApi.updateResidence(token: token, id: id, request: request) { result, error in
|
||||
if let successResult = result as? ApiResultSuccess<Residence> {
|
||||
self.selectedResidence = successResult.data
|
||||
self.isLoading = false
|
||||
completion(true)
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.isLoading = false
|
||||
completion(false)
|
||||
} else if let error = error {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.isLoading = false
|
||||
completion(false)
|
||||
sharedViewModel.updateResidence(residenceId: id, request: request)
|
||||
|
||||
// Observe the state
|
||||
Task {
|
||||
for await state in sharedViewModel.updateResidenceState {
|
||||
if state is ApiResultLoading {
|
||||
await MainActor.run {
|
||||
self.isLoading = true
|
||||
}
|
||||
} else if let success = state as? ApiResultSuccess<Residence> {
|
||||
await MainActor.run {
|
||||
self.selectedResidence = success.data
|
||||
self.isLoading = false
|
||||
}
|
||||
sharedViewModel.resetUpdateState()
|
||||
completion(true)
|
||||
break
|
||||
} else if let error = state as? ApiResultError {
|
||||
await MainActor.run {
|
||||
self.errorMessage = error.message
|
||||
self.isLoading = false
|
||||
}
|
||||
sharedViewModel.resetUpdateState()
|
||||
completion(false)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateTasksReport(residenceId: Int32, email: String? = nil) {
|
||||
guard let token = tokenStorage.getToken() else {
|
||||
reportMessage = "Not authenticated"
|
||||
return
|
||||
}
|
||||
|
||||
isGeneratingReport = true
|
||||
reportMessage = nil
|
||||
|
||||
residenceApi.generateTasksReport(token: token, residenceId: residenceId, email: email) { result, error in
|
||||
defer { self.isGeneratingReport = false }
|
||||
if let successResult = result as? ApiResultSuccess<GenerateReportResponse> {
|
||||
if let response = successResult.data {
|
||||
self.reportMessage = response.message
|
||||
} else {
|
||||
self.reportMessage = "Report generated, but no message returned."
|
||||
sharedViewModel.generateTasksReport(residenceId: residenceId, email: email)
|
||||
|
||||
// Observe the state
|
||||
Task {
|
||||
for await state in sharedViewModel.generateReportState {
|
||||
if state is ApiResultLoading {
|
||||
await MainActor.run {
|
||||
self.isGeneratingReport = true
|
||||
}
|
||||
} else if let success = state as? ApiResultSuccess<GenerateReportResponse> {
|
||||
await MainActor.run {
|
||||
if let response = success.data {
|
||||
self.reportMessage = response.message
|
||||
} else {
|
||||
self.reportMessage = "Report generated, but no message returned."
|
||||
}
|
||||
self.isGeneratingReport = false
|
||||
}
|
||||
sharedViewModel.resetGenerateReportState()
|
||||
break
|
||||
} else if let error = state as? ApiResultError {
|
||||
await MainActor.run {
|
||||
self.reportMessage = error.message
|
||||
self.isGeneratingReport = false
|
||||
}
|
||||
sharedViewModel.resetGenerateReportState()
|
||||
break
|
||||
}
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.reportMessage = errorResult.message
|
||||
} else if let error = error {
|
||||
self.reportMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user