feat: add custom snackbar and UI events for control feature
This commit is contained in:
parent
0f65748195
commit
1ef33bcfda
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.syaroful.agrilinkvocpro.core.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Snackbar
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CustomSnackBarComponent(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
message: String,
|
||||||
|
icon: ImageVector
|
||||||
|
) {
|
||||||
|
Snackbar(
|
||||||
|
modifier = modifier,
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
) {
|
||||||
|
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
|
Text(message)
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = "Check Icon",
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun CustomSnackBarPreview() {
|
||||||
|
CustomSnackBarComponent(
|
||||||
|
message = "Actuator State Changed",
|
||||||
|
icon = Icons.Filled.Check
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.syaroful.agrilinkvocpro.control_feature.core.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Snackbar
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CustomSnackBar(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
message: String,
|
||||||
|
icon: ImageVector
|
||||||
|
) {
|
||||||
|
Snackbar(
|
||||||
|
modifier = modifier,
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
) {
|
||||||
|
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
|
Text(message)
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = "Check Icon",
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun CustomSnackBarPreview() {
|
||||||
|
CustomSnackBar(
|
||||||
|
message = "Actuator State Changed",
|
||||||
|
icon = Icons.Filled.Check
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.syaroful.agrilinkvocpro.control_feature.presentation.control
|
||||||
|
|
||||||
|
sealed class ControlUiEvent {
|
||||||
|
data class Success(val message: String) : ControlUiEvent()
|
||||||
|
data class Error(val message: String) : ControlUiEvent()
|
||||||
|
}
|
||||||
|
|
@ -9,8 +9,10 @@ import com.syaroful.agrilinkvocpro.control_feature.data.model.ActuatorType
|
||||||
import com.syaroful.agrilinkvocpro.control_feature.data.repository.ControlRepository
|
import com.syaroful.agrilinkvocpro.control_feature.data.repository.ControlRepository
|
||||||
import com.syaroful.agrilinkvocpro.data.UserPreferences
|
import com.syaroful.agrilinkvocpro.data.UserPreferences
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
@ -36,6 +38,13 @@ class ControlViewModel(
|
||||||
private val _isLoading = MutableStateFlow<Boolean>(false)
|
private val _isLoading = MutableStateFlow<Boolean>(false)
|
||||||
val isLoading: StateFlow<Boolean> = _isLoading
|
val isLoading: StateFlow<Boolean> = _isLoading
|
||||||
|
|
||||||
|
private val _uiEvent = MutableSharedFlow<ControlUiEvent>()
|
||||||
|
val uiEvent = _uiEvent.asSharedFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
getActuatorStatus()
|
||||||
|
}
|
||||||
|
|
||||||
fun getActuatorStatus() {
|
fun getActuatorStatus() {
|
||||||
_allActuatorState.value = ControlState.Loading
|
_allActuatorState.value = ControlState.Loading
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|
@ -55,6 +64,7 @@ class ControlViewModel(
|
||||||
ActuatorType.PUMP.displayName -> _pumpStatus.value = isOn
|
ActuatorType.PUMP.displayName -> _pumpStatus.value = isOn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Log.d(TAG, "Successfully get Actuator Status ${response.body()}")
|
||||||
} else {
|
} else {
|
||||||
val errorBody = response.errorBody()?.string()
|
val errorBody = response.errorBody()?.string()
|
||||||
val errorMessage = errorBody ?: "Error ${response.code()}"
|
val errorMessage = errorBody ?: "Error ${response.code()}"
|
||||||
|
|
@ -78,7 +88,11 @@ class ControlViewModel(
|
||||||
|
|
||||||
val response = when (actuator) {
|
val response = when (actuator) {
|
||||||
ActuatorType.WATER -> repository.controlWaterValve(authHeader, stringState)
|
ActuatorType.WATER -> repository.controlWaterValve(authHeader, stringState)
|
||||||
ActuatorType.NUTRIENT -> repository.controlNutrientValve(authHeader, stringState)
|
ActuatorType.NUTRIENT -> repository.controlNutrientValve(
|
||||||
|
authHeader,
|
||||||
|
stringState
|
||||||
|
)
|
||||||
|
|
||||||
ActuatorType.PUMP -> repository.controlPump(authHeader, stringState)
|
ActuatorType.PUMP -> repository.controlPump(authHeader, stringState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,12 +100,16 @@ class ControlViewModel(
|
||||||
val isOn = response.body()?.log?.action == "ON"
|
val isOn = response.body()?.log?.action == "ON"
|
||||||
updateActuatorStatus(actuator, isOn)
|
updateActuatorStatus(actuator, isOn)
|
||||||
Log.d(TAG, "Successfully controlled ${actuator.displayName}")
|
Log.d(TAG, "Successfully controlled ${actuator.displayName}")
|
||||||
|
Log.d(TAG, "Successfully controlled ${response.body()}")
|
||||||
|
_uiEvent.emit(ControlUiEvent.Success("${actuator.displayName} turned ${if (isOn) "ON" else "OFF"}"))
|
||||||
} else {
|
} else {
|
||||||
val errorBody = response.errorBody()?.string()
|
val errorBody = response.errorBody()?.string()
|
||||||
Log.e(TAG, "Error controlling ${actuator.displayName}: $errorBody")
|
Log.e(TAG, "Error controlling ${actuator.displayName}: $errorBody")
|
||||||
|
_uiEvent.emit(ControlUiEvent.Error("Failed to control ${actuator.displayName}: $errorBody"))
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Exception while controlling ${actuator.displayName}: ${e.message}")
|
Log.e(TAG, "Exception while controlling ${actuator.displayName}: ${e.message}")
|
||||||
|
_uiEvent.emit(ControlUiEvent.Error("Exception controlling ${actuator.displayName}: ${e.localizedMessage}"))
|
||||||
} finally {
|
} finally {
|
||||||
_isLoading.value = false
|
_isLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user