From 74a6795b6fad45d9c03de9d41a10a01d9c7647ce Mon Sep 17 00:00:00 2001 From: Ivan Fontosh Date: Sun, 28 Jun 2026 14:08:58 +0800 Subject: [PATCH] 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 --- .../licensemanager/ui/LicenseViewModel.kt | 25 ++++++++----------- .../ui/screens/LicenseListScreen.kt | 2 +- .../ui/screens/SettingsScreen.kt | 5 +--- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/LicenseViewModel.kt b/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/LicenseViewModel.kt index 11ae2de..e2dd758 100644 --- a/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/LicenseViewModel.kt +++ b/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/LicenseViewModel.kt @@ -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 = 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 = _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() } } } diff --git a/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/screens/LicenseListScreen.kt b/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/screens/LicenseListScreen.kt index f01a22d..abcebbb 100644 --- a/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/screens/LicenseListScreen.kt +++ b/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/screens/LicenseListScreen.kt @@ -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, diff --git a/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/screens/SettingsScreen.kt b/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/screens/SettingsScreen.kt index 7cb56d4..614d734 100644 --- a/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/ru/mailib/ttrpg/licensemanager/ui/screens/SettingsScreen.kt @@ -64,10 +64,7 @@ fun SettingsScreen(viewModel: LicenseViewModel, onBack: () -> Unit) { } Button( - onClick = { - viewModel.saveSettings() - onBack() - }, + onClick = { viewModel.saveSettings(onSaved = onBack) }, modifier = Modifier.fillMaxWidth(), ) { Text("Сохранить")