fix(scene): preserve fixed gateway device identifiers, fix edit loading race condition and parallel abort strategy
This commit is contained in:
+501
-442
File diff suppressed because it is too large
Load Diff
+15
-3
@@ -50,11 +50,12 @@ data class SceneEditorState(
|
||||
val concurrencyMode: ConcurrencyMode = ConcurrencyMode.SEQUENTIAL,
|
||||
val enableLogging: Boolean = true,
|
||||
val availableRemotes: List<VirtualRemote> = emptyList(),
|
||||
val isLoading: Boolean = false,
|
||||
val isSaving: Boolean = false,
|
||||
val isSaved: Boolean = false,
|
||||
val errorMessage: String? = null,
|
||||
) {
|
||||
val isValid: Boolean get() = name.isNotBlank() && actions.isNotEmpty() && (type != AutomationType.AUTOMATIC || triggers.isNotEmpty())
|
||||
val isValid: Boolean get() = !isLoading && name.isNotBlank() && actions.isNotEmpty() && (type != AutomationType.AUTOMATIC || triggers.isNotEmpty())
|
||||
val isEditing: Boolean get() = sceneId != null
|
||||
}
|
||||
|
||||
@@ -100,14 +101,24 @@ class SceneEditorViewModel @Inject constructor(
|
||||
type = initialType ?: AutomationType.MANUAL,
|
||||
colorHex = if (initialType == AutomationType.AUTOMATIC) "#26A69A" else "#3F51B5",
|
||||
availableRemotes = it.availableRemotes,
|
||||
isLoading = false,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_state.update {
|
||||
it.copy(
|
||||
sceneId = sceneId,
|
||||
isLoading = true,
|
||||
errorMessage = null,
|
||||
)
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
val profile = profileStore.activeProfile()
|
||||
if (profile == null) {
|
||||
_state.update { it.copy(errorMessage = "No active profile") }
|
||||
_state.update { it.copy(isLoading = false, errorMessage = "No active profile") }
|
||||
return@launch
|
||||
}
|
||||
val scene = (sceneRepository.getScene(profile.id, sceneId) as? DomainResult.Success)?.value
|
||||
@@ -127,10 +138,11 @@ class SceneEditorViewModel @Inject constructor(
|
||||
strategy = scene.strategy,
|
||||
concurrencyMode = scene.concurrencyMode,
|
||||
enableLogging = scene.enableLogging,
|
||||
isLoading = false,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
_state.update { it.copy(errorMessage = "Scene not found") }
|
||||
_state.update { it.copy(isLoading = false, errorMessage = "Scene not found") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,8 +196,9 @@
|
||||
<string name="scene_condition_desc_humidity">湿度需 %1$s %2$s%%</string>
|
||||
<string name="scene_condition_desc_socket">%1$s 处于 %2$s</string>
|
||||
<string name="scene_condition_desc_time">时间在 %1$s ~ %2$s 之间</string>
|
||||
<string name="scene_default_socket_name">Zigbee插座控制器</string>
|
||||
<string name="scene_default_ir_name">红外控制器</string>
|
||||
<string name="scene_device_name_label">设备名称(网关配对标识)</string>
|
||||
<string name="scene_loading_scene">正在加载情景配置…</string>
|
||||
<string name="scene_load_failed">无法加载情景配置。</string>
|
||||
<string name="scene_default_button_power">电源</string>
|
||||
<string name="scene_default_notify_title">情景完成</string>
|
||||
<string name="scene_default_notify_msg">自动化任务执行完成</string>
|
||||
|
||||
@@ -196,8 +196,9 @@
|
||||
<string name="scene_condition_desc_humidity">Humidity must be %1$s %2$s%%</string>
|
||||
<string name="scene_condition_desc_socket">%1$s is %2$s</string>
|
||||
<string name="scene_condition_desc_time">Time is between %1$s ~ %2$s</string>
|
||||
<string name="scene_default_socket_name">Zigbee Smart Socket</string>
|
||||
<string name="scene_default_ir_name">Infrared Controller</string>
|
||||
<string name="scene_device_name_label">Device Name (Gateway Identifier)</string>
|
||||
<string name="scene_loading_scene">Loading scene...</string>
|
||||
<string name="scene_load_failed">Failed to load scene.</string>
|
||||
<string name="scene_default_button_power">Power</string>
|
||||
<string name="scene_default_notify_title">Scene Completed</string>
|
||||
<string name="scene_default_notify_msg">Automation task executed successfully</string>
|
||||
|
||||
+8
@@ -81,10 +81,18 @@ class SceneEditorViewModelTest {
|
||||
setupProfile()
|
||||
val viewModel = SceneEditorViewModel(sceneRepository, virtualRemoteRepository, profileStore)
|
||||
viewModel.initialize(existingScene.id)
|
||||
|
||||
// Immediately binds sceneId and enters loading state
|
||||
assertTrue(viewModel.state.value.isEditing)
|
||||
assertEquals(existingScene.id, viewModel.state.value.sceneId)
|
||||
assertTrue(viewModel.state.value.isLoading)
|
||||
assertFalse(viewModel.state.value.isValid)
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
val state = viewModel.state.value
|
||||
assertTrue(state.isEditing)
|
||||
assertFalse(state.isLoading)
|
||||
assertEquals("Existing Scene", state.name)
|
||||
assertEquals(1, state.actions.size)
|
||||
assertTrue(state.isValid)
|
||||
|
||||
Reference in New Issue
Block a user