Implement MVVM for all V3 screens: add ViewModels for Turniere, Bewerbe, Abteilungen, Pferde, Reiter, Vereins, and Funktionaer workflows. Update roadmap to mark B-1 tasks as complete.
This commit is contained in:
+91
@@ -0,0 +1,91 @@
|
||||
package at.mocode.frontend.features.funktionaer.presentation
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
data class FunktionaerListItem(
|
||||
val id: Long,
|
||||
val name: String,
|
||||
val rolle: String,
|
||||
val lizenz: String?,
|
||||
)
|
||||
|
||||
data class FunktionaerState(
|
||||
val isLoading: Boolean = false,
|
||||
val searchQuery: String = "",
|
||||
val list: List<FunktionaerListItem> = emptyList(),
|
||||
val filtered: List<FunktionaerListItem> = emptyList(),
|
||||
val selectedId: Long? = null,
|
||||
val errorMessage: String? = null,
|
||||
)
|
||||
|
||||
sealed interface FunktionaerIntent {
|
||||
data object Load : FunktionaerIntent
|
||||
data object Refresh : FunktionaerIntent
|
||||
data class SearchChanged(val query: String) : FunktionaerIntent
|
||||
data class Select(val id: Long?) : FunktionaerIntent
|
||||
data object ClearError : FunktionaerIntent
|
||||
}
|
||||
|
||||
interface FunktionaerRepository {
|
||||
suspend fun list(): List<FunktionaerListItem>
|
||||
}
|
||||
|
||||
class FunktionaerViewModel(
|
||||
private val repo: FunktionaerRepository,
|
||||
) {
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
|
||||
private val _state = MutableStateFlow(FunktionaerState(isLoading = true))
|
||||
val state: StateFlow<FunktionaerState> = _state
|
||||
|
||||
init { send(FunktionaerIntent.Load) }
|
||||
|
||||
fun send(intent: FunktionaerIntent) {
|
||||
when (intent) {
|
||||
is FunktionaerIntent.Load, is FunktionaerIntent.Refresh -> load()
|
||||
is FunktionaerIntent.SearchChanged -> reduce { it.copy(searchQuery = intent.query) }.also { filter() }
|
||||
is FunktionaerIntent.Select -> reduce { it.copy(selectedId = intent.id) }
|
||||
is FunktionaerIntent.ClearError -> reduce { it.copy(errorMessage = null) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun load() {
|
||||
reduce { it.copy(isLoading = true, errorMessage = null) }
|
||||
scope.launch {
|
||||
try {
|
||||
val items = repo.list()
|
||||
reduce { cur ->
|
||||
val filtered = filterList(items, cur.searchQuery)
|
||||
cur.copy(isLoading = false, list = items, filtered = filtered)
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
reduce { it.copy(isLoading = false, errorMessage = t.message ?: "Fehler beim Laden") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun filter() {
|
||||
val cur = _state.value
|
||||
val filtered = filterList(cur.list, cur.searchQuery)
|
||||
reduce { it.copy(filtered = filtered) }
|
||||
}
|
||||
|
||||
private fun filterList(list: List<FunktionaerListItem>, query: String): List<FunktionaerListItem> {
|
||||
if (query.isBlank()) return list
|
||||
val q = query.trim()
|
||||
return list.filter {
|
||||
it.name.contains(q, ignoreCase = true) ||
|
||||
it.rolle.contains(q, ignoreCase = true) ||
|
||||
(it.lizenz?.contains(q, ignoreCase = true) ?: false)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun reduce(block: (FunktionaerState) -> FunktionaerState) {
|
||||
_state.value = block(_state.value)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user