From 5899b77556072642f992265f08d2646febad5da8 Mon Sep 17 00:00:00 2001 From: Cutiful <113351087+Syaroful@users.noreply.github.com> Date: Sat, 5 Jul 2025 11:23:49 +0700 Subject: [PATCH] feat: implement snackbar for control actuator --- .../control/ControlActuatorScreen.kt | 70 +++++++++++++------ 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/agrilinkvocpro/control_feature/src/main/java/com/syaroful/agrilinkvocpro/control_feature/presentation/control/ControlActuatorScreen.kt b/agrilinkvocpro/control_feature/src/main/java/com/syaroful/agrilinkvocpro/control_feature/presentation/control/ControlActuatorScreen.kt index 8ddcaae..c56f4c8 100644 --- a/agrilinkvocpro/control_feature/src/main/java/com/syaroful/agrilinkvocpro/control_feature/presentation/control/ControlActuatorScreen.kt +++ b/agrilinkvocpro/control_feature/src/main/java/com/syaroful/agrilinkvocpro/control_feature/presentation/control/ControlActuatorScreen.kt @@ -3,19 +3,23 @@ package com.syaroful.agrilinkvocpro.control_feature.presentation.control import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.Info +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.pulltorefresh.PullToRefreshBox @@ -27,18 +31,23 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import androidx.navigation.NavController import com.syaroful.agrilinkvocpro.control_feature.R import com.syaroful.agrilinkvocpro.control_feature.core.components.ControlCard +import com.syaroful.agrilinkvocpro.control_feature.core.components.CustomSnackBar import com.syaroful.agrilinkvocpro.control_feature.core.components.DisableControlCard import com.syaroful.agrilinkvocpro.control_feature.core.state.ControlState import com.syaroful.agrilinkvocpro.control_feature.data.model.ActuatorType import com.syaroful.agrilinkvocpro.core.placeholder.shimmerEffect +import kotlinx.coroutines.flow.collectLatest @OptIn(ExperimentalMaterial3Api::class) @Composable fun ControlActuatorScreen( - viewModel: ControlViewModel + viewModel: ControlViewModel, + navController: NavController ) { val allActuators by viewModel.allActuatorState.collectAsState() val waterValveStatus by viewModel.waterValveStatus.collectAsState() @@ -48,11 +57,27 @@ fun ControlActuatorScreen( val isRefreshing = remember { mutableStateOf(false) } + val snackBarHostState = remember { SnackbarHostState() } LaunchedEffect(Unit) { - viewModel.getActuatorStatus() + viewModel.uiEvent.collectLatest { event -> + val (msg, color) = when (event) { + is ControlUiEvent.Success -> event.message to Color(0xFF4CAF50) + is ControlUiEvent.Error -> event.message to Color(0xFFF44336) + } + snackBarHostState.showSnackbar(message = msg) + } } Scaffold( + snackbarHost = { + SnackbarHost(hostState = snackBarHostState) { data -> + CustomSnackBar( + modifier = Modifier.padding(16.dp), + message = data.visuals.message, + icon = if (data.visuals.message.contains("Failed")) Icons.Default.Clear else Icons.Default.Check + ) + } + }, topBar = { TopAppBar( title = { @@ -63,19 +88,18 @@ fun ControlActuatorScreen( Text("Control Actuator", style = MaterialTheme.typography.titleMedium) } }, - navigationIcon = { - IconButton(onClick = { }) { - Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") - } - }, + actions = { - IconButton(onClick = { }) { - Icon(Icons.Filled.Info, contentDescription = "History") + IconButton(onClick = { + navController.navigate("control_history") + }) { + Icon(Icons.Filled.Refresh, contentDescription = "History") } } ) } ) { innerPadding -> + PullToRefreshBox( isRefreshing = isRefreshing.value, onRefresh = { @@ -89,15 +113,11 @@ fun ControlActuatorScreen( .padding(innerPadding) .padding(16.dp) .verticalScroll(rememberScrollState()), - verticalArrangement = Arrangement.spacedBy(16.dp) + verticalArrangement = Arrangement.spacedBy(8.dp) ) { Text("Daftar Actuator", style = MaterialTheme.typography.titleMedium) + Spacer(modifier = Modifier.height(1.dp)) - - // ControlGrid( - // iconRes = R.drawable.ic_valve, - // items = List(4) { index -> "Bet ${index + 1}" } - // ) when (allActuators) { is ControlState.Loading -> { isRefreshing.value = false @@ -128,14 +148,24 @@ fun ControlActuatorScreen( label = "Water Valve", isOn = waterValveStatus, isLoading = isLoading, - onToggle = { viewModel.controlActuator(ActuatorType.WATER, !waterValveStatus) } + onToggle = { + viewModel.controlActuator( + ActuatorType.WATER, + !waterValveStatus + ) + } ) ControlCard( iconRes = R.drawable.ic_leaf, label = "Nutrient Valve", isOn = nutritionValveStatus, isLoading = isLoading, - onToggle = { viewModel.controlActuator(ActuatorType.NUTRIENT, !nutritionValveStatus) } + onToggle = { + viewModel.controlActuator( + ActuatorType.NUTRIENT, + !nutritionValveStatus + ) + } ) ControlCard( iconRes = R.drawable.ic_valve, @@ -147,8 +177,6 @@ fun ControlActuatorScreen( } } - - } } }