diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/auth/SessionManager.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/auth/SessionManager.kt index 572c92e..420f4be 100644 --- a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/auth/SessionManager.kt +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/auth/SessionManager.kt @@ -4,19 +4,22 @@ import com.vaultdrop.mobile.data.remote.ApiClient import com.vaultdrop.mobile.data.remote.dto.LoginResponseDto import com.vaultdrop.mobile.domain.ActiveUserStore import com.vaultdrop.mobile.domain.DeviceIdentity +import com.vaultdrop.mobile.domain.LocalModeStore import javax.inject.Inject import javax.inject.Singleton /** * Orchestration de la session (restauration/sauvegarde/purge) : relie le store * sécurisé, le miroir `active_user_id` et le token en mémoire lu par - * `AuthInterceptor`. + * `AuthInterceptor`. Le drapeau « mode local » est persisté à part + * ([LocalModeStore]) pour survivre aux redémarrages. */ @Singleton class SessionManager @Inject constructor( private val secureTokenStore: SecureTokenStore, private val activeUserStore: ActiveUserStore, private val tokenProvider: TokenProvider, + private val localModeStore: LocalModeStore, ) { /** Restaure la session persistée (si présente), sinon null. */ @@ -30,6 +33,7 @@ class SessionManager @Inject constructor( suspend fun save(token: String, user: com.vaultdrop.mobile.data.remote.dto.UserDto) { secureTokenStore.save(token, user) activeUserStore.set(user.id) + localModeStore.clear() tokenProvider.current = token } @@ -38,6 +42,12 @@ class SessionManager @Inject constructor( activeUserStore.clear() tokenProvider.current = null } + + suspend fun isLocalMode(): Boolean = localModeStore.get() + + suspend fun setLocalMode() = localModeStore.set() + + suspend fun clearLocalMode() = localModeStore.clear() } data class AuthSession( diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/data/local/entity/UserPreferenceEntity.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/data/local/entity/UserPreferenceEntity.kt index bb05b40..f35d297 100644 --- a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/data/local/entity/UserPreferenceEntity.kt +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/data/local/entity/UserPreferenceEntity.kt @@ -7,7 +7,8 @@ import androidx.room.PrimaryKey /** * Miroir de la table `user_preferences` JS (`key`/`value`/`updated_at`). * Clés connues : `device_user_id` (32-hex, généré une fois), `sync_mode`, - * `active_user_id` (miroir non-sensible du compte connecté). + * `active_user_id` (miroir non-sensible du compte connecté), `local_mode` + * (app utilisée sans compte). */ @Entity(tableName = "user_preferences") data class UserPreferenceEntity( diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/domain/PrefKeys.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/domain/PrefKeys.kt index 031b37b..efd4ef9 100644 --- a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/domain/PrefKeys.kt +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/domain/PrefKeys.kt @@ -4,6 +4,7 @@ package com.vaultdrop.mobile.domain object PrefKeys { const val DEVICE_USER_ID = "device_user_id" const val ACTIVE_USER_ID = "active_user_id" + const val LOCAL_MODE = "local_mode" const val SYNC_MODE = "sync_mode" const val THEME = "theme" const val SERVER_URL = "server_url" diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/auth/AuthViewModel.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/auth/AuthViewModel.kt index 2ead6c7..ad3e4e9 100644 --- a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/auth/AuthViewModel.kt +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/auth/AuthViewModel.kt @@ -37,8 +37,11 @@ class AuthViewModel @Inject constructor( viewModelScope.launch { val session = sessionManager.restore() - _authState.value = - if (session != null) AuthState.SignedIn(session.user) else AuthState.SignedOut + _authState.value = when { + session != null -> AuthState.SignedIn(session.user) + sessionManager.isLocalMode() -> AuthState.Local + else -> AuthState.SignedOut + } authRepository.registerDevice() } } @@ -67,7 +70,10 @@ class AuthViewModel @Inject constructor( } fun continueWithoutAccount() { - _authState.value = AuthState.Local + viewModelScope.launch { + sessionManager.setLocalMode() + _authState.value = AuthState.Local + } } /** Purge la session et revient en mode local (sans repasser par le login). */ @@ -76,6 +82,7 @@ class AuthViewModel @Inject constructor( Timber.d("auth: session purgée (mode local)") withContext(Dispatchers.IO) { sessionManager.clear() + sessionManager.setLocalMode() } _authState.value = AuthState.Local } @@ -86,6 +93,7 @@ class AuthViewModel @Inject constructor( Timber.d("auth: session purgée (signOut)") withContext(Dispatchers.IO) { sessionManager.clear() + sessionManager.clearLocalMode() } _authState.value = AuthState.SignedOut }