Skip to content

Commit

Permalink
Merge pull request #113 from choffmann/implement-dashboard-viewmodel
Browse files Browse the repository at this point in the history
Implement Dashboard Viewmodel
  • Loading branch information
PasclDev authored Jun 19, 2022
2 parents 7c93061 + b0201eb commit 8dbf77d
Show file tree
Hide file tree
Showing 20 changed files with 791 additions and 255 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,45 +16,45 @@ actual fun CategoryImageToIcon(icon: Category.Image) {
Category.Image.WRONG -> Icon(imageVector = Icons.Default.Dangerous, contentDescription = null)
Category.Image.HOME -> Icon(imageVector = Icons.Default.Home, contentDescription = null)
Category.Image.FOOD -> Icon(imageVector = Icons.Default.BakeryDining, contentDescription = null)
Category.Image.FASTFOOD -> Icon(imageVector = Icons.Default.BakeryDining, contentDescription = null)
Category.Image.FASTFOOD -> Icon(imageVector = Icons.Default.Fastfood, contentDescription = null)
Category.Image.RESTAURANT -> Icon(imageVector = Icons.Default.Restaurant, contentDescription = null)
Category.Image.FAMILY -> Icon(imageVector = Icons.Default.People, contentDescription = null)
Category.Image.MONEY -> Icon(imageVector = Icons.Default.Payments, contentDescription = null)
Category.Image.HEALTH -> Icon(imageVector = Icons.Default.HealthAndSafety, contentDescription = null)
Category.Image.MEDICATION -> Icon(imageVector = Icons.Default.Medication, contentDescription = null)
Category.Image.INVEST -> Icon(imageVector = Icons.Default.QueryStats, contentDescription = null)
Category.Image.SPORT -> Icon(imageVector = Icons.Default.SportsSoccer, contentDescription = null)
Category.Image.CLOTH -> Icon(imageVector = Icons.Default.Checkroom, contentDescription = null)
Category.Image.GIFT -> Icon(imageVector = Icons.Default.Redeem, contentDescription = null)
Category.Image.WEALTH -> Icon(imageVector = Icons.Default.MonetizationOn, contentDescription = null)
Category.Image.FLOWER -> Icon(imageVector = Icons.Default.LocalFlorist, contentDescription = null)
Category.Image.PET -> Icon(imageVector = Icons.Default.Pets, contentDescription = null)
Category.Image.BILLS -> Icon(imageVector = Icons.Default.Receipt, contentDescription = null)
Category.Image.KEYBOARD -> Icon(imageVector = Icons.Default.Keyboard, contentDescription = null)
Category.Image.PRINTER -> Icon(imageVector = Icons.Default.Print, contentDescription = null)
Category.Image.WATER -> Icon(imageVector = Icons.Default.WaterDrop, contentDescription = null)
Category.Image.FIRE -> Icon(imageVector = Icons.Default.LocalFireDepartment, contentDescription = null)
Category.Image.STAR -> Icon(imageVector = Icons.Default.Grade, contentDescription = null)
Category.Image.SAVINGS -> Icon(imageVector = Icons.Default.Savings, contentDescription = null)
Category.Image.CAR -> Icon(imageVector = Icons.Default.CarRepair, contentDescription = null)
Category.Image.BIKE -> Icon(imageVector = Icons.Default.PedalBike, contentDescription = null)
Category.Image.TRAIN -> Icon(imageVector = Icons.Default.DirectionsTransit, contentDescription = null)
Category.Image.MOPED -> Icon(imageVector = Icons.Default.Moped, contentDescription = null)
Category.Image.MOTORCYCLE -> Icon(imageVector = Icons.Default.TwoWheeler, contentDescription = null)
Category.Image.ELECTRONICS -> Icon(imageVector = Icons.Default.ElectricalServices, contentDescription = null)
Category.Image.BOOK -> Icon(imageVector = Icons.Default.Book, contentDescription = null)
Category.Image.FLIGHT -> Icon(imageVector = Icons.Default.FlightTakeoff, contentDescription = null)
Category.Image.WORK -> Icon(imageVector = Icons.Default.Work, contentDescription = null)
Category.Image.MOON -> Icon(imageVector = Icons.Default.NightlightRound, contentDescription = null)
Category.Image.LOCK -> Icon(imageVector = Icons.Default.Https, contentDescription = null)
Category.Image.PHONE -> Icon(imageVector = Icons.Default.PermPhoneMsg, contentDescription = null)
Category.Image.STORE -> Icon(imageVector = Icons.Default.StoreMallDirectory, contentDescription = null)
Category.Image.BAR -> Icon(imageVector = Icons.Default.Nightlife, contentDescription = null)
Category.Image.FOREST -> Icon(imageVector = Icons.Default.Redeem, contentDescription = null)
Category.Image.HARDWARE -> Icon(imageVector = Icons.Default.Hardware, contentDescription = null)
Category.Image.PEST -> Icon(imageVector = Icons.Default.PestControl, contentDescription = null)
else -> {
Icon(imageVector = Icons.Default.QuestionAnswer, contentDescription = null)
Icon(imageVector = Icons.Default.ShoppingCart, contentDescription = null)
}
/*Category.Image.RESTAURANT -> "restaurant"
Category.Image.FAMILY -> "people"
Category.Image.MONEY -> "payments"
Category.Image.HEALTH -> "health_and_safety"
Category.Image.MEDICATION -> "medication"
Category.Image.INVEST -> "query_stats"
Category.Image.SPORT -> "sports_soccer"
Category.Image.CLOTH -> "checkroom"
Category.Image.GIFT -> "redeem"
Category.Image.WEALTH -> "monetization_on"
Category.Image.FLOWER -> "local_florist"
Category.Image.PET -> "pets"
Category.Image.BILLS -> "receipt"
Category.Image.KEYBOARD-> "redeem"
Category.Image.PRINTER-> "print"
Category.Image.WATER -> "water_drop"
Category.Image.FIRE -> "fire"
Category.Image.STAR -> "grade"
Category.Image.SAVINGS -> "savings"
Category.Image.CAR -> "minor_crash"
Category.Image.BIKE -> "pedal_bike"
Category.Image.TRAIN -> "directions_transit"
Category.Image.MOPED-> "moped"
Category.Image.MOTORCYCLE -> "two_wheeler"
Category.Image.ELECTRONICS -> "electrical_services"
Category.Image.BOOK -> "book"
Category.Image.FLIGHT -> "flight_takeoff"
Category.Image.WORK -> "work"
Category.Image.MOON -> "nightlight_round"
Category.Image.LOCK -> "https"
Category.Image.PHONE -> "perm_phone_msg"
Category.Image.STORE -> "store_mall_directory"
Category.Image.BAR -> "nightlife"
Category.Image.FOREST -> "forest"
Category.Image.HARDWARE -> "hardware"
Category.Image.PEST -> "pest_control"*/
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,17 @@ interface ApiClient {
suspend fun deleteCategoryById(id: Int): APIResponse<Category>

/**
* Get Entries from a Category ID
* Get Entries from a Category ID. The request has a query in the link ?current
* @param id ID from category to get the Entries. When ID null, the Api returns all entries with no category
*/
suspend fun getEntriesFromCategory(id: Int?): APIResponse<List<Entry>>

/**
* Get Entries from a Category ID on period
* @param id ID from category to get the Entries
* @param period Period definition in format MM-YYYY (03-2022)
*/
suspend fun getEntriesFromCategory(id: Int): APIResponse<List<Entry>>
suspend fun getEntriesFromCategory(id: Int?, period: String): APIResponse<List<Entry>>

/**
* Get All Entries from current month. The request has a query in the link ?current
Expand Down Expand Up @@ -160,7 +167,7 @@ expect fun HttpClientConfig<*>.specificClientConfig()
* @author Cedrik Hoffmann
* @see de.hsfl.budgetBinder.data.client.ApiClient
*/
class Client( engine: HttpClientEngine) : ApiClient {
class Client(engine: HttpClientEngine) : ApiClient {
private val client = HttpClient(engine) {
install(ContentNegotiation) {
json()
Expand Down Expand Up @@ -261,8 +268,16 @@ class Client( engine: HttpClientEngine) : ApiClient {
}.body()
}

override suspend fun getEntriesFromCategory(id: Int): APIResponse<List<Entry>> {
return client.get(urlString = "/categories/$id/entries").body()
override suspend fun getEntriesFromCategory(id: Int?): APIResponse<List<Entry>> {
return client.submitForm(url = "/categories/$id/entries", formParameters = Parameters.build {
append("current", "true")
}, encodeInQuery = true).body()
}

override suspend fun getEntriesFromCategory(id: Int?, period: String): APIResponse<List<Entry>> {
return client.submitForm(url = "/categories/$id/entries", formParameters = Parameters.build {
append("period", period)
}, encodeInQuery = true).body()
}

override suspend fun getAllEntries(): APIResponse<List<Entry>> {
Expand All @@ -272,7 +287,7 @@ class Client( engine: HttpClientEngine) : ApiClient {
}

override suspend fun getAllEntries(period: String): APIResponse<List<Entry>> {
return client.submitForm(url = "/categories", formParameters = Parameters.build {
return client.submitForm(url = "/entries", formParameters = Parameters.build {
append("period", period)
}, encodeInQuery = true).body()
}
Expand Down Expand Up @@ -300,4 +315,4 @@ class Client( engine: HttpClientEngine) : ApiClient {
contentType(ContentType.Application.Json)
}.body()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ class CategoryRepositoryImpl(
return client.deleteCategoryById(id)
}

override suspend fun getEntriesFromCategory(id: Int): APIResponse<List<Entry>> {
override suspend fun getEntriesFromCategory(id: Int?): APIResponse<List<Entry>> {
return client.getEntriesFromCategory(id)
}
}

override suspend fun getEntriesFromCategory(id: Int?, period: String): APIResponse<List<Entry>> {
return client.getEntriesFromCategory(id, period)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import de.hsfl.budgetBinder.presentation.flow.UiEventSharedFlow
import de.hsfl.budgetBinder.presentation.viewmodel.login.LoginViewModel
import de.hsfl.budgetBinder.presentation.viewmodel.register.RegisterViewModel
import de.hsfl.budgetBinder.presentation.viewmodel.*
import de.hsfl.budgetBinder.presentation.viewmodel.dashboard.DashboardViewModel
import de.hsfl.budgetBinder.presentation.viewmodel.navdrawer.NavDrawerViewModel
import de.hsfl.budgetBinder.presentation.viewmodel.settings.SettingsEditServerUrlViewModel
import de.hsfl.budgetBinder.presentation.viewmodel.settings.SettingsEditUserViewModel
Expand Down Expand Up @@ -77,7 +78,7 @@ fun kodein(ktorEngine: HttpClientEngine) = DI {
bindSingleton { CategoriesUseCases(instance(), instance(), instance(), instance(), instance(), instance()) }
bindSingleton { SettingsUseCases(instance(), instance(), instance()) }
bindSingleton { LoginUseCases(instance(), instance()) }
bindSingleton { DashboardUseCases(instance(), instance()) }
bindSingleton { DashboardUseCases(instance(), instance(), instance(), instance()) }
bindSingleton { RegisterUseCases(instance(), instance(), instance()) }
bindSingleton { DataFlowUseCases(instance(), instance(), instance()) }

Expand All @@ -94,6 +95,6 @@ fun kodein(ktorEngine: HttpClientEngine) = DI {
bindSingleton { SettingsEditServerUrlViewModel(instance(), instance(), instance()) }
bindSingleton { CategoryViewModel(instance(), instance()) }
bindSingleton { EntryViewModel(instance(), instance()) }
bindSingleton { DashboardViewModel(instance(), instance(), instance(), instance(), instance(), instance()) }
bindSingleton { NavDrawerViewModel(instance(), instance(), instance()) }
bindSingleton { DashboardViewModel(instance(), instance(), instance(), instance()) }
bindSingleton { NavDrawerViewModel(instance(), instance()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,17 @@ interface CategoryRepository {
suspend fun deleteCategoryById(id: Int): APIResponse<Category>

/**
* Get all entries from a category
* Get all entries from a category of current month
* @param id ID from Category to get all Entries from this
* @author Cedrik Hoffmann
*/
suspend fun getEntriesFromCategory(id: Int): APIResponse<List<Entry>>
}
suspend fun getEntriesFromCategory(id: Int?): APIResponse<List<Entry>>

/**
* Get all entries from a category on period of time
* @param id ID from Category to get all Entries from this
* @param period Time period in format MM-YYYY (03-2022)
* @author Cedrik Hoffmann
*/
suspend fun getEntriesFromCategory(id: Int?, period: String): APIResponse<List<Entry>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,21 @@ class DeleteCategoryByIdUseCase(private val repository: CategoryRepository) {
}

class GetAllEntriesByCategoryUseCase(private val repository: CategoryRepository) {
operator fun invoke(id: Int): Flow<DataResponse<List<Entry>>> = flow {
operator fun invoke(id: Int?, period: String? = null): Flow<DataResponse<List<Entry>>> = flow {
try {
emit(DataResponse.Loading())
repository.getEntriesFromCategory(id).let { response ->
period?.let { period ->
repository.getEntriesFromCategory(id, period).let { response ->
response.data?.let {
emit(DataResponse.Success(it))
} ?: response.error!!.let { error ->
when (error.code) {
HttpStatusCode.Unauthorized.value -> emit(DataResponse.Unauthorized(error))
else -> emit(DataResponse.Error(error))
}
}
}
} ?: repository.getEntriesFromCategory(id).let { response ->
response.data?.let {
emit(DataResponse.Success(it))
} ?: response.error!!.let { error ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ package de.hsfl.budgetBinder.domain.usecase

data class DashboardUseCases(
val getAllEntriesUseCase: GetAllEntriesUseCase,
val getAllCategoriesUseCase: GetAllCategoriesUseCase
val getAllCategoriesUseCase: GetAllCategoriesUseCase,
val getAllEntriesByCategoryUseCase: GetAllEntriesByCategoryUseCase,
val deleteEntryByIdUseCase: DeleteEntryByIdUseCase
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.hsfl.budgetBinder.presentation

import de.hsfl.budgetBinder.domain.usecase.DeleteEntryByIdUseCase

sealed class Screen {
sealed class Welcome: Screen() {
object Screen1: Welcome()
Expand All @@ -18,8 +20,9 @@ sealed class Screen {
object CreateOnRegister: Category()
}
sealed class Entry: Screen() {
object Edit: Category()
object Create: Category()
data class Overview(val id: Int): Entry()
object Edit: Entry()
object Create: Entry()
}
object Login : Screen()
object Register : Screen()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ sealed class UiEvent {

// Show Success
data class ShowSuccess(val msg: String) : UiEvent()

// Call on Success, which could reset the loading state
object HideSuccess : UiEvent()
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package de.hsfl.budgetBinder.presentation.viewmodel.dashboard

import de.hsfl.budgetBinder.common.Category
import de.hsfl.budgetBinder.common.Entry

data class DashboardEntryState(
val entry: Entry,
val categoryImage: Category.Image = Category.Image.DEFAULT
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.hsfl.budgetBinder.presentation.viewmodel.dashboard


sealed class DashboardEvent {
object OnPrevCategory : DashboardEvent()
object OnNextCategory : DashboardEvent()
object OnRefresh: DashboardEvent()
object OnLoadMore: DashboardEvent()
data class OnEntry(val id: Int) : DashboardEvent()
data class OnEntryDelete(val id: Int): DashboardEvent()
object OnEntryCreate : DashboardEvent()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.hsfl.budgetBinder.presentation.viewmodel.dashboard

import de.hsfl.budgetBinder.common.Category
import de.hsfl.budgetBinder.common.Entry

data class DashboardState(
val hasPrev: Boolean = false,
val hasNext: Boolean = true,
val category: Category = Category(0, "Overall", "111111", Category.Image.DEFAULT, 0f),
val entryList: List<DashboardEntryState> = emptyList(),
val spendBudgetOnCurrentCategory: Float = 0f
)
Loading

0 comments on commit 8dbf77d

Please sign in to comment.