Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ea633a329 |
@@ -85,13 +85,13 @@ fun ABoxManagerApp(
|
|||||||
onSocketClick = {
|
onSocketClick = {
|
||||||
navController.navigateToSocket(
|
navController.navigateToSocket(
|
||||||
profileId = state.profile.id,
|
profileId = state.profile.id,
|
||||||
deviceName = DeviceName("智能插座"),
|
deviceName = DeviceName("智能插座 1"),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onTemperatureHumidityClick = {
|
onTemperatureHumidityClick = {
|
||||||
navController.navigateToTemperatureHumidity(
|
navController.navigateToTemperatureHumidity(
|
||||||
profileId = state.profile.id,
|
profileId = state.profile.id,
|
||||||
deviceName = DeviceName("温湿度传感器"),
|
deviceName = DeviceName("温湿度传感器 1"),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
+2
-7
@@ -9,21 +9,16 @@ import com.flagship.abox.manager.domain.DeviceName
|
|||||||
import com.flagship.abox.manager.domain.ProviderProfileId
|
import com.flagship.abox.manager.domain.ProviderProfileId
|
||||||
import com.flagship.abox.manager.ui.socket.SocketRoute
|
import com.flagship.abox.manager.ui.socket.SocketRoute
|
||||||
import com.flagship.abox.manager.ui.temperaturehumidity.TemperatureHumidityRoute
|
import com.flagship.abox.manager.ui.temperaturehumidity.TemperatureHumidityRoute
|
||||||
import java.net.URLEncoder
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
object DeviceDetailRoutes {
|
object DeviceDetailRoutes {
|
||||||
const val SOCKET_PATTERN = "device/socket/{profileId}/{deviceName}"
|
const val SOCKET_PATTERN = "device/socket/{profileId}/{deviceName}"
|
||||||
const val TH_PATTERN = "device/th/{profileId}/{deviceName}"
|
const val TH_PATTERN = "device/th/{profileId}/{deviceName}"
|
||||||
|
|
||||||
fun socketRoute(profileId: ProviderProfileId, deviceName: DeviceName): String =
|
fun socketRoute(profileId: ProviderProfileId, deviceName: DeviceName): String =
|
||||||
"device/socket/${encode(profileId.value)}/${encode(deviceName.value)}"
|
"device/socket/${profileId.value}/${deviceName.value}"
|
||||||
|
|
||||||
fun temperatureHumidityRoute(profileId: ProviderProfileId, deviceName: DeviceName): String =
|
fun temperatureHumidityRoute(profileId: ProviderProfileId, deviceName: DeviceName): String =
|
||||||
"device/th/${encode(profileId.value)}/${encode(deviceName.value)}"
|
"device/th/${profileId.value}/${deviceName.value}"
|
||||||
|
|
||||||
private fun encode(value: String): String =
|
|
||||||
URLEncoder.encode(value, StandardCharsets.UTF_8.name())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NavController.navigateToSocket(profileId: ProviderProfileId, deviceName: DeviceName) {
|
fun NavController.navigateToSocket(profileId: ProviderProfileId, deviceName: DeviceName) {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.width
|
|||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@@ -24,16 +23,12 @@ import androidx.compose.material3.Icon
|
|||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.OutlinedTextField
|
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
@@ -48,7 +43,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import com.flagship.abox.manager.R
|
import com.flagship.abox.manager.R
|
||||||
import com.flagship.abox.manager.designsystem.ABoxButton
|
import com.flagship.abox.manager.designsystem.ABoxButton
|
||||||
import com.flagship.abox.manager.designsystem.ABoxLoadingIndicator
|
import com.flagship.abox.manager.designsystem.ABoxLoadingIndicator
|
||||||
import com.flagship.abox.manager.domain.DeviceName
|
|
||||||
import com.flagship.abox.manager.domain.SocketPowerState
|
import com.flagship.abox.manager.domain.SocketPowerState
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
@@ -79,7 +73,6 @@ fun SocketScreen(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
var showEditNameDialog by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
@@ -97,12 +90,6 @@ fun SocketScreen(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(
|
|
||||||
onClick = { showEditNameDialog = true },
|
|
||||||
enabled = !state.isLoading && !state.isOperating,
|
|
||||||
) {
|
|
||||||
Text("✎", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
|
||||||
}
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { onEvent(SocketUiEvent.Refresh) },
|
onClick = { onEvent(SocketUiEvent.Refresh) },
|
||||||
enabled = !state.isLoading && !state.isOperating,
|
enabled = !state.isLoading && !state.isOperating,
|
||||||
@@ -247,42 +234,6 @@ fun SocketScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showEditNameDialog) {
|
|
||||||
var inputName by remember(state.deviceName) { mutableStateOf(state.deviceName?.value.orEmpty()) }
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = { showEditNameDialog = false },
|
|
||||||
title = { Text(stringResource(R.string.dialog_edit_device_name_title)) },
|
|
||||||
text = {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = inputName,
|
|
||||||
onValueChange = { inputName = it },
|
|
||||||
label = { Text(stringResource(R.string.dialog_edit_device_name_hint)) },
|
|
||||||
singleLine = true,
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
val trimmed = inputName.trim()
|
|
||||||
if (trimmed.isNotBlank()) {
|
|
||||||
onEvent(SocketUiEvent.ChangeDeviceName(DeviceName(trimmed)))
|
|
||||||
}
|
|
||||||
showEditNameDialog = false
|
|
||||||
},
|
|
||||||
enabled = inputName.isNotBlank(),
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.action_confirm))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = { showEditNameDialog = false }) {
|
|
||||||
Text(stringResource(R.string.action_cancel))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ data class SocketUiState(
|
|||||||
|
|
||||||
sealed interface SocketUiEvent {
|
sealed interface SocketUiEvent {
|
||||||
data class Initialize(val profileId: ProviderProfileId, val deviceName: DeviceName) : SocketUiEvent
|
data class Initialize(val profileId: ProviderProfileId, val deviceName: DeviceName) : SocketUiEvent
|
||||||
data class ChangeDeviceName(val deviceName: DeviceName) : SocketUiEvent
|
|
||||||
data object Refresh : SocketUiEvent
|
data object Refresh : SocketUiEvent
|
||||||
data object TogglePower : SocketUiEvent
|
data object TogglePower : SocketUiEvent
|
||||||
data class SetPower(val targetState: SocketPowerState) : SocketUiEvent
|
data class SetPower(val targetState: SocketPowerState) : SocketUiEvent
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.net.URLDecoder
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@@ -30,8 +28,8 @@ class SocketViewModel @Inject constructor(
|
|||||||
val state: StateFlow<SocketUiState> = _state.asStateFlow()
|
val state: StateFlow<SocketUiState> = _state.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val profileIdString: String? = savedStateHandle.get<String>("profileId")?.let { decode(it) }
|
val profileIdString: String? = savedStateHandle["profileId"]
|
||||||
val deviceNameString: String? = savedStateHandle.get<String>("deviceName")?.let { decode(it) }
|
val deviceNameString: String? = savedStateHandle["deviceName"]
|
||||||
|
|
||||||
if (!profileIdString.isNullOrBlank() && !deviceNameString.isNullOrBlank()) {
|
if (!profileIdString.isNullOrBlank() && !deviceNameString.isNullOrBlank()) {
|
||||||
onEvent(
|
onEvent(
|
||||||
@@ -43,12 +41,6 @@ class SocketViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decode(value: String): String = try {
|
|
||||||
URLDecoder.decode(value, StandardCharsets.UTF_8.name())
|
|
||||||
} catch (_: Exception) {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onEvent(event: SocketUiEvent) {
|
fun onEvent(event: SocketUiEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is SocketUiEvent.Initialize -> {
|
is SocketUiEvent.Initialize -> {
|
||||||
@@ -61,21 +53,6 @@ class SocketViewModel @Inject constructor(
|
|||||||
loadStatus()
|
loadStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
is SocketUiEvent.ChangeDeviceName -> {
|
|
||||||
val trimmed = event.deviceName.value.trim()
|
|
||||||
if (trimmed.isNotBlank() && trimmed != _state.value.deviceName?.value) {
|
|
||||||
_state.update {
|
|
||||||
it.copy(
|
|
||||||
deviceName = DeviceName(trimmed),
|
|
||||||
powerState = null,
|
|
||||||
errorMessage = null,
|
|
||||||
lastConfirmedTimeMillis = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
loadStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketUiEvent.Refresh -> {
|
SocketUiEvent.Refresh -> {
|
||||||
loadStatus()
|
loadStatus()
|
||||||
}
|
}
|
||||||
|
|||||||
-49
@@ -13,22 +13,17 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -40,7 +35,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import com.flagship.abox.manager.R
|
import com.flagship.abox.manager.R
|
||||||
import com.flagship.abox.manager.designsystem.ABoxButton
|
import com.flagship.abox.manager.designsystem.ABoxButton
|
||||||
import com.flagship.abox.manager.designsystem.ABoxLoadingIndicator
|
import com.flagship.abox.manager.designsystem.ABoxLoadingIndicator
|
||||||
import com.flagship.abox.manager.domain.DeviceName
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@@ -70,7 +64,6 @@ fun TemperatureHumidityScreen(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
var showEditNameDialog by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
@@ -88,12 +81,6 @@ fun TemperatureHumidityScreen(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(
|
|
||||||
onClick = { showEditNameDialog = true },
|
|
||||||
enabled = !state.isLoading && !state.isRefreshing,
|
|
||||||
) {
|
|
||||||
Text("✎", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
|
||||||
}
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { onEvent(TemperatureHumidityUiEvent.Refresh) },
|
onClick = { onEvent(TemperatureHumidityUiEvent.Refresh) },
|
||||||
enabled = !state.isLoading && !state.isRefreshing,
|
enabled = !state.isLoading && !state.isRefreshing,
|
||||||
@@ -238,42 +225,6 @@ fun TemperatureHumidityScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showEditNameDialog) {
|
|
||||||
var inputName by remember(state.deviceName) { mutableStateOf(state.deviceName?.value.orEmpty()) }
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = { showEditNameDialog = false },
|
|
||||||
title = { Text(stringResource(R.string.dialog_edit_device_name_title)) },
|
|
||||||
text = {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = inputName,
|
|
||||||
onValueChange = { inputName = it },
|
|
||||||
label = { Text(stringResource(R.string.dialog_edit_device_name_hint)) },
|
|
||||||
singleLine = true,
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
val trimmed = inputName.trim()
|
|
||||||
if (trimmed.isNotBlank()) {
|
|
||||||
onEvent(TemperatureHumidityUiEvent.ChangeDeviceName(DeviceName(trimmed)))
|
|
||||||
}
|
|
||||||
showEditNameDialog = false
|
|
||||||
},
|
|
||||||
enabled = inputName.isNotBlank(),
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.action_confirm))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = { showEditNameDialog = false }) {
|
|
||||||
Text(stringResource(R.string.action_cancel))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
-1
@@ -28,7 +28,6 @@ data class TemperatureHumidityUiState(
|
|||||||
|
|
||||||
sealed interface TemperatureHumidityUiEvent {
|
sealed interface TemperatureHumidityUiEvent {
|
||||||
data class Initialize(val profileId: ProviderProfileId, val deviceName: DeviceName) : TemperatureHumidityUiEvent
|
data class Initialize(val profileId: ProviderProfileId, val deviceName: DeviceName) : TemperatureHumidityUiEvent
|
||||||
data class ChangeDeviceName(val deviceName: DeviceName) : TemperatureHumidityUiEvent
|
|
||||||
data object Refresh : TemperatureHumidityUiEvent
|
data object Refresh : TemperatureHumidityUiEvent
|
||||||
data object ClearError : TemperatureHumidityUiEvent
|
data object ClearError : TemperatureHumidityUiEvent
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-27
@@ -15,8 +15,6 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.net.URLDecoder
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@@ -29,8 +27,8 @@ class TemperatureHumidityViewModel @Inject constructor(
|
|||||||
val state: StateFlow<TemperatureHumidityUiState> = _state.asStateFlow()
|
val state: StateFlow<TemperatureHumidityUiState> = _state.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val profileIdString: String? = savedStateHandle.get<String>("profileId")?.let { decode(it) }
|
val profileIdString: String? = savedStateHandle["profileId"]
|
||||||
val deviceNameString: String? = savedStateHandle.get<String>("deviceName")?.let { decode(it) }
|
val deviceNameString: String? = savedStateHandle["deviceName"]
|
||||||
|
|
||||||
if (!profileIdString.isNullOrBlank() && !deviceNameString.isNullOrBlank()) {
|
if (!profileIdString.isNullOrBlank() && !deviceNameString.isNullOrBlank()) {
|
||||||
onEvent(
|
onEvent(
|
||||||
@@ -42,12 +40,6 @@ class TemperatureHumidityViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decode(value: String): String = try {
|
|
||||||
URLDecoder.decode(value, StandardCharsets.UTF_8.name())
|
|
||||||
} catch (_: Exception) {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onEvent(event: TemperatureHumidityUiEvent) {
|
fun onEvent(event: TemperatureHumidityUiEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is TemperatureHumidityUiEvent.Initialize -> {
|
is TemperatureHumidityUiEvent.Initialize -> {
|
||||||
@@ -60,23 +52,6 @@ class TemperatureHumidityViewModel @Inject constructor(
|
|||||||
loadReading(isRefresh = false)
|
loadReading(isRefresh = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
is TemperatureHumidityUiEvent.ChangeDeviceName -> {
|
|
||||||
val trimmed = event.deviceName.value.trim()
|
|
||||||
if (trimmed.isNotBlank() && trimmed != _state.value.deviceName?.value) {
|
|
||||||
_state.update {
|
|
||||||
it.copy(
|
|
||||||
deviceName = DeviceName(trimmed),
|
|
||||||
temperature = null,
|
|
||||||
humidity = null,
|
|
||||||
hasNoData = false,
|
|
||||||
errorMessage = null,
|
|
||||||
lastUpdatedTimeMillis = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
loadReading(isRefresh = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TemperatureHumidityUiEvent.Refresh -> {
|
TemperatureHumidityUiEvent.Refresh -> {
|
||||||
loadReading(isRefresh = true)
|
loadReading(isRefresh = true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,11 +81,6 @@
|
|||||||
<string name="action_refresh">刷新</string>
|
<string name="action_refresh">刷新</string>
|
||||||
<string name="action_retry">重试</string>
|
<string name="action_retry">重试</string>
|
||||||
<string name="action_back">返回</string>
|
<string name="action_back">返回</string>
|
||||||
<string name="action_edit_device_name">修改设备名称</string>
|
|
||||||
<string name="dialog_edit_device_name_title">修改设备名称</string>
|
|
||||||
<string name="dialog_edit_device_name_hint">请输入网关中的设备名称</string>
|
|
||||||
<string name="action_confirm">确定</string>
|
|
||||||
<string name="action_cancel">取消</string>
|
|
||||||
<string name="status_loading">加载中…</string>
|
<string name="status_loading">加载中…</string>
|
||||||
<string name="status_device_offline_banner">设备已离线,无法进行通信。</string>
|
<string name="status_device_offline_banner">设备已离线,无法进行通信。</string>
|
||||||
<string name="error_authentication">认证失败,请重新登录。</string>
|
<string name="error_authentication">认证失败,请重新登录。</string>
|
||||||
|
|||||||
@@ -81,11 +81,6 @@
|
|||||||
<string name="action_refresh">Refresh</string>
|
<string name="action_refresh">Refresh</string>
|
||||||
<string name="action_retry">Retry</string>
|
<string name="action_retry">Retry</string>
|
||||||
<string name="action_back">Back</string>
|
<string name="action_back">Back</string>
|
||||||
<string name="action_edit_device_name">Edit Device Name</string>
|
|
||||||
<string name="dialog_edit_device_name_title">Edit Device Name</string>
|
|
||||||
<string name="dialog_edit_device_name_hint">Enter gateway device name</string>
|
|
||||||
<string name="action_confirm">Confirm</string>
|
|
||||||
<string name="action_cancel">Cancel</string>
|
|
||||||
<string name="status_loading">Loading…</string>
|
<string name="status_loading">Loading…</string>
|
||||||
<string name="status_device_offline_banner">Device is offline. Commands cannot be sent.</string>
|
<string name="status_device_offline_banner">Device is offline. Commands cannot be sent.</string>
|
||||||
<string name="error_authentication">Authentication failed. Please log in again.</string>
|
<string name="error_authentication">Authentication failed. Please log in again.</string>
|
||||||
|
|||||||
-14
@@ -170,20 +170,6 @@ class SocketViewModelTest {
|
|||||||
assertEquals(SocketPowerState.ON, viewModel.state.value.powerState)
|
assertEquals(SocketPowerState.ON, viewModel.state.value.powerState)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun changeDeviceName_updatesNameAndReloads() = runTest {
|
|
||||||
val newDevice = DeviceName("socket-2")
|
|
||||||
fakeSocketGateway.setPower(profileId, newDevice, SocketPowerState.ON)
|
|
||||||
|
|
||||||
val viewModel = SocketViewModel(fakeSocketGateway, SavedStateHandle())
|
|
||||||
viewModel.onEvent(SocketUiEvent.Initialize(profileId, deviceName))
|
|
||||||
assertEquals(SocketPowerState.OFF, viewModel.state.value.powerState)
|
|
||||||
|
|
||||||
viewModel.onEvent(SocketUiEvent.ChangeDeviceName(newDevice))
|
|
||||||
assertEquals(newDevice, viewModel.state.value.deviceName)
|
|
||||||
assertEquals(SocketPowerState.ON, viewModel.state.value.powerState)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun clearError_resetsErrorMessage() = runTest {
|
fun clearError_resetsErrorMessage() = runTest {
|
||||||
fakeSocketGateway.setError(profileId, deviceName, DomainError.InvalidRequest())
|
fakeSocketGateway.setError(profileId, deviceName, DomainError.InvalidRequest())
|
||||||
|
|||||||
-22
@@ -158,28 +158,6 @@ class TemperatureHumidityViewModelTest {
|
|||||||
assertFalse(state.isRefreshing)
|
assertFalse(state.isRefreshing)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun changeDeviceName_updatesNameAndReloads() = runTest {
|
|
||||||
val newDevice = DeviceName("th-2")
|
|
||||||
fakeGateway.setReading(
|
|
||||||
TemperatureHumidityReading(
|
|
||||||
profileId = profileId,
|
|
||||||
deviceName = newDevice,
|
|
||||||
temperature = "26.8",
|
|
||||||
humidity = "55",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
val viewModel = TemperatureHumidityViewModel(fakeGateway, SavedStateHandle())
|
|
||||||
viewModel.onEvent(TemperatureHumidityUiEvent.Initialize(profileId, deviceName))
|
|
||||||
assertEquals("23.5", viewModel.state.value.temperature)
|
|
||||||
|
|
||||||
viewModel.onEvent(TemperatureHumidityUiEvent.ChangeDeviceName(newDevice))
|
|
||||||
assertEquals(newDevice, viewModel.state.value.deviceName)
|
|
||||||
assertEquals("26.8", viewModel.state.value.temperature)
|
|
||||||
assertEquals("55", viewModel.state.value.humidity)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun clearError_resetsErrorMessage() = runTest {
|
fun clearError_resetsErrorMessage() = runTest {
|
||||||
fakeGateway.setError(profileId, deviceName, DomainError.Network())
|
fakeGateway.setError(profileId, deviceName, DomainError.Network())
|
||||||
|
|||||||
Reference in New Issue
Block a user