diff --git a/app/src/main/java/com/example/livingai/di/AppModule.kt b/app/src/main/java/com/example/livingai/di/AppModule.kt index 80bce15..d727f7c 100644 --- a/app/src/main/java/com/example/livingai/di/AppModule.kt +++ b/app/src/main/java/com/example/livingai/di/AppModule.kt @@ -181,7 +181,7 @@ val appModule = module { viewModel { HomeViewModel(get()) } viewModel { OnBoardingViewModel(get()) } viewModel { (savedStateHandle: androidx.lifecycle.SavedStateHandle?) -> - AddProfileViewModel(get(), get(), androidContext(), savedStateHandle) + AddProfileViewModel(get(), get(), savedStateHandle) } viewModel { ListingsViewModel(get()) } viewModel { SettingsViewModel(get()) } diff --git a/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileScreen.kt b/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileScreen.kt index 12f9f3c..cb82600 100644 --- a/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileScreen.kt +++ b/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileScreen.kt @@ -47,22 +47,12 @@ import com.example.livingai.utils.Constants fun AddProfileScreen( navController: NavController, viewModel: AddProfileViewModel, - animalId: String?, - loadEntry: Boolean, onSave: () -> Unit, onCancel: () -> Unit, onTakePhoto: (String) -> Unit, onTakeVideo: () -> Unit ) { val context = LocalContext.current - - LaunchedEffect(animalId, loadEntry) { - if (loadEntry && animalId != null) { - viewModel.loadAnimal(animalId) - } else { - viewModel.initializeNewProfile() - } - } val speciesList = stringArrayResource(id = R.array.species_list).toList() val breedList = stringArrayResource(id = R.array.cow_breed_list).toList() diff --git a/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileViewModel.kt b/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileViewModel.kt index 240e23f..9c11aca 100644 --- a/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileViewModel.kt +++ b/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileViewModel.kt @@ -1,8 +1,5 @@ package com.example.livingai.pages.addprofile -import android.content.Context -import android.net.Uri -import android.provider.OpenableColumns import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf @@ -10,9 +7,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.livingai.domain.model.AnimalDetails -import com.example.livingai.domain.usecases.GetAnimalDetails import com.example.livingai.domain.usecases.ProfileEntry.ProfileEntryUseCase -import com.example.livingai.utils.Constants import com.example.livingai.utils.CoroutineDispatchers import com.example.livingai.utils.IdGenerator import kotlinx.coroutines.flow.launchIn @@ -23,17 +18,19 @@ import kotlinx.coroutines.withContext class AddProfileViewModel( private val profileEntryUseCase: ProfileEntryUseCase, private val dispatchers: CoroutineDispatchers, - private val context: Context, - val savedStateHandle: SavedStateHandle? = null + private val savedStateHandle: SavedStateHandle? = null ) : ViewModel() { - - private val _animalDetails = mutableStateOf(null) - val animalDetails: State = _animalDetails - private val _currentAnimalId = mutableStateOf(null) + companion object { + private const val KEY_ANIMAL_ID = "animal_id" + } + + private val _animalDetails = mutableStateOf(null) + + private val _currentAnimalId = + mutableStateOf(savedStateHandle?.get(KEY_ANIMAL_ID)) val currentAnimalId: State = _currentAnimalId - - // UI State + var species = mutableStateOf(null) var breed = mutableStateOf(null) var age = mutableStateOf("") @@ -42,57 +39,102 @@ class AddProfileViewModel( var reproductiveStatus = mutableStateOf(null) var description = mutableStateOf("") - // Errors var ageError = mutableStateOf(null) var milkYieldError = mutableStateOf(null) var calvingNumberError = mutableStateOf(null) var speciesError = mutableStateOf(null) var breedError = mutableStateOf(null) var reproductiveStatusError = mutableStateOf(null) - - // Save state + private val _saveSuccess = mutableStateOf(false) - val saveSuccess: State = _saveSuccess - - // State for photos and video + val photos = mutableStateMapOf() val segmentedImages = mutableStateMapOf() private val _videoUri = mutableStateOf(null) val videoUri: State = _videoUri + fun initializeNewProfileIfNeeded() { + if (_currentAnimalId.value != null) return + + val id = IdGenerator.generateAnimalId() + _currentAnimalId.value = id + savedStateHandle?.let { it[KEY_ANIMAL_ID] = id } + + _animalDetails.value = null + species.value = null + breed.value = null + age.value = "" + milkYield.value = "" + calvingNumber.value = "" + reproductiveStatus.value = null + description.value = "" + clearErrors() + photos.clear() + segmentedImages.clear() + _videoUri.value = null + } + fun loadAnimal(animalId: String) { - if (animalId == _currentAnimalId.value) return + if (_currentAnimalId.value == animalId) return _currentAnimalId.value = animalId + savedStateHandle?.set(KEY_ANIMAL_ID, animalId) + + profileEntryUseCase.getAnimalDetails(animalId) + .onEach { details -> + details ?: return@onEach - profileEntryUseCase.getAnimalDetails(animalId).onEach { details -> - if (details != null) { _animalDetails.value = details - - // Populate UI State species.value = details.species.ifBlank { null } breed.value = details.breed.ifBlank { null } - age.value = if (details.age == 0) "" else details.age.toString() - milkYield.value = if (details.milkYield == 0) "" else details.milkYield.toString() - calvingNumber.value = if (details.calvingNumber == 0) "" else details.calvingNumber.toString() + age.value = details.age.takeIf { it > 0 }?.toString() ?: "" + milkYield.value = details.milkYield.takeIf { it > 0 }?.toString() ?: "" + calvingNumber.value = details.calvingNumber.takeIf { it > 0 }?.toString() ?: "" reproductiveStatus.value = details.reproductiveStatus.ifBlank { null } description.value = details.description clearErrors() - + photos.clear() segmentedImages.clear() - + withContext(dispatchers.main) { - details.images.entries.forEach { (orientation, path) -> - photos[orientation] = path - } - details.segmentedImages.entries.forEach { (orientation, path) -> - segmentedImages[orientation] = path - } + details.images.forEach { photos[it.key] = it.value } + details.segmentedImages.forEach { segmentedImages[it.key] = it.value } } + _videoUri.value = details.video.ifBlank { null } } - }.launchIn(viewModelScope) + .launchIn(viewModelScope) + } + + fun saveAnimalDetails(): Boolean { + if (!validateInputs()) return false + + val id = _currentAnimalId.value ?: return false + + val details = AnimalDetails( + animalId = id, + species = species.value ?: "", + breed = breed.value ?: "", + age = age.value.toIntOrNull() ?: 0, + milkYield = milkYield.value.toIntOrNull() ?: 0, + calvingNumber = calvingNumber.value.toIntOrNull() ?: 0, + reproductiveStatus = reproductiveStatus.value ?: "", + description = description.value, + images = photos, + video = _videoUri.value ?: "", + segmentedImages = segmentedImages, + name = "", + sex = "", + weight = 0 + ) + + viewModelScope.launch { + profileEntryUseCase.setAnimalDetails(details) + _saveSuccess.value = true + } + + return true } private fun clearErrors() { @@ -111,11 +153,24 @@ class AddProfileViewModel( fun addSegmentedImage(orientation: String, uri: String) { segmentedImages[orientation] = uri } - + fun setVideo(uri: String) { _videoUri.value = uri } + private fun validateInputs(): Boolean { + var isValid = true + + if (!validateSpeciesInputs()) isValid = false + if (!validateBreedInputs()) isValid = false + if (!validateAgeInputs()) isValid = false + if (!validateMilkYieldInputs()) isValid = false + if (!validateCalvingInputs()) isValid = false + if (!validateReproductiveStatusInputs()) isValid = false + + return isValid + } + fun validateSpeciesInputs(s: String? = null): Boolean { if (s != null) species.value = s @@ -201,72 +256,15 @@ class AddProfileViewModel( calvingNumber.value = c var isValid = true - + val calvingInt = calvingNumber.value.toIntOrNull() if (calvingInt == null || calvingInt < 0 || calvingInt > 12) { calvingNumberError.value = "Invalid calving number" isValid = false } else { - calvingNumberError.value = null + calvingNumberError.value = null } - + return isValid } - - private fun validateInputs(): Boolean { - return validateSpeciesInputs() && validateBreedInputs() && validateAgeInputs() - && validateMilkYieldInputs() && validateCalvingInputs() && validateReproductiveStatusInputs() - } - - fun saveAnimalDetails(): Boolean { - if (!validateInputs()) return false - - val id = _currentAnimalId.value ?: IdGenerator.generateAnimalId().also { _currentAnimalId.value = it } - - val details = AnimalDetails( - animalId = id, - species = species.value ?: "", - breed = breed.value ?: "", - age = age.value.toIntOrNull() ?: 0, - milkYield = milkYield.value.toIntOrNull() ?: 0, - calvingNumber = calvingNumber.value.toIntOrNull() ?: 0, - reproductiveStatus = reproductiveStatus.value ?: "", - description = description.value, - images = photos, - video = _videoUri.value ?: "", - segmentedImages = segmentedImages, - name = "", sex = "", weight = 0 - ) - - viewModelScope.launch { - profileEntryUseCase.setAnimalDetails(details) - _saveSuccess.value = true - } - - return true - } - - fun onSaveComplete() { - _saveSuccess.value = false - } - - fun initializeNewProfile() { - val newId = IdGenerator.generateAnimalId() - _currentAnimalId.value = newId - _animalDetails.value = null - - // Reset UI State - species.value = null - breed.value = null - age.value = "" - milkYield.value = "" - calvingNumber.value = "" - reproductiveStatus.value = null - description.value = "" - clearErrors() - - photos.clear() - segmentedImages.clear() - _videoUri.value = null - } }