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
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.annotation.StringRes
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import cafe.adriel.voyager.navigator.Navigator
|
||||
@@ -36,18 +36,22 @@ sealed interface ImportState {
|
||||
) : ImportState
|
||||
}
|
||||
|
||||
sealed interface ImportSnackbarMessage {
|
||||
data class Resource(@StringRes val id: Int) : ImportSnackbarMessage
|
||||
data class Dynamic(val text: String) : ImportSnackbarMessage
|
||||
}
|
||||
|
||||
class ImportBankStatementScreenModel(
|
||||
private val importers: List<BankStatementImporter> = inject(),
|
||||
private val getCategories: GetCategories = inject(),
|
||||
private val insertExpenses: InsertExpenses = inject(),
|
||||
private val context: Context = inject(),
|
||||
) : ScreenModel {
|
||||
|
||||
private val _state = MutableStateFlow<ImportState>(ImportState.BankPicker(importers))
|
||||
val state: StateFlow<ImportState> = _state.asStateFlow()
|
||||
|
||||
private val _snackbar = MutableSharedFlow<String>(extraBufferCapacity = 1)
|
||||
val snackbar: SharedFlow<String> = _snackbar.asSharedFlow()
|
||||
private val _snackbar = MutableSharedFlow<ImportSnackbarMessage>(extraBufferCapacity = 1)
|
||||
val snackbar: SharedFlow<ImportSnackbarMessage> = _snackbar.asSharedFlow()
|
||||
|
||||
val categories: StateFlow<List<Category>> = getCategories.subscribeAll()
|
||||
.flowOn(Dispatchers.IO)
|
||||
@@ -69,7 +73,7 @@ class ImportBankStatementScreenModel(
|
||||
.onSuccess { rows ->
|
||||
if (rows.isEmpty()) {
|
||||
_state.value = ImportState.BankPicker(importers)
|
||||
_snackbar.tryEmit(context.getString(R.string.import_no_transactions))
|
||||
_snackbar.tryEmit(ImportSnackbarMessage.Resource(R.string.import_no_transactions))
|
||||
} else {
|
||||
_state.value = ImportState.Confirmation(importer.bankName, rows)
|
||||
}
|
||||
@@ -77,7 +81,8 @@ class ImportBankStatementScreenModel(
|
||||
.onFailure { error ->
|
||||
_state.value = ImportState.BankPicker(importers)
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -115,7 +120,7 @@ class ImportBankStatementScreenModel(
|
||||
val confirmation = _state.value as? ImportState.Confirmation ?: return@launch
|
||||
val selected = confirmation.rows.filter { it.isSelected }
|
||||
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
|
||||
}
|
||||
withContext(Dispatchers.IO) {
|
||||
|
||||
Reference in New Issue
Block a user