Hardcode production license server URL in app.

Remove editable server URL from settings; only admin token is configured by user.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-06-28 13:52:32 +08:00
parent 2037905976
commit f2e0c61010
3 changed files with 25 additions and 25 deletions
@@ -17,30 +17,29 @@ import retrofit2.converter.gson.GsonConverterFactory
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings") private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
data class AppSettings( data class AppSettings(
val serverUrl: String = DEFAULT_SERVER_URL, val serverUrl: String = SERVER_URL,
val adminToken: String = "", val adminToken: String = "",
) { ) {
companion object { companion object {
const val DEFAULT_SERVER_URL = "https://license.mailib.ru/" /** Продакшен-сервер лицензий (захардкожен, не меняется в UI). */
const val SERVER_URL = "https://license.mailib.ru/"
@Deprecated("Use SERVER_URL", ReplaceWith("SERVER_URL"))
const val DEFAULT_SERVER_URL = SERVER_URL
} }
} }
class SettingsRepository(private val context: Context) { class SettingsRepository(private val context: Context) {
private val serverUrlKey = stringPreferencesKey("server_url")
private val adminTokenKey = stringPreferencesKey("admin_token") private val adminTokenKey = stringPreferencesKey("admin_token")
val settings: Flow<AppSettings> = context.dataStore.data.map { prefs -> val settings: Flow<AppSettings> = context.dataStore.data.map { prefs ->
AppSettings( AppSettings(
serverUrl = prefs[serverUrlKey] ?: AppSettings.DEFAULT_SERVER_URL, serverUrl = AppSettings.SERVER_URL,
adminToken = prefs[adminTokenKey].orEmpty(), adminToken = prefs[adminTokenKey].orEmpty(),
) )
} }
suspend fun save(serverUrl: String, adminToken: String) { suspend fun save(adminToken: String) {
context.dataStore.edit { prefs -> context.dataStore.edit { prefs ->
prefs[serverUrlKey] = serverUrl.trim().let { url ->
if (url.endsWith("/")) url else "$url/"
}
prefs[adminTokenKey] = adminToken.trim() prefs[adminTokenKey] = adminToken.trim()
} }
} }
@@ -37,7 +37,6 @@ data class GenerateKeyUiState(
) )
data class SettingsUiState( data class SettingsUiState(
val serverUrl: String = AppSettings.DEFAULT_SERVER_URL,
val adminToken: String = "", val adminToken: String = "",
val savedMessage: String? = null, val savedMessage: String? = null,
) )
@@ -62,10 +61,7 @@ class LicenseViewModel(application: Application) : AndroidViewModel(application)
viewModelScope.launch { viewModelScope.launch {
settings.collect { appSettings -> settings.collect { appSettings ->
_settingsState.update { _settingsState.update {
it.copy( it.copy(adminToken = appSettings.adminToken)
serverUrl = appSettings.serverUrl,
adminToken = appSettings.adminToken,
)
} }
} }
} }
@@ -159,16 +155,16 @@ class LicenseViewModel(application: Application) : AndroidViewModel(application)
} }
} }
fun updateSettingsDraft(serverUrl: String, adminToken: String) { fun updateAdminToken(value: String) {
_settingsState.update { _settingsState.update {
it.copy(serverUrl = serverUrl, adminToken = adminToken, savedMessage = null) it.copy(adminToken = value, savedMessage = null)
} }
} }
fun saveSettings() { fun saveSettings() {
val draft = _settingsState.value val draft = _settingsState.value
viewModelScope.launch { viewModelScope.launch {
settingsRepository.save(draft.serverUrl, draft.adminToken) settingsRepository.save(draft.adminToken)
_settingsState.update { it.copy(savedMessage = "Сохранено") } _settingsState.update { it.copy(savedMessage = "Сохранено") }
} }
} }
@@ -16,6 +16,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import ru.mailib.ttrpg.licensemanager.data.AppSettings
import ru.mailib.ttrpg.licensemanager.ui.LicenseViewModel import ru.mailib.ttrpg.licensemanager.ui.LicenseViewModel
@Composable @Composable
@@ -29,23 +30,27 @@ fun SettingsScreen(viewModel: LicenseViewModel, onBack: () -> Unit) {
verticalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp),
) { ) {
Text( Text(
text = "Настройки сервера", text = "Настройки",
style = MaterialTheme.typography.headlineSmall, style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
) )
OutlinedTextField( Text(
value = state.serverUrl, text = "Сервер: ${AppSettings.SERVER_URL}",
onValueChange = { value -> viewModel.updateSettingsDraft(value, state.adminToken) }, style = MaterialTheme.typography.bodyMedium,
label = { Text("URL сервера") }, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.fillMaxWidth(), )
singleLine = true,
Text(
text = "Токен администратора (LICENSE_ADMIN_TOKEN) — см. инструкцию в README или спросите у администратора сервера.",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
) )
OutlinedTextField( OutlinedTextField(
value = state.adminToken, value = state.adminToken,
onValueChange = { value -> viewModel.updateSettingsDraft(state.serverUrl, value) }, onValueChange = viewModel::updateAdminToken,
label = { Text("Admin token (LICENSE_ADMIN_TOKEN)") }, label = { Text("Admin token") },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
singleLine = true, singleLine = true,
visualTransformation = PasswordVisualTransformation(), visualTransformation = PasswordVisualTransformation(),