feat(settings): restore last session on startup (#1102) (#1105)

This commit is contained in:
M57 authored and GitHub committed 2026-09-13 16:45:04 +03:00
1 parent 7e8af8db01
commit 4246ae196d
16 files changed
+234 -4

No files matched your search

@@ -113,7 +113,7 @@ fun SettingsScreen(
icon = Icons.Filled.Tune,
label = stringResource(R.string.settings_sec_behavior),
summary =
if (state.autoReconnect) {
if (state.autoReconnect || state.restoreLastSession) {
stringResource(R.string.settings_summary_on)
} else {
stringResource(R.string.settings_summary_off)
@@ -227,6 +227,8 @@ internal fun SettingsBehaviorPage(
BehaviorSection(
autoReconnect = state.autoReconnect,
onAutoReconnectChange = viewModel::onAutoReconnectChange,
restoreLastSession = state.restoreLastSession,
onRestoreLastSessionChange = viewModel::onRestoreLastSessionChange,
)
}
}
@@ -28,6 +28,7 @@ data class SettingsUiState(
val transportWarning: String? = null,
val token: String = "",
val autoReconnect: Boolean = true,
val restoreLastSession: Boolean = false,
val themePreference: ThemePreference = ThemePreference.SYSTEM,
val useDynamicColors: Boolean = false,
val themePreset: ThemePreset = ThemePreset.DEFAULT,
@@ -72,6 +73,7 @@ class SettingsViewModel(
val baseUrl = AuthManager.getBaseUrl()
val token = AuthManager.getToken() ?: ""
val autoReconnect = AuthManager.isAutoReconnect()
val restoreLastSession = AuthManager.isRestoreLastSession()
val themePreference = AuthManager.getThemePreference()
val useDynamicColors = AuthManager.isUseDynamicColors()
val themePreset = AuthManager.getThemePreset()
@@ -92,6 +94,7 @@ class SettingsViewModel(
transportWarning = transportWarning,
token = token,
autoReconnect = autoReconnect,
restoreLastSession = restoreLastSession,
themePreference = themePreference,
useDynamicColors = useDynamicColors,
themePreset = themePreset,
@@ -120,6 +123,7 @@ class SettingsViewModel(
val updatedProfiles = AuthManager.getConnectionProfiles().filter { it.id != profileId }
AuthManager.saveConnectionProfiles(updatedProfiles)
AuthManager.setProfileToken(profileId, null)
AuthManager.clearLastOpenedSessionIdsForConnection(profileId)
// Never leave selection null (issue #478): if the deleted profile was selected,
// fall back to the default profile instead of clearing selection.
val reselectDefault =
@@ -283,6 +287,11 @@ class SettingsViewModel(
AuthManager.setAutoReconnect(enabled)
}
fun onRestoreLastSessionChange(enabled: Boolean) {
_uiState.update { it.copy(restoreLastSession = enabled, isSaved = false) }
AuthManager.setRestoreLastSession(enabled)
}
fun onThemeChange(theme: ThemePreference) {
_uiState.update { it.copy(themePreference = theme, isSaved = false) }
AuthManager.setThemePreference(theme)
@@ -323,6 +332,7 @@ class SettingsViewModel(
AuthManager.setToken(null)
AuthManager.setSessionCookie(null)
AuthManager.setWsAuthParam("token")
AuthManager.clearLastOpenedSessionId()
HermesWsClient.disconnect(clearPendingMessages = true)
// Don't rebuild ApiClient here — let the navigation complete first
}
@@ -337,6 +347,7 @@ class SettingsViewModel(
AuthManager.setBaseUrl(normalized)
AuthManager.setToken(state.token)
AuthManager.setAutoReconnect(state.autoReconnect)
AuthManager.setRestoreLastSession(state.restoreLastSession)
// B6 (Jun 18 2026, kanban t_86e9be9b): persist theme choice so it
// survives a cold start (was previously dropped on save()).
AuthManager.setThemePreference(state.themePreference)
@@ -3,17 +3,19 @@ package com.m57.hermescontrol.ui.settings.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.m57.hermescontrol.R
import com.m57.hermescontrol.ui.settings.SectionCard
@@ -21,6 +23,8 @@ import com.m57.hermescontrol.ui.settings.SectionCard
internal fun BehaviorSection(
autoReconnect: Boolean,
onAutoReconnectChange: (Boolean) -> Unit,
restoreLastSession: Boolean,
onRestoreLastSessionChange: (Boolean) -> Unit,
) {
SectionCard {
Row(
@@ -28,7 +32,7 @@ internal fun BehaviorSection(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column {
Column(modifier = Modifier.weight(1f)) {
Text(
text = stringResource(R.string.settings_item_auto_reconnect),
style = MaterialTheme.typography.bodyLarge,
@@ -51,5 +55,38 @@ internal fun BehaviorSection(
),
)
}
Spacer(modifier = Modifier.height(12.dp))
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
Spacer(modifier = Modifier.height(12.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = stringResource(R.string.settings_item_restore_session),
style = MaterialTheme.typography.bodyLarge,
)
Text(
text = stringResource(R.string.settings_desc_restore_session),
style =
MaterialTheme.typography.bodySmall.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant,
),
)
}
Switch(
checked = restoreLastSession,
onCheckedChange = onRestoreLastSessionChange,
colors =
SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colorScheme.primary,
checkedTrackColor = MaterialTheme.colorScheme.primaryContainer,
),
)
}
}
}