refactor: session/auth в MainViewModel (Этап 2 п.10, ч.2)
Верхнеуровневый session-жизненный цикл вынесен из AppScaffold в MainViewModel
(AndroidViewModel): держит session (StateFlow), login()=save+set,
logout()=ревокация app password + OfficeWarmup.clear + WebViewPool.dispose +
AuthStore.clear. AppScaffold читает vm.session.collectAsState(), forceLogout=
{vm.logout()}. Бонус: сессия переживает пересоздание Activity (поворот) —
раньше жила в remember и перечитывалась из AuthStore. UI-тумблеры (menuOpen,
sidebar/settings-флаги, pending* deep-link) осознанно оставлены в composable —
это чистое UI-состояние, не бизнес-логика.
This commit is contained in:
@@ -50,12 +50,10 @@ import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.tasks.await
|
||||
import ru.forbion.f7cloud.mobile.BuildConfig
|
||||
import ru.forbion.f7cloud.core.auth.AppLockStore
|
||||
import ru.forbion.f7cloud.core.auth.AppPasswordRevoker
|
||||
import ru.forbion.f7cloud.core.auth.AuthSession
|
||||
import ru.forbion.f7cloud.core.auth.AuthStore
|
||||
import ru.forbion.f7cloud.core.auth.AuthVerifier
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import ru.forbion.f7cloud.core.network.LoginFlowClient
|
||||
import ru.forbion.f7cloud.core.push.F7PushEvent
|
||||
import ru.forbion.f7cloud.core.push.F7PushEventHub
|
||||
@@ -68,12 +66,10 @@ import ru.forbion.f7cloud.core.designsystem.F7BottomBarActions
|
||||
import ru.forbion.f7cloud.core.designsystem.F7BottomBarConfig
|
||||
import ru.forbion.f7cloud.core.designsystem.F7MobileBottomBar
|
||||
import ru.forbion.f7cloud.core.designsystem.F7Colors
|
||||
import ru.forbion.f7cloud.core.designsystem.F7TextButton
|
||||
import ru.forbion.f7cloud.core.designsystem.F7Theme
|
||||
import ru.forbion.f7cloud.core.designsystem.f7SafeTopInsets
|
||||
import ru.forbion.f7cloud.mobile.OfficeEditorActivity
|
||||
import ru.forbion.f7cloud.mobile.qr.F7QrScannerActivity
|
||||
import ru.forbion.f7cloud.mobile.OfficeWebViewPool
|
||||
import ru.forbion.f7cloud.feature.files.OfficeWarmup
|
||||
import ru.forbion.f7cloud.mobile.permissions.F7AppPermissions
|
||||
import ru.forbion.f7cloud.mobile.permissions.F7PermissionRationaleDialog
|
||||
@@ -95,8 +91,8 @@ fun AppScaffold(
|
||||
onRequestRuntimePermissions: (onFinished: (() -> Unit)?) -> Unit = {},
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val authStore = remember { AuthStore(context) }
|
||||
var session by remember { mutableStateOf(authStore.load()) }
|
||||
val mainVm: MainViewModel = viewModel()
|
||||
val session by mainVm.session.collectAsState()
|
||||
var activeTab by rememberSaveable(
|
||||
saver = Saver(
|
||||
save = { state -> state.value.name },
|
||||
@@ -151,17 +147,7 @@ fun AppScaffold(
|
||||
context.applicationContext.getSharedPreferences("f7_permissions", android.content.Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
val logoutScope = rememberCoroutineScope()
|
||||
val forceLogout = {
|
||||
// Best-effort ревокация app password на сервере ДО очистки локальной сессии.
|
||||
session?.let { current ->
|
||||
logoutScope.launch { AppPasswordRevoker.revoke(current) }
|
||||
}
|
||||
OfficeWarmup.clear()
|
||||
OfficeWebViewPool.dispose()
|
||||
authStore.clear()
|
||||
session = null
|
||||
}
|
||||
val forceLogout = { mainVm.logout() }
|
||||
LaunchedEffect(session?.serverUrl, session?.username) {
|
||||
session?.let { OfficeWarmup.warm(it) }
|
||||
}
|
||||
@@ -174,10 +160,7 @@ fun AppScaffold(
|
||||
|
||||
F7Theme {
|
||||
if (session == null) {
|
||||
LoginScreen(onLogin = {
|
||||
authStore.save(it)
|
||||
session = it
|
||||
})
|
||||
LoginScreen(onLogin = { mainVm.login(it) })
|
||||
return@F7Theme
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package ru.forbion.f7cloud.mobile.ui
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.forbion.f7cloud.core.auth.AppPasswordRevoker
|
||||
import ru.forbion.f7cloud.core.auth.AuthSession
|
||||
import ru.forbion.f7cloud.core.auth.AuthStore
|
||||
import ru.forbion.f7cloud.feature.files.OfficeWarmup
|
||||
import ru.forbion.f7cloud.mobile.OfficeWebViewPool
|
||||
|
||||
/**
|
||||
* Держит session/auth-жизненный цикл верхнего уровня (вынесено из AppScaffold).
|
||||
* В ViewModel сессия переживает пересоздание Activity (поворот и т.п.) — раньше
|
||||
* жила в remember и перечитывалась из AuthStore.
|
||||
*/
|
||||
class MainViewModel(app: Application) : AndroidViewModel(app) {
|
||||
private val authStore = AuthStore(app)
|
||||
private val _session = MutableStateFlow(authStore.load())
|
||||
val session: StateFlow<AuthSession?> = _session.asStateFlow()
|
||||
|
||||
fun login(newSession: AuthSession) {
|
||||
authStore.save(newSession)
|
||||
_session.value = newSession
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
// Best-effort ревокация app password на сервере ДО очистки локальной сессии.
|
||||
_session.value?.let { current ->
|
||||
viewModelScope.launch { runCatching { AppPasswordRevoker.revoke(current) } }
|
||||
}
|
||||
OfficeWarmup.clear()
|
||||
OfficeWebViewPool.dispose()
|
||||
authStore.clear()
|
||||
_session.value = null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user