From ff25ef6f47d3306c77bca996fe4606baeb2e181b Mon Sep 17 00:00:00 2001 From: Garfie Date: Tue, 14 Apr 2026 18:14:52 -0500 Subject: [PATCH] fix(settings): stop double-invoking authRepository.logout on logout/delete SettingsViewModel.logout() and deleteAccount() each called authRepository.logout() directly before flipping the state flag that drives the screen's LaunchedEffect -> onLogout callback -> NavHostViewModel.logout(), which invokes authRepository.logout() again. Each redundant call fires a POST /api/auth/logout that 401s (tokens already gone) and re-runs session cleanup; with the planned full-wipe change that duplication becomes actively harmful. Collapse SettingsViewModel.logout() to a single state update, and drop the direct authRepository.logout() call from the deleteAccount success branch. NavHostViewModel.logout() remains the single source of truth for the cleanup side of the flow. --- .../settings/viewmodel/SettingsViewModelTest.kt | 16 +++++----------- .../settings/viewmodel/SettingsViewModel.kt | 7 +------ 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/feature/settings/src/androidUnitTest/kotlin/com/garfiec/librechat/feature/settings/viewmodel/SettingsViewModelTest.kt b/feature/settings/src/androidUnitTest/kotlin/com/garfiec/librechat/feature/settings/viewmodel/SettingsViewModelTest.kt index cd0da2c..6df2e02 100644 --- a/feature/settings/src/androidUnitTest/kotlin/com/garfiec/librechat/feature/settings/viewmodel/SettingsViewModelTest.kt +++ b/feature/settings/src/androidUnitTest/kotlin/com/garfiec/librechat/feature/settings/viewmodel/SettingsViewModelTest.kt @@ -23,7 +23,6 @@ import com.garfiec.librechat.feature.settings.util.PlatformCacheCleaner import com.garfiec.librechat.feature.settings.viewmodel.delegate.SpeechSettingsContract import com.garfiec.librechat.feature.settings.viewmodel.delegate.SpeechSettingsFactory import com.google.common.truth.Truth.assertThat -import io.mockk.Ordering import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every @@ -158,23 +157,20 @@ class SettingsViewModelTest { } @Test - fun `logout calls authRepository and sets isLoggedOut`() = runTest { - coEvery { authRepository.logout() } returns Result.Success(Unit) - + fun `logout sets isLoggedOut flag without calling authRepository`() = runTest { viewModel = createViewModel() advanceUntilIdle() viewModel.logout() advanceUntilIdle() - coVerify { authRepository.logout() } + coVerify(exactly = 0) { authRepository.logout() } assertThat(viewModel.uiState.value.isLoggedOut).isTrue() } @Test - fun `deleteAccount calls userRepository then authRepository`() = runTest { + fun `deleteAccount calls userRepository and sets isAccountDeleted without calling authRepository`() = runTest { coEvery { userRepository.deleteUser() } returns Result.Success(Unit) - coEvery { authRepository.logout() } returns Result.Success(Unit) viewModel = createViewModel() advanceUntilIdle() @@ -182,10 +178,8 @@ class SettingsViewModelTest { viewModel.deleteAccount() advanceUntilIdle() - coVerify(ordering = Ordering.ORDERED) { - userRepository.deleteUser() - authRepository.logout() - } + coVerify { userRepository.deleteUser() } + coVerify(exactly = 0) { authRepository.logout() } assertThat(viewModel.uiState.value.isAccountDeleted).isTrue() } diff --git a/feature/settings/src/commonMain/kotlin/com/garfiec/librechat/feature/settings/viewmodel/SettingsViewModel.kt b/feature/settings/src/commonMain/kotlin/com/garfiec/librechat/feature/settings/viewmodel/SettingsViewModel.kt index 213a186..0f14249 100644 --- a/feature/settings/src/commonMain/kotlin/com/garfiec/librechat/feature/settings/viewmodel/SettingsViewModel.kt +++ b/feature/settings/src/commonMain/kotlin/com/garfiec/librechat/feature/settings/viewmodel/SettingsViewModel.kt @@ -585,11 +585,7 @@ class SettingsViewModel( // ── Auth actions ─────────────────────────────────────────────── fun logout() { - viewModelScope.launch { - _uiState.update { it.copy(isLoading = true) } - authRepository.logout() - _uiState.update { it.copy(isLoading = false, isLoggedOut = true) } - } + _uiState.update { it.copy(isLoggedOut = true) } } fun deleteAccount(token: String? = null, backupCode: String? = null) { @@ -597,7 +593,6 @@ class SettingsViewModel( _uiState.update { it.copy(isLoading = true) } when (val result = userRepository.deleteUser(token = token, backupCode = backupCode)) { is Result.Success -> { - authRepository.logout() _uiState.update { it.copy(isLoading = false, isAccountDeleted = true, showDeleteAccountOtpDialog = false) } } is Result.Error -> {