diff --git a/compose/.gradle/8.0/executionHistory/executionHistory.bin b/compose/.gradle/8.0/executionHistory/executionHistory.bin index 4ea19bb..5a77576 100644 Binary files a/compose/.gradle/8.0/executionHistory/executionHistory.bin and b/compose/.gradle/8.0/executionHistory/executionHistory.bin differ diff --git a/compose/.gradle/8.0/executionHistory/executionHistory.lock b/compose/.gradle/8.0/executionHistory/executionHistory.lock index e8ea792..934b1b5 100644 Binary files a/compose/.gradle/8.0/executionHistory/executionHistory.lock and b/compose/.gradle/8.0/executionHistory/executionHistory.lock differ diff --git a/compose/.gradle/8.0/fileHashes/fileHashes.bin b/compose/.gradle/8.0/fileHashes/fileHashes.bin index 5333363..b3d4430 100644 Binary files a/compose/.gradle/8.0/fileHashes/fileHashes.bin and b/compose/.gradle/8.0/fileHashes/fileHashes.bin differ diff --git a/compose/.gradle/8.0/fileHashes/fileHashes.lock b/compose/.gradle/8.0/fileHashes/fileHashes.lock index 71fc5ff..cb8c2c9 100644 Binary files a/compose/.gradle/8.0/fileHashes/fileHashes.lock and b/compose/.gradle/8.0/fileHashes/fileHashes.lock differ diff --git a/compose/.gradle/8.0/fileHashes/resourceHashesCache.bin b/compose/.gradle/8.0/fileHashes/resourceHashesCache.bin index 27a514a..1f01126 100644 Binary files a/compose/.gradle/8.0/fileHashes/resourceHashesCache.bin and b/compose/.gradle/8.0/fileHashes/resourceHashesCache.bin differ diff --git a/compose/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/compose/.gradle/buildOutputCleanup/buildOutputCleanup.lock index f6a73f8..c306d64 100644 Binary files a/compose/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/compose/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/TankList.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/TankList.kt index b1bf574..f7a84d9 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/TankList.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/TankList.kt @@ -72,18 +72,6 @@ fun ColumnItem( number: Int, tanks: List ) { - /* - val context = LocalContext.current - val tanks = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - AppDatabase.getInstance(context).tankDao().getAll().collect() { data -> - tanks.clear() - tanks.addAll(data) - tanks.reverse() - } - } - }*/ Column( modifier = Modifier.padding(0.dp, 10.dp) ) { @@ -223,7 +211,10 @@ fun TankEmptyListPreview() { Surface( color = CustomDark ) { - TankList(numbers = listOf(1, 2, 3), listTanks = listOf()) + TankList( + numbers = listOf(1, 2, 3), + listTanks = listOf() + ) } } } \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/edit/TankEditViewModel.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/edit/TankEditViewModel.kt new file mode 100644 index 0000000..130942d --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/edit/TankEditViewModel.kt @@ -0,0 +1,96 @@ +package ru.ulstu.`is`.pmu.tank.composeui.edit + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import ru.ulstu.`is`.pmu.tank.model.Tank +import ru.ulstu.`is`.pmu.tank.repository.TankRepository + +class TankEditViewModel( + savedStateHandle: SavedStateHandle, + private val tankRepository: TankRepository +) : ViewModel() { + var tankUiState by mutableStateOf(TankUiState()) + private set + + private val tankUid: Long = checkNotNull(savedStateHandle["id"]) + + init { + viewModelScope.launch { + if (tankUid > 0) { + tankUiState = tankRepository.getTank(tankUid) + .filterNotNull() + .first() + .toUiState(true) + } + } + } + + fun updateUiState(tankDetails: TankDetails) { + tankUiState = TankUiState( + tankDetails = tankDetails, + isEntryValid = validateInput(tankDetails) + ) + } + + suspend fun saveTank() { + if (validateInput()) { + if (tankUid > 0) { + tankRepository.updateTank(tankUiState.tankDetails.toTank(tankUid)) + } else { + tankRepository.insertTank(tankUiState.tankDetails.toTank()) + } + } + } + + private fun validateInput(uiState: TankDetails = tankUiState.tankDetails): Boolean { + return with(uiState) { + name.isNotBlank() + && price > 0 + && image > 0 + && levelId!! > 0 + && nationId!! > 0 + } + } +} + +data class TankUiState( + val tankDetails: TankDetails = TankDetails(), + val isEntryValid: Boolean = false +) + +data class TankDetails( + val name: String = "", + val price: Int = 0, + val image: Int = 0, + val levelId: Long? = 0, + val nationId: Long? = 0, +) + +fun TankDetails.toTank(uid: Long = 0): Tank = Tank( + tankId = uid, + name = name, + price = price, + image = image, + levelId = levelId, + nationId = levelId +) + +fun Tank.toDetails(): TankDetails = TankDetails( + name = name, + price = price, + image = image, + levelId = levelId, + nationId = nationId +) + +fun Tank.toUiState(isEntryValid: Boolean = false): TankUiState = TankUiState( + tankDetails = this.toDetails(), + isEntryValid = isEntryValid +) \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Level.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Level.kt index 6f3278d..581bba9 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Level.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Level.kt @@ -27,6 +27,13 @@ data class Level( return true } + companion object { + val DEMO_LEVEL = Level( + 0, + 1 + ) + } + override fun hashCode(): Int { return (uid ?: -1) as Int } diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Nation.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Nation.kt index 73fb1c7..4b46b2b 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Nation.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Nation.kt @@ -28,6 +28,13 @@ data class Nation( override fun hashCode(): Int { return (uid ?: -1) as Int } + + companion object { + val DEMO_NATION = Nation( + 0, + "СССР" + ) + } } fun getNations(): List { diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Tank.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Tank.kt index d89b61b..65156de 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Tank.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/Tank.kt @@ -4,6 +4,7 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.Ignore import androidx.room.PrimaryKey +import ru.ulstu.`is`.pmu.R @Entity( tableName = "tanks" @@ -31,6 +32,19 @@ data class Tank( nation: Nation ) : this(null, name, price, image, level.uid, nation.uid) + companion object { + fun getTank(index: Long = 0): Tank { + return Tank( + index, + "Первый танк", + 100000, + R.drawable.t_34_85, + 1, + 1 + ) + } + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Constructor.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Constructor.kt index b607968..7da8537 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Constructor.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Constructor.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -33,25 +34,78 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController +import kotlinx.coroutines.launch import ru.ulstu.`is`.pmu.R import ru.ulstu.`is`.pmu.composeui.navigation.Screen +import ru.ulstu.`is`.pmu.tank.composeui.edit.LevelDropDownViewModel +import ru.ulstu.`is`.pmu.tank.composeui.edit.LevelUiState +import ru.ulstu.`is`.pmu.tank.composeui.edit.LevelsListUiState +import ru.ulstu.`is`.pmu.tank.composeui.edit.NationDropDownViewModel +import ru.ulstu.`is`.pmu.tank.composeui.edit.NationUiState +import ru.ulstu.`is`.pmu.tank.composeui.edit.NationsListUiState +import ru.ulstu.`is`.pmu.tank.composeui.edit.TankDetails +import ru.ulstu.`is`.pmu.tank.composeui.edit.TankEditViewModel +import ru.ulstu.`is`.pmu.tank.composeui.edit.TankUiState +import ru.ulstu.`is`.pmu.tank.composeui.edit.toUiState +import ru.ulstu.`is`.pmu.tank.composeui.list.TankListViewModel +import ru.ulstu.`is`.pmu.tank.model.Level +import ru.ulstu.`is`.pmu.tank.model.Nation +import ru.ulstu.`is`.pmu.tank.model.Tank import ru.ulstu.`is`.pmu.tank.model.getLevels import ru.ulstu.`is`.pmu.tank.model.getNations +import ru.ulstu.`is`.pmu.ui.AppViewModelProvider import ru.ulstu.`is`.pmu.ui.theme.CustomDark import ru.ulstu.`is`.pmu.ui.theme.CustomOrange import ru.ulstu.`is`.pmu.ui.theme.CustomYellow import ru.ulstu.`is`.pmu.ui.theme.PmudemoTheme +@Composable +fun Constructor( + navController: NavController, + tankEditViewModel: TankEditViewModel = viewModel(factory = AppViewModelProvider.Factory), + levelDropDownViewModel: LevelDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory), + nationDropDownViewModel: NationDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory) +){ + val coroutineScope = rememberCoroutineScope() + + levelDropDownViewModel.setCurrentLevel(tankEditViewModel.tankUiState.tankDetails.levelId ?: 1) + nationDropDownViewModel.setCurrentNation(tankEditViewModel.tankUiState.tankDetails.nationId ?: 1) + + Constructor( + tankUiState = tankEditViewModel.tankUiState, + levelUiState = levelDropDownViewModel.levelUiState, + levelsListUiState = levelDropDownViewModel.levelsListUiState, + onLevelUpdate = levelDropDownViewModel::updateUiState, + nationUiState = nationDropDownViewModel.nationUiState, + nationsListUiState = nationDropDownViewModel.nationsListUiState, + onNationUpdate = nationDropDownViewModel::updateUiState, + onClick = { + coroutineScope.launch { + tankEditViewModel.saveTank() + navController.popBackStack() + } + }, + onUpdate = tankEditViewModel::updateUiState + ) +} + @OptIn(ExperimentalMaterial3Api::class, ExperimentalTextApi::class) @Composable -fun Constructor(navController: NavController) { - val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentDestination = navBackStackEntry?.destination - val currentScreen = currentDestination?.route?.let { Screen.getItem(it) } - +private fun Constructor( + tankUiState: TankUiState, + levelUiState: LevelUiState, + levelsListUiState: LevelsListUiState, + onLevelUpdate: (Level) -> Unit, + nationUiState: NationUiState, + nationsListUiState: NationsListUiState, + onNationUpdate: (Nation) -> Unit, + onClick: () -> Unit, + onUpdate: (TankDetails) -> Unit +) { var nationName by remember { mutableStateOf("") } var price by remember { mutableStateOf("") } @@ -69,7 +123,9 @@ fun Constructor(navController: NavController) { ) { Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth().background(CustomYellow) + modifier = Modifier + .fillMaxWidth() + .background(CustomYellow) ){ Column { Row( @@ -98,7 +154,7 @@ fun Constructor(navController: NavController) { TextField( value = nationName, placeholder = { Text(text = "Название", color = CustomDark) }, - onValueChange = { nationName = it }, + onValueChange = { onUpdate(tankUiState.tankDetails.copy(name = it)) }, modifier = Modifier .width(200.dp), ) @@ -108,7 +164,7 @@ fun Constructor(navController: NavController) { expanded = expandedLevels, onExpandedChange = { expandedLevels = !expandedLevels - } + }, ) { // textfield TextField( @@ -136,6 +192,7 @@ fun Constructor(navController: NavController) { text = { Text(selectionOption.level.toString()) }, onClick = { selectedLevel = selectionOption.level + onLevelUpdate(selectionOption) expandedLevels = false }, contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, @@ -177,6 +234,7 @@ fun Constructor(navController: NavController) { text = { Text(selectionOption.nationName) }, onClick = { selectedNation = selectionOption.nationName + onNationUpdate(selectionOption) expandedNation = false }, contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, @@ -188,7 +246,7 @@ fun Constructor(navController: NavController) { TextField( value = price, placeholder = { Text(text = "Стоимость", color = CustomDark) }, - onValueChange = { price = it }, + onValueChange = { onUpdate(tankUiState.tankDetails.copy(price = it.toInt())) }, modifier = Modifier .width(200.dp), ) @@ -196,7 +254,9 @@ fun Constructor(navController: NavController) { } Row( horizontalArrangement = Arrangement.Center, - modifier = Modifier.fillMaxWidth().padding(10.dp, 10.dp) + modifier = Modifier + .fillMaxWidth() + .padding(10.dp, 10.dp) ){ Button( modifier = Modifier @@ -208,14 +268,16 @@ fun Constructor(navController: NavController) { ), onClick = { }) { //"${student.firstName} ${student.lastName}" - Text(text = stringResource(id = R.string.create_account_button), fontSize = 20.sp, fontWeight = FontWeight.Bold) + Text(text = stringResource(id = R.string.save_account_button), fontSize = 20.sp, fontWeight = FontWeight.Bold) } } } } Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth().background(CustomYellow) + modifier = Modifier + .fillMaxWidth() + .background(CustomYellow) ){ Column { Row( @@ -246,7 +308,9 @@ fun Constructor(navController: NavController) { } Row( horizontalArrangement = Arrangement.Center, - modifier = Modifier.fillMaxWidth().padding(10.dp, 10.dp) + modifier = Modifier + .fillMaxWidth() + .padding(10.dp, 10.dp) ){ Button( modifier = Modifier @@ -265,7 +329,9 @@ fun Constructor(navController: NavController) { } Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth().background(CustomYellow) + modifier = Modifier + .fillMaxWidth() + .background(CustomYellow) ){ Column { Row( @@ -296,7 +362,9 @@ fun Constructor(navController: NavController) { } Row( horizontalArrangement = Arrangement.Center, - modifier = Modifier.fillMaxWidth().padding(10.dp, 10.dp) + modifier = Modifier + .fillMaxWidth() + .padding(10.dp, 10.dp) ){ Button( modifier = Modifier @@ -320,13 +388,23 @@ fun Constructor(navController: NavController) { @Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO) @Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -fun ConstructorPreview() { +fun ConstructorEditPreview() { PmudemoTheme { Surface( color = CustomDark ) { val navController = rememberNavController() - Constructor(navController) + Constructor( + tankUiState = Tank.getTank().toUiState(true), + levelUiState = Level.DEMO_LEVEL.toUiState(), + levelsListUiState = LevelsListUiState(listOf()), + onLevelUpdate = { }, + nationUiState = Nation.DEMO_NATION.toUiState(), + nationsListUiState = NationsListUiState(listOf()), + onNationUpdate = { }, + onClick = { }, + onUpdate = { }, + ) } } } \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Hangar.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Hangar.kt index 1d0e471..987b43d 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Hangar.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Hangar.kt @@ -170,15 +170,12 @@ private fun Hangar( } } } - index++ } } Spacer(Modifier.height(20.dp)) } } - - } } @@ -190,8 +187,20 @@ fun HangarPreview() { Surface( color = CustomDark ) { - val navController = rememberNavController() - Hangar(navController) + Hangar(tankList = listOf()) + } + } +} + +@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun HangarEmptyPreview() { + PmudemoTheme { + Surface( + color = CustomDark + ) { + Hangar(tankList = listOf()) } } } \ No newline at end of file