From 4104930ea5d8fd5f6b0b181a985d65df48f7cf80 Mon Sep 17 00:00:00 2001 From: "Maurice L." Date: Wed, 4 Mar 2026 12:28:12 +0100 Subject: [PATCH] Added UserRepository and ExpenseShareRepository Implemented `UserRepository` and `ExpenseShareRepository` to handle data fetching with a caching strategy (local DAO + remote API). Specific changes include: - Added `getUserById` to `UserDao` and updated `getShareById` in `ExpenseShareDao` to support nullable returns. - Updated `APIService` and `ComDataTypes` to include endpoints and data models for User info, Expense Shares, and pluralized Expense responses. - Refactored `ExpenseRepository` to use the updated API naming conventions and removed debug print statements. --- .../shap_planner/entities/ExpenseShare.kt | 2 +- .../miaurizius/shap_planner/entities/User.kt | 3 ++ .../shap_planner/network/APIService.kt | 10 +++- .../shap_planner/network/ComDataTypes.kt | 11 +++- .../repository/ExpenseRepository.kt | 5 +- .../repository/ExpenseShareRepository.kt | 53 +++++++++++++++++++ .../shap_planner/repository/UserRepository.kt | 31 +++++++++++ 7 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/de/miaurizius/shap_planner/repository/ExpenseShareRepository.kt create mode 100644 app/src/main/java/de/miaurizius/shap_planner/repository/UserRepository.kt diff --git a/app/src/main/java/de/miaurizius/shap_planner/entities/ExpenseShare.kt b/app/src/main/java/de/miaurizius/shap_planner/entities/ExpenseShare.kt index 231a117..79b88b8 100644 --- a/app/src/main/java/de/miaurizius/shap_planner/entities/ExpenseShare.kt +++ b/app/src/main/java/de/miaurizius/shap_planner/entities/ExpenseShare.kt @@ -24,7 +24,7 @@ interface ExpenseShareDao { fun getAllShares(): Flow> @Query("SELECT * FROM expense_shares WHERE id = :shareId") - fun getShareById(shareId: UUID): Flow + fun getShareById(shareId: UUID): Flow @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertShare(share: ExpenseShare) diff --git a/app/src/main/java/de/miaurizius/shap_planner/entities/User.kt b/app/src/main/java/de/miaurizius/shap_planner/entities/User.kt index 0d37e36..ddfa7ed 100644 --- a/app/src/main/java/de/miaurizius/shap_planner/entities/User.kt +++ b/app/src/main/java/de/miaurizius/shap_planner/entities/User.kt @@ -22,6 +22,9 @@ interface UserDao { @Query("SELECT * FROM users") fun getAllUsers(): Flow> + @Query("SELECT * FROM users WHERE id = :userId") + fun getUserById(userId: UUID): Flow + @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertUser(user: User) diff --git a/app/src/main/java/de/miaurizius/shap_planner/network/APIService.kt b/app/src/main/java/de/miaurizius/shap_planner/network/APIService.kt index 6a63453..e6eee00 100644 --- a/app/src/main/java/de/miaurizius/shap_planner/network/APIService.kt +++ b/app/src/main/java/de/miaurizius/shap_planner/network/APIService.kt @@ -21,7 +21,7 @@ interface APIService { // Expenses @GET("api/expenses") - suspend fun expenseGet(@Header("Authorization") token: String): Response + suspend fun expensesGet(@Header("Authorization") token: String): Response @POST("api/expenses") suspend fun expenseCreate(@Header("Authorization") token: String) @PUT("api/expenses") @@ -29,7 +29,13 @@ interface APIService { @DELETE("api/expenses") suspend fun expenseDelete(@Header("Authorization") token: String) + // Shares + @GET("api/shares") + suspend fun sharesGet(@Header("Authorization") token: String): Response + @GET("api/shares") + suspend fun shareGet(@Header("Authorization") token: String, @Query("id") shareId: UUID): Response + // User @GET("api/userinfo") - suspend fun userinfo(@Header("Authorization") token: String, @Query("id") userId: UUID) + suspend fun userinfo(@Header("Authorization") token: String, @Query("id") userId: UUID): Response } \ No newline at end of file diff --git a/app/src/main/java/de/miaurizius/shap_planner/network/ComDataTypes.kt b/app/src/main/java/de/miaurizius/shap_planner/network/ComDataTypes.kt index a7e1616..d414417 100644 --- a/app/src/main/java/de/miaurizius/shap_planner/network/ComDataTypes.kt +++ b/app/src/main/java/de/miaurizius/shap_planner/network/ComDataTypes.kt @@ -1,6 +1,8 @@ package de.miaurizius.shap_planner.network import de.miaurizius.shap_planner.entities.Expense +import de.miaurizius.shap_planner.entities.ExpenseShare +import de.miaurizius.shap_planner.entities.User // Login data class LoginRequest(val username: String, val password: String) @@ -12,4 +14,11 @@ data class RefreshRequest(val refresh_token: String) data class RefreshResponse(val access_token: String, val refresh_token: String) // Expenses -data class ExpenseResponse(val expenses: List) \ No newline at end of file +data class ExpensesResponse(val expenses: List) + +// ExpenseShares +data class ExpenseSharesResponse(val shares: List) +data class ExpenseShareResponse(val share: ExpenseShare) + +// User +data class UserinfoResponse(val user: User) \ No newline at end of file diff --git a/app/src/main/java/de/miaurizius/shap_planner/repository/ExpenseRepository.kt b/app/src/main/java/de/miaurizius/shap_planner/repository/ExpenseRepository.kt index 93b37fc..780d300 100644 --- a/app/src/main/java/de/miaurizius/shap_planner/repository/ExpenseRepository.kt +++ b/app/src/main/java/de/miaurizius/shap_planner/repository/ExpenseRepository.kt @@ -13,18 +13,15 @@ class ExpenseRepository( ) { fun getExpenses(token: String, forceRefresh: Boolean = false): Flow>> = flow { val cachedExpense = dao.getAllExpenses().first() - println("CachedExpense: $cachedExpense") emit(Resource.Loading(cachedExpense)) if(cachedExpense.isEmpty() || forceRefresh) { try { - val response = api.expenseGet("Bearer $token") + val response = api.expensesGet("Bearer $token") if(response.isSuccessful) { val remoteExpense = response.body()?.expenses ?: emptyList() - println("Fetched expenses: $remoteExpense") remoteExpense.forEach { dao.insertExpense(it) - println("Added $it") } } } catch(e: Exception) { diff --git a/app/src/main/java/de/miaurizius/shap_planner/repository/ExpenseShareRepository.kt b/app/src/main/java/de/miaurizius/shap_planner/repository/ExpenseShareRepository.kt new file mode 100644 index 0000000..f172e90 --- /dev/null +++ b/app/src/main/java/de/miaurizius/shap_planner/repository/ExpenseShareRepository.kt @@ -0,0 +1,53 @@ +package de.miaurizius.shap_planner.repository + +import de.miaurizius.shap_planner.entities.ExpenseShare +import de.miaurizius.shap_planner.entities.ExpenseShareDao +import de.miaurizius.shap_planner.network.APIService +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flow +import java.util.UUID + +class ExpenseShareRepository( + private val dao: ExpenseShareDao, + private val api: APIService +) { + fun getShares(token: String, forceRefresh: Boolean = false): Flow>> = flow { + val cachedData = dao.getAllShares().first() + emit(Resource.Loading(cachedData)) + + if(cachedData.isEmpty() || forceRefresh) { + try { + val response = api.sharesGet("Bearer $token") + if(response.isSuccessful) { + val remoteShare = response.body()?.shares ?: emptyList() + remoteShare.forEach { + dao.insertShare(it) + } + } + } catch(e: Exception) { + emit(Resource.Error("Network Error: ${e.localizedMessage}", cachedData)) + } + } + dao.getAllShares().collect { emit(Resource.Success(it)) } + } + + fun getShareById(token: String, shareId: UUID, forceRefresh: Boolean = false): Flow> = flow { + val cached = dao.getShareById(shareId).first() + emit(Resource.Loading(cached)) + if(cached == null || forceRefresh) { + try { + val response = api.shareGet("Bearer $token", shareId) + if(response.isSuccessful) { + response.body()?.share?.let { remoteShare -> dao.insertShare(remoteShare) } + } + } catch(e: Exception) { + emit(Resource.Error("Network-Error: ${e.localizedMessage}", cached)) + } + } + dao.getShareById(shareId).collect { share -> + if(share != null) emit(Resource.Success(share)) + else emit(Resource.Error("Share nicht gefunden", null)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/miaurizius/shap_planner/repository/UserRepository.kt b/app/src/main/java/de/miaurizius/shap_planner/repository/UserRepository.kt new file mode 100644 index 0000000..5495015 --- /dev/null +++ b/app/src/main/java/de/miaurizius/shap_planner/repository/UserRepository.kt @@ -0,0 +1,31 @@ +package de.miaurizius.shap_planner.repository + +import de.miaurizius.shap_planner.entities.User +import de.miaurizius.shap_planner.entities.UserDao +import de.miaurizius.shap_planner.network.APIService +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flow +import java.util.UUID + +class UserRepository( + private val dao: UserDao, + private val api: APIService +) { + fun getUser(token: String, userId: UUID, forceRefresh: Boolean = false): Flow> = flow { + val cached = dao.getUserById(userId).first() + emit(Resource.Loading(cached)) + if(cached == null || forceRefresh) { + try { + val response = api.userinfo("Bearer $token", userId) + if(response.isSuccessful) { + response.body()?.user?.let { remoteUser -> dao.insertUser(remoteUser) } + } + } catch(e: Exception) { + emit(Resource.Error("Network-Error: ${e.localizedMessage}", cached)) + } + } + dao.getUserById(userId).collect { user -> if(user != null) emit(Resource.Success(user)) else emit( + Resource.Error("User nicht gefunden", null)) } + } +} \ No newline at end of file