Add view model and modify MainActivity. Should be everything needed to start testing near-basic functionality and begin tweaking as needed!
This commit is contained in:
parent
e3a2625f68
commit
7b1c0db0cc
@ -0,0 +1,98 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ExpenseViewModel (
|
||||
private val dao: ExpenseDao,
|
||||
): ViewModel() {
|
||||
|
||||
private val _sortType = MutableStateFlow(SortType.NAME)
|
||||
private val _state = MutableStateFlow(ExpenseState())
|
||||
|
||||
private val _expenses = _sortType
|
||||
.flatMapLatest { sortType ->
|
||||
when(sortType) {
|
||||
SortType.NAME -> dao.selectExpensesName()
|
||||
SortType.COST -> dao.selectExpensesCost()
|
||||
SortType.RATE -> dao.selectExpensesRate()
|
||||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
|
||||
|
||||
val state = combine(_state, _sortType, _expenses) { state, sortType, expenses ->
|
||||
state.copy(
|
||||
expenses = expenses,
|
||||
sortType = sortType
|
||||
)
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ExpenseState())
|
||||
|
||||
fun onEvent(event: ExpenseEvent) {
|
||||
when(event) {
|
||||
is ExpenseEvent.DeleteExpense -> {
|
||||
viewModelScope.launch {
|
||||
dao.deleteExpense(event.expense)
|
||||
}
|
||||
}
|
||||
ExpenseEvent.HideDialog -> {
|
||||
_state.update { it.copy(
|
||||
isAddingExpense = false
|
||||
) }
|
||||
}
|
||||
ExpenseEvent.SaveExpense -> {
|
||||
val name = state.value.name
|
||||
val cost = state.value.cost
|
||||
val rate = state.value.rate
|
||||
|
||||
if (name.isBlank()
|
||||
|| cost.isBlank()
|
||||
//|| rate.isBlank() # TBD, enable this once Rate is working.
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
val expense = Expense(name, cost, rate)
|
||||
|
||||
viewModelScope.launch {
|
||||
dao.upsertExpense(expense)
|
||||
}
|
||||
|
||||
_state.update { it.copy(
|
||||
isAddingExpense = false,
|
||||
name = "",
|
||||
cost = "",
|
||||
rate = Rate.MONTHLY,
|
||||
) }
|
||||
}
|
||||
is ExpenseEvent.SetName -> {
|
||||
_state.update { it.copy(
|
||||
name = event.name
|
||||
) }
|
||||
}
|
||||
is ExpenseEvent.SetCost -> {
|
||||
_state.update { it.copy(
|
||||
cost = event.cost
|
||||
) }
|
||||
}
|
||||
is ExpenseEvent.SetRate -> {
|
||||
_state.update { it.copy(
|
||||
rate = event.rate
|
||||
) }
|
||||
}
|
||||
ExpenseEvent.ShowDialog -> {
|
||||
_state.update { it.copy(
|
||||
isAddingExpense = true
|
||||
) }
|
||||
}
|
||||
is ExpenseEvent.SortExpenses -> {
|
||||
_sortType.value = event.sortType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import com.hyperling.expensetracker.ui.theme.ExpenseTrackerTheme
|
||||
|
||||
class MainActivityOLD : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
ExpenseTrackerTheme {
|
||||
Surface {
|
||||
Main()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This seems like a great tutorial for what I need to do!
|
||||
// https://www.answertopia.com/jetpack-compose/a-jetpack-compose-room-database-and-repository-tutorial/
|
||||
|
||||
@Composable
|
||||
fun Main() {
|
||||
val context = LocalContext.current
|
||||
|
||||
var sumDaily by remember { mutableStateOf(0.0) }
|
||||
var sumWeekly by remember { mutableStateOf(0.0) }
|
||||
var sumMonthly by remember { mutableStateOf(0.0) }
|
||||
var sumYearly by remember { mutableStateOf(0.0) }
|
||||
|
||||
val sums = mapOf(
|
||||
"Daily" to sumDaily,
|
||||
"Weekly" to sumWeekly,
|
||||
"Monthly" to sumMonthly,
|
||||
"Yearly" to sumYearly,
|
||||
)
|
||||
|
||||
Column (
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Current Expense Summary"
|
||||
)
|
||||
|
||||
//for ((name, value) in sums) {
|
||||
sums.forEach { (name, value) ->
|
||||
Row (
|
||||
horizontalArrangement = Arrangement.End,
|
||||
){
|
||||
Text(text = name + ": ")
|
||||
Text(text = value.toString())
|
||||
}
|
||||
}
|
||||
|
||||
// FORTESTING
|
||||
Text (text = String.format("%.2f",sumDaily))
|
||||
Text (text = String.format("%.2f",sumWeekly))
|
||||
Text (text = String.format("%.2f",sumMonthly))
|
||||
Text (text = String.format("%.2f",sumYearly))
|
||||
|
||||
Row (
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Button(onClick = {
|
||||
val intent = Intent(context, NewExpenseActivity::class.java)
|
||||
context.startActivity(intent)
|
||||
}) {
|
||||
Text(text = "Create New Expense")
|
||||
}
|
||||
}
|
||||
|
||||
Text(text = "Expenses")
|
||||
// https://medium.com/@rowaido.game/implementing-the-room-library-with-jetpack-compose-590d13101fa7
|
||||
/* TODO:
|
||||
ForEach over all DB records, show it, and add its value to the summary.
|
||||
*/
|
||||
val expenseArray = listOf(
|
||||
Expense("Test", 180.0, 'Y', "My notes."),
|
||||
Expense("Test2", 20.0, 'M', "My notes, 2!"),
|
||||
)
|
||||
expenseArray.forEach { expense ->
|
||||
when (expense.freq) {
|
||||
'D' -> sumYearly += (expense.cost * 365.25)
|
||||
'W' -> sumYearly += (expense.cost * (365.25 / 7))
|
||||
'M' -> sumYearly += (expense.cost * 12)
|
||||
'Y' -> sumYearly += (expense.cost)
|
||||
}
|
||||
|
||||
sumDaily = sumYearly / 365.25
|
||||
sumWeekly = sumYearly / (365.25 / 7)
|
||||
sumMonthly = sumYearly / 12
|
||||
}
|
||||
|
||||
// FORTESTING
|
||||
Text (text = String.format("$ %.2f",sumDaily))
|
||||
Text (text = String.format("$ %.2f",sumWeekly))
|
||||
Text (text = String.format("$ %.2f",sumMonthly))
|
||||
Text (text = String.format("$ %.2f",sumYearly))
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun MainPreview() {
|
||||
ExpenseTrackerTheme {
|
||||
Main()
|
||||
}
|
||||
}
|
@ -1,136 +1,61 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.hyperling.expensetracker.ui.theme.ExpenseTrackerTheme
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import com.hyperling.expensetracker.ui.theme.ExpenseTrackerTheme
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.room.Room
|
||||
import androidx.compose.material3.Surface
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
private val _db by lazy {
|
||||
Room.databaseBuilder(
|
||||
applicationContext,
|
||||
ExpenseDatabase::class.java,
|
||||
"expenses.db"
|
||||
).build()
|
||||
}
|
||||
private val _viewModel by viewModels<ExpenseViewModel> (
|
||||
factoryProducer = {
|
||||
object : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ExpenseViewModel(_db.dao) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
ExpenseTrackerTheme {
|
||||
Surface {
|
||||
Main()
|
||||
}
|
||||
val state by _viewModel.state.collectAsState()
|
||||
ExpenseScreen(
|
||||
state = state,
|
||||
onEvent = _viewModel::onEvent
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This seems like a great tutorial for what I need to do!
|
||||
// https://www.answertopia.com/jetpack-compose/a-jetpack-compose-room-database-and-repository-tutorial/
|
||||
|
||||
@Composable
|
||||
fun Main() {
|
||||
val context = LocalContext.current
|
||||
|
||||
var sumDaily by remember { mutableStateOf(0.0) }
|
||||
var sumWeekly by remember { mutableStateOf(0.0) }
|
||||
var sumMonthly by remember { mutableStateOf(0.0) }
|
||||
var sumYearly by remember { mutableStateOf(0.0) }
|
||||
|
||||
val sums = mapOf(
|
||||
"Daily" to sumDaily,
|
||||
"Weekly" to sumWeekly,
|
||||
"Monthly" to sumMonthly,
|
||||
"Yearly" to sumYearly,
|
||||
)
|
||||
|
||||
Column (
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Current Expense Summary"
|
||||
)
|
||||
|
||||
//for ((name, value) in sums) {
|
||||
sums.forEach { (name, value) ->
|
||||
Row (
|
||||
horizontalArrangement = Arrangement.End,
|
||||
){
|
||||
Text(text = name + ": ")
|
||||
Text(text = value.toString())
|
||||
}
|
||||
}
|
||||
|
||||
// FORTESTING
|
||||
Text (text = String.format("%.2f",sumDaily))
|
||||
Text (text = String.format("%.2f",sumWeekly))
|
||||
Text (text = String.format("%.2f",sumMonthly))
|
||||
Text (text = String.format("%.2f",sumYearly))
|
||||
|
||||
Row (
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Button(onClick = {
|
||||
val intent = Intent(context, NewExpenseActivity::class.java)
|
||||
context.startActivity(intent)
|
||||
}) {
|
||||
Text(text = "Create New Expense")
|
||||
}
|
||||
}
|
||||
|
||||
Text(text = "Expenses")
|
||||
// https://medium.com/@rowaido.game/implementing-the-room-library-with-jetpack-compose-590d13101fa7
|
||||
/* TODO:
|
||||
ForEach over all DB records, show it, and add its value to the summary.
|
||||
*/
|
||||
val expenseArray = listOf(
|
||||
Expense("Test", 180.0, 'Y', "My notes."),
|
||||
Expense("Test2", 20.0, 'M', "My notes, 2!"),
|
||||
)
|
||||
expenseArray.forEach { expense ->
|
||||
when (expense.freq) {
|
||||
'D' -> sumYearly += (expense.cost * 365.25)
|
||||
'W' -> sumYearly += (expense.cost * (365.25 / 7))
|
||||
'M' -> sumYearly += (expense.cost * 12)
|
||||
'Y' -> sumYearly += (expense.cost)
|
||||
}
|
||||
|
||||
sumDaily = sumYearly / 365.25
|
||||
sumWeekly = sumYearly / (365.25 / 7)
|
||||
sumMonthly = sumYearly / 12
|
||||
}
|
||||
|
||||
// FORTESTING
|
||||
Text (text = String.format("$ %.2f",sumDaily))
|
||||
Text (text = String.format("$ %.2f",sumWeekly))
|
||||
Text (text = String.format("$ %.2f",sumMonthly))
|
||||
Text (text = String.format("$ %.2f",sumYearly))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun MainPreview() {
|
||||
@ -138,3 +63,4 @@ fun MainPreview() {
|
||||
Main()
|
||||
}
|
||||
}
|
||||
*/
|
Loading…
x
Reference in New Issue
Block a user