diff --git a/Data/Data/Repository/ExpenseRepository.swift b/Data/Data/Repository/ExpenseRepository.swift index e9ea0a5e..9b32497a 100644 --- a/Data/Data/Repository/ExpenseRepository.swift +++ b/Data/Data/Repository/ExpenseRepository.swift @@ -165,7 +165,7 @@ public class ExpenseRepository: ObservableObject { return try await store.fetchExpenseBy(groupId: groupId, expenseId: expenseId) } - public func fetchExpensesForUser(userId: String, limit: Int = 10, lastDocument: DocumentSnapshot? = nil) async throws -> (expenses: [Expense], lastDocument: DocumentSnapshot?) { - return try await store.fetchExpensesForUser(userId: userId, limit: limit, lastDocument: lastDocument) + public func fetchExpensesOfAllGroups(limit: Int = 10, lastDocument: DocumentSnapshot? = nil) async throws -> (expenses: [Expense], lastDocument: DocumentSnapshot?) { + return try await store.fetchExpensesOfAllGroups(limit: limit, lastDocument: lastDocument) } } diff --git a/Data/Data/Store/ExpenseStore.swift b/Data/Data/Store/ExpenseStore.swift index e394e506..c0d9e5db 100644 --- a/Data/Data/Store/ExpenseStore.swift +++ b/Data/Data/Store/ExpenseStore.swift @@ -60,7 +60,7 @@ public class ExpenseStore: ObservableObject { return (expenses, snapshot.documents.last) } - func fetchExpensesForUser(userId: String, limit: Int, lastDocument: DocumentSnapshot?) async throws -> (expenses: [Expense], lastDocument: DocumentSnapshot?) { + func fetchExpensesForUser2(userId: String, limit: Int, lastDocument: DocumentSnapshot?) async throws -> (expenses: [Expense], lastDocument: DocumentSnapshot?) { var userExpenses: [Expense] = [] // Step 1: Fetch groups where the user is a member @@ -109,4 +109,29 @@ public class ExpenseStore: ObservableObject { return (userExpenses, lastFetchedDocument) } + + func fetchExpensesOfAllGroups(limit: Int, lastDocument: DocumentSnapshot?) async throws -> (expenses: [Expense], lastDocument: DocumentSnapshot?) { + // Query to fetch expenses from all groups using collectionGroup + var query = database.collectionGroup("expenses") + .whereField("is_active", isEqualTo: true) + .order(by: "date", descending: true) + .limit(to: limit) + + if let lastDocument { + query = query.start(afterDocument: lastDocument) + } + + let expenseSnapshot = try await query.getDocuments() + + let fetchedExpenses = expenseSnapshot.documents.compactMap { doc -> Expense? in + do { + return try doc.data(as: Expense.self) + } catch { + LogE("ExpenseStore: \(#function) Error decoding expense: \(error.localizedDescription)") + return nil + } + } + + return (fetchedExpenses, expenseSnapshot.documents.last) + } } diff --git a/Splito/UI/Home/ActivityLog/Search/SearchExpensesView.swift b/Splito/UI/Home/ActivityLog/Search/SearchExpensesView.swift index 0594b183..b873c7cd 100644 --- a/Splito/UI/Home/ActivityLog/Search/SearchExpensesView.swift +++ b/Splito/UI/Home/ActivityLog/Search/SearchExpensesView.swift @@ -42,14 +42,14 @@ struct SearchExpensesView: View { if case .noExpense = viewModel.viewState { EmptyStateView(geometry: geometry) } else if case .hasExpense = viewModel.viewState { - ExpenseListView(viewModel: viewModel, geometry: geometry) + ExpenseListView(viewModel: viewModel, geometry: geometry, isFocused: $isFocused) } } } } .background(surfaceColor) - .toastView(toast: $viewModel.toast) .toolbarRole(.editor) + .toastView(toast: $viewModel.toast) .alertView.alert(isPresented: $viewModel.showAlert, alertStruct: viewModel.alert) } } @@ -59,6 +59,7 @@ private struct ExpenseListView: View { @ObservedObject var viewModel: SearchExpensesViewModel let geometry: GeometryProxy + var isFocused: FocusState.Binding var body: some View { List { @@ -104,7 +105,7 @@ private struct ExpenseListView: View { Spacer() } .onTapGestureForced { - UIApplication.shared.endEditing() + isFocused.wrappedValue = false } } } diff --git a/Splito/UI/Home/ActivityLog/Search/SearchExpensesViewModel.swift b/Splito/UI/Home/ActivityLog/Search/SearchExpensesViewModel.swift index 42735c08..542c24c4 100644 --- a/Splito/UI/Home/ActivityLog/Search/SearchExpensesViewModel.swift +++ b/Splito/UI/Home/ActivityLog/Search/SearchExpensesViewModel.swift @@ -46,13 +46,13 @@ class SearchExpensesViewModel: BaseViewModel, ObservableObject { func fetchInitialExpenses() { lastDocument = nil Task { - await fetchAllUserExpenses() + await fetchExpensesOfAllGroups() } } // MARK: - Data Loading - private func fetchAllUserExpenses() async { - guard let userId = preference.user?.id, hasMoreExpenses else { + private func fetchExpensesOfAllGroups() async { + guard hasMoreExpenses else { viewState = .noExpense return } @@ -62,13 +62,15 @@ class SearchExpensesViewModel: BaseViewModel, ObservableObject { } do { - let result = try await expenseRepository.fetchExpensesForUser(userId: userId, limit: EXPENSES_LIMIT, lastDocument: lastDocument) + let result = try await expenseRepository.fetchExpensesOfAllGroups(limit: EXPENSES_LIMIT, lastDocument: lastDocument) self.expenses = lastDocument == nil ? result.expenses.uniqued() : (expenses + result.expenses.uniqued()) lastDocument = result.lastDocument await combineMemberWithExpense(expenses: result.expenses.uniqued()) hasMoreExpenses = !(result.expenses.count < self.EXPENSES_LIMIT) LogD("SearchExpensesViewModel: \(#function) Expenses fetched successfully.") + + print("xxx \(expenses.count)") } catch { LogE("SearchExpensesViewModel: \(#function) Failed to fetch expenses: \(error).") handleServiceError()