@@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
16import kotlinx.coroutines.flow.StateFlow
16import kotlinx.coroutines.flow.StateFlow
17import kotlinx.coroutines.flow.asStateFlow
17import kotlinx.coroutines.flow.asStateFlow
18import kotlinx.coroutines.flow.collectLatest
18import kotlinx.coroutines.flow.collectLatest
19+import kotlinx.coroutines.flow.update
19import kotlinx.coroutines.launch
20import kotlinx.coroutines.launch
@@ -76,7 +77,7 @@ class FileBrowserViewModel(private val container: AppContainer) : ViewModel() {
76 statusJob = viewModelScope.launch {
77 statusJob = viewModelScope.launch {
77 runCatchingCancellable { client.fileStatus() }
78 runCatchingCancellable { client.fileStatus() }
78 .onSuccess { entries ->
79 .onSuccess { entries ->
79− _state.value = _state.value.copy(statusMap = entries.associateBy { it.path })
80+ _state.update { it.copy(statusMap = entries.associateBy { it.path }) }
81 .onFailure {
82 .onFailure {
82 // VCS status is best-effort: don't block the file browser, but surface
83 // VCS status is best-effort: don't block the file browser, but surface
@@ -88,32 +89,32 @@ class FileBrowserViewModel(private val container: AppContainer) : ViewModel() {
89 fun open(path: String) {
90 fun open(path: String) {
90 val client = api ?: run {
91 val client = api ?: run {
91− _state.value = _state.value.copy(
92+ _state.update { it.copy(
92 path = path,
93 path = path,
93 loading = false,
94 loading = false,
94 error = container.string(R.string.not_connected),
95 error = container.string(R.string.not_connected),
98− _state.value = _state.value.copy(
99+ _state.update { it.copy(
99 path = path,
100 path = path,
100 query = "",
101 query = "",
101 results = emptyList(),
102 results = emptyList(),
102 searching = false,
103 searching = false,
103 loading = true,
104 loading = true,
104 error = null,
105 error = null,
106 openJob?.cancel()
107 openJob?.cancel()
107 searchJob?.cancel()
108 searchJob?.cancel()
108 openJob = viewModelScope.launch {
109 openJob = viewModelScope.launch {
109 runCatchingCancellable { client.listDirectory(path) }
110 runCatchingCancellable { client.listDirectory(path) }
110 .onSuccess { entries ->
111 .onSuccess { entries ->
111− _state.value = _state.value.copy(
112+ _state.update { it.copy(
112 entries = entries.sortedWith(compareByDescending<FileNode> { it.isDirectory }.thenBy { it.name.lowercase() }),
113 entries = entries.sortedWith(compareByDescending<FileNode> { it.isDirectory }.thenBy { it.name.lowercase() }),
113 loading = false,
114 loading = false,
116− .onFailure { _state.value = _state.value.copy(loading = false, error = container.friendlyError(it)) }
117+ .onFailure { e -> _state.update { it.copy(loading = false, error = container.friendlyError(e)) } }
@@ -126,23 +127,23 @@ class FileBrowserViewModel(private val container: AppContainer) : ViewModel() {
128 fun setQuery(query: String) {
129 fun setQuery(query: String) {
129− _state.value = _state.value.copy(query = query, error = null)
130+ _state.update { it.copy(query = query, error = null) }
130 searchJob?.cancel()
131 searchJob?.cancel()
131 openJob?.cancel()
132 openJob?.cancel()
132 if (query.isBlank()) {
133 if (query.isBlank()) {
133− _state.value = _state.value.copy(results = emptyList(), searching = false)
134+ _state.update { it.copy(results = emptyList(), searching = false) }
136 val client = api ?: run {
137 val client = api ?: run {
137− _state.value = _state.value.copy(searching = false, error = container.string(R.string.not_connected))
138+ _state.update { it.copy(searching = false, error = container.string(R.string.not_connected)) }
140 searchJob = viewModelScope.launch {
141 searchJob = viewModelScope.launch {
141 delay(NetworkConfig.fileSearchDebounceMs) // debounce
142 delay(NetworkConfig.fileSearchDebounceMs) // debounce
142− _state.value = _state.value.copy(searching = true)
143+ _state.update { it.copy(searching = true) }
143 runCatchingCancellable { client.findFiles(query) }
144 runCatchingCancellable { client.findFiles(query) }
144− .onSuccess { _state.value = _state.value.copy(results = it, searching = false) }
145+ .onSuccess { results -> _state.update { it.copy(results = results, searching = false) } }
145− .onFailure { _state.value = _state.value.copy(searching = false, error = container.friendlyError(it)) }
146+ .onFailure { e -> _state.update { it.copy(searching = false, error = container.friendlyError(e)) } }