Fix license list loading and crash on duplicate subs.

Read admin token from DataStore before API calls, reload licenses after save, and use unique product key as LazyColumn item key.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-06-28 14:08:58 +08:00
parent f2e0c61010
commit 74a6795b6f
3 changed files with 13 additions and 19 deletions
@@ -4,12 +4,11 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.update
import ru.mailib.ttrpg.licensemanager.data.AppSettings
import ru.mailib.ttrpg.licensemanager.data.CreateProductKeyRequest
import ru.mailib.ttrpg.licensemanager.data.LicenseEntry
@@ -45,8 +44,7 @@ class LicenseViewModel(application: Application) : AndroidViewModel(application)
private val settingsRepository = SettingsRepository(application)
private val licenseRepository = LicenseRepository(settingsRepository)
val settings: StateFlow<AppSettings> = settingsRepository.settings
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), AppSettings())
private suspend fun currentSettings(): AppSettings = settingsRepository.settings.first()
private val _listState = MutableStateFlow(LicenseListUiState())
val listState: StateFlow<LicenseListUiState> = _listState.asStateFlow()
@@ -59,17 +57,14 @@ class LicenseViewModel(application: Application) : AndroidViewModel(application)
init {
viewModelScope.launch {
settings.collect { appSettings ->
_settingsState.update {
it.copy(adminToken = appSettings.adminToken)
}
}
val initial = settingsRepository.settings.first()
_settingsState.update { it.copy(adminToken = initial.adminToken) }
}
}
fun loadLicenses() {
viewModelScope.launch {
val currentSettings = settings.value
val currentSettings = currentSettings()
_listState.update { it.copy(isLoading = true, error = null) }
licenseRepository.listLicenses(currentSettings)
.onSuccess { licenses ->
@@ -84,7 +79,7 @@ class LicenseViewModel(application: Application) : AndroidViewModel(application)
fun revokeLicense(sub: String) {
viewModelScope.launch {
_listState.update { it.copy(revokingSub = sub, error = null) }
licenseRepository.revokeLicense(settings.value, sub)
licenseRepository.revokeLicense(currentSettings(), sub)
.onSuccess {
loadLicenses()
_listState.update { it.copy(revokingSub = null) }
@@ -136,7 +131,7 @@ class LicenseViewModel(application: Application) : AndroidViewModel(application)
viewModelScope.launch {
_generateState.update { it.copy(isSubmitting = true, error = null, createdKey = null) }
licenseRepository.createProductKey(
settings.value,
currentSettings(),
CreateProductKeyRequest(
pid = state.pid.trim(),
maxDevices = maxDevices,
@@ -161,11 +156,13 @@ class LicenseViewModel(application: Application) : AndroidViewModel(application)
}
}
fun saveSettings() {
fun saveSettings(onSaved: () -> Unit = {}) {
val draft = _settingsState.value
viewModelScope.launch {
settingsRepository.save(draft.adminToken)
_settingsState.update { it.copy(savedMessage = "Сохранено") }
loadLicenses()
onSaved()
}
}
}
@@ -113,7 +113,7 @@ fun LicenseListScreen(viewModel: LicenseViewModel) {
ErrorMessage(message = state.error ?: "")
}
}
items(state.licenses, key = { it.sub }) { license ->
items(state.licenses, key = { it.key }) { license ->
LicenseCard(
license = license,
isRevoking = state.revokingSub == license.sub,
@@ -64,10 +64,7 @@ fun SettingsScreen(viewModel: LicenseViewModel, onBack: () -> Unit) {
}
Button(
onClick = {
viewModel.saveSettings()
onBack()
},
onClick = { viewModel.saveSettings(onSaved = onBack) },
modifier = Modifier.fillMaxWidth(),
) {
Text("Сохранить")