feat(auth): show delete-local-data dialog on logout for logged-in users
This commit is contained in:
parent
320f0e8880
commit
557a4bcaf8
4 changed files with 47 additions and 4 deletions
|
|
@ -324,13 +324,33 @@ internal fun SettingsScreen(
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
onClick = viewModel::logout,
|
onClick = viewModel::onLogoutClicked,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Text("Abmelden")
|
Text("Abmelden")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (uiState.showLogoutDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = viewModel::onLogoutDialogDismissed,
|
||||||
|
title = { Text("Lokale Daten löschen?") },
|
||||||
|
text = {
|
||||||
|
Text("Möchtest du alle lokal gespeicherten Daten (Inventar, Nachrichten, …) von diesem Gerät entfernen?")
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = { viewModel.logout(deleteLocalData = true) }) {
|
||||||
|
Text("Löschen")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { viewModel.logout(deleteLocalData = false) }) {
|
||||||
|
Text("Behalten")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = uiState.loginUsername,
|
value = uiState.loginUsername,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ internal data class SettingsUiState(
|
||||||
val pendingQueueCount: Int = 0,
|
val pendingQueueCount: Int = 0,
|
||||||
val isSyncing: Boolean = false,
|
val isSyncing: Boolean = false,
|
||||||
val lastSyncTime: String? = null,
|
val lastSyncTime: String? = null,
|
||||||
val openAiApiKey: String = ""
|
val openAiApiKey: String = "",
|
||||||
|
val showLogoutDialog: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
internal sealed interface ImportResult {
|
internal sealed interface ImportResult {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import de.bollwerk.app.data.db.BollwerkDatabase
|
||||||
import de.bollwerk.app.data.db.dao.PendingSyncOpDao
|
import de.bollwerk.app.data.db.dao.PendingSyncOpDao
|
||||||
import de.bollwerk.app.data.sync.ConnectionState
|
import de.bollwerk.app.data.sync.ConnectionState
|
||||||
import de.bollwerk.app.data.sync.WebSocketClient
|
import de.bollwerk.app.data.sync.WebSocketClient
|
||||||
|
|
@ -22,6 +23,7 @@ import de.bollwerk.app.domain.repository.ImportExportRepository
|
||||||
import de.bollwerk.app.domain.repository.SettingsRepository
|
import de.bollwerk.app.domain.repository.SettingsRepository
|
||||||
import de.bollwerk.app.domain.repository.SyncService
|
import de.bollwerk.app.domain.repository.SyncService
|
||||||
import de.bollwerk.app.notification.MessagingService
|
import de.bollwerk.app.notification.MessagingService
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
@ -29,6 +31,7 @@ import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
|
|
@ -43,6 +46,7 @@ internal class SettingsViewModel @Inject constructor(
|
||||||
private val syncService: SyncService,
|
private val syncService: SyncService,
|
||||||
private val webSocketClient: WebSocketClient,
|
private val webSocketClient: WebSocketClient,
|
||||||
private val pendingSyncOpDao: PendingSyncOpDao,
|
private val pendingSyncOpDao: PendingSyncOpDao,
|
||||||
|
private val database: BollwerkDatabase,
|
||||||
@ApplicationContext private val context: Context
|
@ApplicationContext private val context: Context
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
|
@ -227,8 +231,21 @@ internal class SettingsViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun logout() {
|
fun onLogoutClicked() {
|
||||||
|
_uiState.update { it.copy(showLogoutDialog = true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onLogoutDialogDismissed() {
|
||||||
|
_uiState.update { it.copy(showLogoutDialog = false) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logout(deleteLocalData: Boolean = false) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
if (deleteLocalData) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
database.clearAllTables()
|
||||||
|
}
|
||||||
|
}
|
||||||
syncService.logout()
|
syncService.logout()
|
||||||
webSocketClient.disconnect()
|
webSocketClient.disconnect()
|
||||||
MessagingService.stop(context)
|
MessagingService.stop(context)
|
||||||
|
|
@ -237,7 +254,8 @@ internal class SettingsViewModel @Inject constructor(
|
||||||
isLoggedIn = false,
|
isLoggedIn = false,
|
||||||
loggedInUsername = "",
|
loggedInUsername = "",
|
||||||
connectionState = ConnectionState.NotConfigured,
|
connectionState = ConnectionState.NotConfigured,
|
||||||
syncActivity = null
|
syncActivity = null,
|
||||||
|
showLogoutDialog = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import de.bollwerk.app.data.db.BollwerkDatabase
|
||||||
import de.bollwerk.app.data.db.dao.PendingSyncOpDao
|
import de.bollwerk.app.data.db.dao.PendingSyncOpDao
|
||||||
import de.bollwerk.app.data.db.entity.PendingSyncOpEntity
|
import de.bollwerk.app.data.db.entity.PendingSyncOpEntity
|
||||||
import de.bollwerk.app.data.sync.WebSocketClient
|
import de.bollwerk.app.data.sync.WebSocketClient
|
||||||
|
|
@ -53,6 +54,7 @@ class SettingsViewModelTest {
|
||||||
private lateinit var fakeSyncService: FakeSyncService
|
private lateinit var fakeSyncService: FakeSyncService
|
||||||
private lateinit var fakeWebSocketClient: FakeWebSocketClient
|
private lateinit var fakeWebSocketClient: FakeWebSocketClient
|
||||||
private lateinit var fakePendingSyncOpDao: FakePendingSyncOpDao
|
private lateinit var fakePendingSyncOpDao: FakePendingSyncOpDao
|
||||||
|
private lateinit var mockDatabase: BollwerkDatabase
|
||||||
private lateinit var mockContext: Context
|
private lateinit var mockContext: Context
|
||||||
private lateinit var tempDir: File
|
private lateinit var tempDir: File
|
||||||
private lateinit var viewModel: SettingsViewModel
|
private lateinit var viewModel: SettingsViewModel
|
||||||
|
|
@ -65,6 +67,7 @@ class SettingsViewModelTest {
|
||||||
fakeSyncService = FakeSyncService()
|
fakeSyncService = FakeSyncService()
|
||||||
fakeWebSocketClient = FakeWebSocketClient()
|
fakeWebSocketClient = FakeWebSocketClient()
|
||||||
fakePendingSyncOpDao = FakePendingSyncOpDao()
|
fakePendingSyncOpDao = FakePendingSyncOpDao()
|
||||||
|
mockDatabase = mockk(relaxed = true)
|
||||||
tempDir = File(System.getProperty("java.io.tmpdir"), "test_exports")
|
tempDir = File(System.getProperty("java.io.tmpdir"), "test_exports")
|
||||||
tempDir.mkdirs()
|
tempDir.mkdirs()
|
||||||
mockContext = mockk(relaxed = true) {
|
mockContext = mockk(relaxed = true) {
|
||||||
|
|
@ -85,6 +88,7 @@ class SettingsViewModelTest {
|
||||||
syncService = fakeSyncService,
|
syncService = fakeSyncService,
|
||||||
webSocketClient = fakeWebSocketClient,
|
webSocketClient = fakeWebSocketClient,
|
||||||
pendingSyncOpDao = fakePendingSyncOpDao,
|
pendingSyncOpDao = fakePendingSyncOpDao,
|
||||||
|
database = mockDatabase,
|
||||||
context = mockContext
|
context = mockContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue