fix(#7): emit snackbar as resource id, resolve with stringResource in UI
Drop the Context injection from the ScreenModel. Emit a sealed ImportSnackbarMessage carrying either a @StringRes id or a dynamic text (for the error.message case where the bank importer surfaces a useful 'BRI import not yet implemented' string). The screen resolves resource ids via stringResource() in Composable scope and shows the snackbar; the original error.message info is preserved via the Dynamic variant instead of being dropped.
This commit is contained in:
+14
-1
@@ -86,9 +86,22 @@ object ImportBankStatementScreen : Screen {
|
|||||||
pendingImporter = null
|
pendingImporter = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val importFailedText = stringResource(R.string.import_failed)
|
||||||
|
val importNoTransactionsText = stringResource(R.string.import_no_transactions)
|
||||||
|
val importNoItemsSelectedText = stringResource(R.string.import_no_items_selected)
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
screenModel.snackbar.collect { message ->
|
screenModel.snackbar.collect { message ->
|
||||||
snackbarHostState.showSnackbar(message)
|
val text = when (message) {
|
||||||
|
is ImportSnackbarMessage.Resource -> when (message.id) {
|
||||||
|
R.string.import_failed -> importFailedText
|
||||||
|
R.string.import_no_transactions -> importNoTransactionsText
|
||||||
|
R.string.import_no_items_selected -> importNoItemsSelectedText
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
is ImportSnackbarMessage.Dynamic -> message.text
|
||||||
|
}
|
||||||
|
text?.let { snackbarHostState.showSnackbar(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-7
@@ -1,7 +1,7 @@
|
|||||||
package dev.achmad.ledgerr.ui.screens.import_bank_statement
|
package dev.achmad.ledgerr.ui.screens.import_bank_statement
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import cafe.adriel.voyager.core.model.ScreenModel
|
import cafe.adriel.voyager.core.model.ScreenModel
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
import cafe.adriel.voyager.navigator.Navigator
|
import cafe.adriel.voyager.navigator.Navigator
|
||||||
@@ -36,18 +36,22 @@ sealed interface ImportState {
|
|||||||
) : ImportState
|
) : ImportState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed interface ImportSnackbarMessage {
|
||||||
|
data class Resource(@StringRes val id: Int) : ImportSnackbarMessage
|
||||||
|
data class Dynamic(val text: String) : ImportSnackbarMessage
|
||||||
|
}
|
||||||
|
|
||||||
class ImportBankStatementScreenModel(
|
class ImportBankStatementScreenModel(
|
||||||
private val importers: List<BankStatementImporter> = inject(),
|
private val importers: List<BankStatementImporter> = inject(),
|
||||||
private val getCategories: GetCategories = inject(),
|
private val getCategories: GetCategories = inject(),
|
||||||
private val insertExpenses: InsertExpenses = inject(),
|
private val insertExpenses: InsertExpenses = inject(),
|
||||||
private val context: Context = inject(),
|
|
||||||
) : ScreenModel {
|
) : ScreenModel {
|
||||||
|
|
||||||
private val _state = MutableStateFlow<ImportState>(ImportState.BankPicker(importers))
|
private val _state = MutableStateFlow<ImportState>(ImportState.BankPicker(importers))
|
||||||
val state: StateFlow<ImportState> = _state.asStateFlow()
|
val state: StateFlow<ImportState> = _state.asStateFlow()
|
||||||
|
|
||||||
private val _snackbar = MutableSharedFlow<String>(extraBufferCapacity = 1)
|
private val _snackbar = MutableSharedFlow<ImportSnackbarMessage>(extraBufferCapacity = 1)
|
||||||
val snackbar: SharedFlow<String> = _snackbar.asSharedFlow()
|
val snackbar: SharedFlow<ImportSnackbarMessage> = _snackbar.asSharedFlow()
|
||||||
|
|
||||||
val categories: StateFlow<List<Category>> = getCategories.subscribeAll()
|
val categories: StateFlow<List<Category>> = getCategories.subscribeAll()
|
||||||
.flowOn(Dispatchers.IO)
|
.flowOn(Dispatchers.IO)
|
||||||
@@ -69,7 +73,7 @@ class ImportBankStatementScreenModel(
|
|||||||
.onSuccess { rows ->
|
.onSuccess { rows ->
|
||||||
if (rows.isEmpty()) {
|
if (rows.isEmpty()) {
|
||||||
_state.value = ImportState.BankPicker(importers)
|
_state.value = ImportState.BankPicker(importers)
|
||||||
_snackbar.tryEmit(context.getString(R.string.import_no_transactions))
|
_snackbar.tryEmit(ImportSnackbarMessage.Resource(R.string.import_no_transactions))
|
||||||
} else {
|
} else {
|
||||||
_state.value = ImportState.Confirmation(importer.bankName, rows)
|
_state.value = ImportState.Confirmation(importer.bankName, rows)
|
||||||
}
|
}
|
||||||
@@ -77,7 +81,8 @@ class ImportBankStatementScreenModel(
|
|||||||
.onFailure { error ->
|
.onFailure { error ->
|
||||||
_state.value = ImportState.BankPicker(importers)
|
_state.value = ImportState.BankPicker(importers)
|
||||||
val message = error.message?.takeIf { it.isNotBlank() }
|
val message = error.message?.takeIf { it.isNotBlank() }
|
||||||
?: context.getString(R.string.import_failed)
|
?.let { ImportSnackbarMessage.Dynamic(it) }
|
||||||
|
?: ImportSnackbarMessage.Resource(R.string.import_failed)
|
||||||
_snackbar.tryEmit(message)
|
_snackbar.tryEmit(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +120,7 @@ class ImportBankStatementScreenModel(
|
|||||||
val confirmation = _state.value as? ImportState.Confirmation ?: return@launch
|
val confirmation = _state.value as? ImportState.Confirmation ?: return@launch
|
||||||
val selected = confirmation.rows.filter { it.isSelected }
|
val selected = confirmation.rows.filter { it.isSelected }
|
||||||
if (selected.isEmpty()) {
|
if (selected.isEmpty()) {
|
||||||
_snackbar.tryEmit(context.getString(R.string.import_no_items_selected))
|
_snackbar.tryEmit(ImportSnackbarMessage.Resource(R.string.import_no_items_selected))
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
|||||||
Reference in New Issue
Block a user