16 files changed
+234
-4
No files matched your search
@@ -677,6 +677,13 @@ class ChatViewModel(
|
||||
if (!initial.isNullOrBlank()) {
|
||||
initialSessionId = null
|
||||
switchSession(initial)
|
||||
} else if (AuthManager.isRestoreLastSession()) {
|
||||
val restoredId = AuthManager.getLastOpenedSessionId()
|
||||
if (!restoredId.isNullOrBlank()) {
|
||||
switchSession(restoredId)
|
||||
} else {
|
||||
createNewSession(setLoading = false)
|
||||
}
|
||||
} else {
|
||||
createNewSession(setLoading = false)
|
||||
}
|
||||
@@ -1406,6 +1413,7 @@ class ChatViewModel(
|
||||
userMessage: ChatMessage? = null,
|
||||
) {
|
||||
val dispatchGeneration = sessionGeneration
|
||||
AuthManager.setLastOpenedSessionId(storageSessionId)
|
||||
val msgToPersist =
|
||||
userMessage ?: ChatMessage(
|
||||
role = MessageRole.USER,
|
||||
@@ -2356,6 +2364,7 @@ class ChatViewModel(
|
||||
.find { it.id == sessionId }
|
||||
?.title ?: "Hermes"
|
||||
val generation = resetSessionState(sessionId, title, isLoading = true)
|
||||
AuthManager.setLastOpenedSessionId(sessionId)
|
||||
viewModelScope.launch {
|
||||
// Warm-cache fast-path (desktop parity): paint the cached Room
|
||||
// transcript immediately so the screen never sits blank, then load
|
||||
@@ -2708,6 +2717,9 @@ class ChatViewModel(
|
||||
}
|
||||
sessionGoneRecoveryInFlight = true
|
||||
cancelResumeRetry()
|
||||
if (AuthManager.getLastOpenedSessionId() == sessionId) {
|
||||
AuthManager.clearLastOpenedSessionId()
|
||||
}
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
isLoading = false,
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.m57.hermescontrol.ui.sessions
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.m57.hermescontrol.data.local.AuthManager
|
||||
import com.m57.hermescontrol.data.model.BulkDeleteRequest
|
||||
import com.m57.hermescontrol.data.model.PruneRequest
|
||||
import com.m57.hermescontrol.data.model.SessionInfo
|
||||
@@ -689,6 +690,9 @@ class SessionsViewModel(
|
||||
}
|
||||
when (result) {
|
||||
is NetworkResult.Success -> {
|
||||
if (AuthManager.getLastOpenedSessionId() == sessionId) {
|
||||
AuthManager.clearLastOpenedSessionId()
|
||||
}
|
||||
_uiState.update {
|
||||
val updatedSessions = it.sessions.filter { s -> s.id != sessionId }
|
||||
val newTotal = (it.total - 1).coerceAtLeast(0)
|
||||
@@ -739,6 +743,10 @@ class SessionsViewModel(
|
||||
}
|
||||
when (result) {
|
||||
is NetworkResult.Success -> {
|
||||
val lastOpened = AuthManager.getLastOpenedSessionId()
|
||||
if (lastOpened != null && ids.contains(lastOpened)) {
|
||||
AuthManager.clearLastOpenedSessionId()
|
||||
}
|
||||
val deletedCount = result.data.deleted
|
||||
val toastMsg =
|
||||
if (deletedCount > 0) {
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user