Implemented autocomplete list for potential remittees in TransferMoneyDialog

This commit is contained in:
dankito 2020-06-04 13:48:40 +02:00
parent c7845ca779
commit d1bb7d81c3
12 changed files with 105 additions and 21 deletions

View File

@ -16,7 +16,7 @@ compileTestKotlin {
dependencies { dependencies {
implementation project(":BankingUiCommon") implementation project(":BankingUiCommon")
implementation project(":BankingPersistenceJson") api project(":BankingPersistenceJson")
implementation "net.dankito.search:lucene-4-utils:$luceneUtilsVersion" implementation "net.dankito.search:lucene-4-utils:$luceneUtilsVersion"

View File

@ -5,12 +5,11 @@ import androidx.recyclerview.widget.RecyclerView
import com.otaliastudios.autocomplete.RecyclerViewPresenter import com.otaliastudios.autocomplete.RecyclerViewPresenter
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.dankito.banking.ui.android.adapter.RemitteeListAdapter import net.dankito.banking.ui.android.adapter.RemitteeListAdapter
import net.dankito.banking.search.IRemitteeSearcher
import net.dankito.banking.search.Remittee import net.dankito.banking.search.Remittee
import net.dankito.utils.Stopwatch import net.dankito.banking.ui.presenter.BankingPresenter
open class RemitteePresenter(protected val remitteeSearcher: IRemitteeSearcher, context: Context) : RecyclerViewPresenter<Remittee>(context) { open class RemitteePresenter(protected val bankingPresenter: BankingPresenter, context: Context) : RecyclerViewPresenter<Remittee>(context) {
protected val adapter = RemitteeListAdapter { dispatchClick(it) } protected val adapter = RemitteeListAdapter { dispatchClick(it) }
@ -25,7 +24,7 @@ open class RemitteePresenter(protected val remitteeSearcher: IRemitteeSearcher,
lastSearchRemitteeJob?.cancel() lastSearchRemitteeJob?.cancel()
lastSearchRemitteeJob = GlobalScope.launch(Dispatchers.IO) { lastSearchRemitteeJob = GlobalScope.launch(Dispatchers.IO) {
val potentialRemittees = Stopwatch.logDuration("findRemittees()") { remitteeSearcher.findRemittees(query?.toString() ?: "") } val potentialRemittees = bankingPresenter.findRemitteesForName(query?.toString() ?: "")
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
adapter.items = potentialRemittees adapter.items = potentialRemittees

View File

@ -78,9 +78,10 @@ class BankingModule(private val applicationContext: Context) {
@Singleton @Singleton
fun provideBankingPresenter(bankingClientCreator: IBankingClientCreator, bankFinder: IBankFinder, fun provideBankingPresenter(bankingClientCreator: IBankingClientCreator, bankFinder: IBankFinder,
@Named(DatabaseFolderKey) databaseFolder: File, @Named(DataFolderKey) dataFolder: File, @Named(DatabaseFolderKey) databaseFolder: File, @Named(DataFolderKey) dataFolder: File,
persister: IBankingPersistence, bankIconFinder: IBankIconFinder, persister: IBankingPersistence, bankIconFinder: IBankIconFinder, remitteeSearcher: IRemitteeSearcher,
router: IRouter, serializer: ISerializer, threadPool: IThreadPool) : BankingPresenter { router: IRouter, serializer: ISerializer, threadPool: IThreadPool) : BankingPresenter {
return BankingPresenter(bankingClientCreator, bankFinder, databaseFolder, dataFolder, persister, bankIconFinder, router, serializer, threadPool) return BankingPresenter(bankingClientCreator, bankFinder, databaseFolder, dataFolder, persister,
remitteeSearcher, bankIconFinder, router, serializer, threadPool)
} }
@Provides @Provides

View File

@ -60,9 +60,6 @@ open class TransferMoneyDialog : DialogFragment() {
@Inject @Inject
protected lateinit var presenter: BankingPresenter protected lateinit var presenter: BankingPresenter
@Inject
protected lateinit var remitteeSearcher: IRemitteeSearcher
init { init {
BankingComponent.component.inject(this) BankingComponent.component.inject(this)
@ -181,7 +178,7 @@ open class TransferMoneyDialog : DialogFragment() {
.with(6f) .with(6f)
.with(ColorDrawable(Color.WHITE)) .with(ColorDrawable(Color.WHITE))
.with(autocompleteCallback) .with(autocompleteCallback)
.with(RemitteePresenter(remitteeSearcher, edtxtRemitteeName.context)) .with(RemitteePresenter(presenter, edtxtRemitteeName.context))
.build() .build()
.closePopupOnBackButtonPress(dialog) .closePopupOnBackButtonPress(dialog)
} }

View File

@ -24,7 +24,7 @@ dependencies {
implementation project(':fints4kBankingClient') implementation project(':fints4kBankingClient')
implementation project(':hbci4jBankingClient') implementation project(':hbci4jBankingClient')
implementation project(':BankingPersistenceJson') implementation project(':LuceneBankingPersistence')
implementation "ch.qos.logback:logback-classic:$logbackVersion" implementation "ch.qos.logback:logback-classic:$logbackVersion"
} }

View File

@ -2,7 +2,6 @@ package net.dankito.banking.ui.javafx.dialogs.mainwindow
import javafx.scene.control.SplitPane import javafx.scene.control.SplitPane
import net.dankito.banking.fints4kBankingClientCreator import net.dankito.banking.fints4kBankingClientCreator
import net.dankito.banking.persistence.BankingPersistenceJson
import net.dankito.banking.ui.javafx.RouterJavaFx import net.dankito.banking.ui.javafx.RouterJavaFx
import net.dankito.banking.ui.javafx.controls.AccountTransactionsView import net.dankito.banking.ui.javafx.controls.AccountTransactionsView
import net.dankito.banking.ui.javafx.controls.AccountsView import net.dankito.banking.ui.javafx.controls.AccountsView
@ -11,6 +10,8 @@ import net.dankito.banking.ui.javafx.util.Base64ServiceJava8
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.banking.util.BankIconFinder import net.dankito.banking.util.BankIconFinder
import net.dankito.banking.fints.banks.LuceneBankFinder import net.dankito.banking.fints.banks.LuceneBankFinder
import net.dankito.banking.persistence.LuceneBankingPersistence
import net.dankito.banking.search.LuceneRemitteeSearcher
import net.dankito.utils.web.client.OkHttpWebClient import net.dankito.utils.web.client.OkHttpWebClient
import tornadofx.* import tornadofx.*
import tornadofx.FX.Companion.messages import tornadofx.FX.Companion.messages
@ -26,9 +27,10 @@ class MainWindow : View(messages["application.title"]) {
private val indexFolder = File(dataFolder, "index") private val indexFolder = File(dataFolder, "index")
private val presenter = BankingPresenter(fints4kBankingClientCreator(OkHttpWebClient(), Base64ServiceJava8()), private val presenter = BankingPresenter(fints4kBankingClientCreator(OkHttpWebClient(), Base64ServiceJava8()),
LuceneBankFinder(indexFolder), databaseFolder, dataFolder, BankingPersistenceJson(File(databaseFolder, "accounts.json")), BankIconFinder(), RouterJavaFx()) LuceneBankFinder(indexFolder), databaseFolder, dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder),
LuceneRemitteeSearcher(indexFolder), BankIconFinder(), RouterJavaFx())
// private val presenter = BankingPresenter(hbci4jBankingClientCreator(), LuceneBankFinder(indexFolder), databaseFolder, // private val presenter = BankingPresenter(hbci4jBankingClientCreator(), LuceneBankFinder(indexFolder), databaseFolder,
// dataFolder, BankingPersistenceJson(File(databaseFolder, "accounts.json")), BankIconFinder(), RouterJavaFx()) // dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), LuceneRemitteeSearcher(indexFolder), BankIconFinder(), RouterJavaFx())

View File

@ -17,6 +17,7 @@ import net.dankito.banking.ui.model.responses.AddAccountResponse
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.banking.fints.model.BankInfo import net.dankito.banking.fints.model.BankInfo
import net.dankito.banking.ui.javafx.dialogs.addaccount.BankInfoListCellFragment import net.dankito.banking.ui.javafx.dialogs.addaccount.BankInfoListCellFragment
import net.dankito.banking.ui.javafx.extensions.focusNextControl
import net.dankito.utils.javafx.ui.controls.AutoCompletionSearchTextField import net.dankito.utils.javafx.ui.controls.AutoCompletionSearchTextField
import net.dankito.utils.javafx.ui.controls.ProcessingIndicatorButton import net.dankito.utils.javafx.ui.controls.ProcessingIndicatorButton
import net.dankito.utils.javafx.ui.controls.autocompletionsearchtextfield import net.dankito.utils.javafx.ui.controls.autocompletionsearchtextfield
@ -209,7 +210,7 @@ open class AddAccountDialog(protected val presenter: BankingPresenter) : Window(
} }
protected open fun bankSelected(bank: BankInfo) { protected open fun bankSelected(bank: BankInfo) {
unfocusBankCodeTextField() txtfldBankCode.focusNextControl()
selectedBank = bank selectedBank = bank
@ -222,10 +223,6 @@ open class AddAccountDialog(protected val presenter: BankingPresenter) : Window(
} }
} }
protected open fun unfocusBankCodeTextField() {
txtfldBankCode.impl_traverse(Direction.NEXT)
}
protected open fun showBankDoesNotSupportFinTs30ErrorMessage(bank: BankInfo) { protected open fun showBankDoesNotSupportFinTs30ErrorMessage(bank: BankInfo) {
val errorMessage = String.format(messages["add.account.dialog.error.bank.does.not.support.fints.3.error.message"], bank.name) val errorMessage = String.format(messages["add.account.dialog.error.bank.does.not.support.fints.3.error.message"], bank.name)

View File

@ -0,0 +1,21 @@
package net.dankito.banking.ui.javafx.dialogs.cashtransfer
import net.dankito.banking.search.Remittee
import tornadofx.ListCellFragment
import tornadofx.bindTo
import tornadofx.label
import tornadofx.vbox
open class RemitteeListCellFragment : ListCellFragment<Remittee>() {
open val remittee = RemitteeViewModel().bindTo(this)
override val root = vbox {
label(remittee.name)
label(remittee.iban)
}
}

View File

@ -0,0 +1,14 @@
package net.dankito.banking.ui.javafx.dialogs.cashtransfer
import javafx.beans.property.SimpleStringProperty
import net.dankito.banking.search.Remittee
import tornadofx.ItemViewModel
open class RemitteeViewModel : ItemViewModel<Remittee>() {
val name = bind { SimpleStringProperty(item?.name) }
val iban = bind { SimpleStringProperty(item?.iban) }
}

View File

@ -10,6 +10,7 @@ import javafx.geometry.Pos
import javafx.scene.control.ContentDisplay import javafx.scene.control.ContentDisplay
import javafx.scene.image.ImageView import javafx.scene.image.ImageView
import javafx.scene.layout.Priority import javafx.scene.layout.Priority
import kotlinx.coroutines.*
import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService
import net.dankito.banking.ui.model.BankAccount import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.parameters.TransferMoneyData
@ -17,6 +18,10 @@ import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.banking.util.InputValidator import net.dankito.banking.util.InputValidator
import net.dankito.banking.fints.model.BankInfo import net.dankito.banking.fints.model.BankInfo
import net.dankito.banking.search.Remittee
import net.dankito.banking.ui.javafx.extensions.focusNextControl
import net.dankito.utils.javafx.ui.controls.AutoCompletionSearchTextField
import net.dankito.utils.javafx.ui.controls.autocompletionsearchtextfield
import net.dankito.utils.javafx.ui.controls.doubleTextfield import net.dankito.utils.javafx.ui.controls.doubleTextfield
import net.dankito.utils.javafx.ui.dialogs.Window import net.dankito.utils.javafx.ui.dialogs.Window
import net.dankito.utils.javafx.ui.extensions.ensureOnlyUsesSpaceIfVisible import net.dankito.utils.javafx.ui.extensions.ensureOnlyUsesSpaceIfVisible
@ -69,6 +74,11 @@ open class TransferMoneyDialog @JvmOverloads constructor(
protected val requiredDataEntered = SimpleBooleanProperty(false) protected val requiredDataEntered = SimpleBooleanProperty(false)
protected var txtfldRemitteeName: AutoCompletionSearchTextField<Remittee> by singleAssign()
protected var lastSearchRemitteeJob: Job? = null
protected val inputValidator = InputValidator() protected val inputValidator = InputValidator()
protected val dialogService = JavaFxDialogService() protected val dialogService = JavaFxDialogService()
@ -124,8 +134,13 @@ open class TransferMoneyDialog @JvmOverloads constructor(
field(messages["transfer.money.dialog.remittee.name.label"]) { field(messages["transfer.money.dialog.remittee.name.label"]) {
fixedHeight = FieldHeight fixedHeight = FieldHeight
textfield(this@TransferMoneyDialog.remitteeName) { txtfldRemitteeName = autocompletionsearchtextfield(this@TransferMoneyDialog.remitteeName) {
fixedHeight = TextFieldHeight fixedHeight = TextFieldHeight
textProperty().addListener { _, _, newValue -> searchRemittees(newValue) }
onAutoCompletion = { remitteeSelected(it) }
listCellFragment = RemitteeListCellFragment::class
} }
} }
@ -259,6 +274,28 @@ open class TransferMoneyDialog @JvmOverloads constructor(
} }
} }
protected open fun searchRemittees(query: String?) {
lastSearchRemitteeJob?.cancel()
lastSearchRemitteeJob = GlobalScope.launch(Dispatchers.IO) {
val potentialRemittees = presenter.findRemitteesForName(query?.toString() ?: "")
withContext(Dispatchers.Main) {
txtfldRemitteeName.setAutoCompleteList(potentialRemittees)
}
}
}
protected open fun remitteeSelected(remittee: Remittee) {
txtfldRemitteeName.focusNextControl()
remitteeName.value = remittee.name
remitteeBic.value = remittee.bic
remitteeIban.value = remittee.iban
}
protected open fun tryToGetBicFromIban(enteredIban: String) { protected open fun tryToGetBicFromIban(enteredIban: String) {
presenter.findUniqueBankForIbanAsync(enteredIban) { foundBank -> presenter.findUniqueBankForIbanAsync(enteredIban) { foundBank ->
runLater { runLater {

View File

@ -0,0 +1,9 @@
package net.dankito.banking.ui.javafx.extensions
import com.sun.javafx.scene.traversal.Direction
import javafx.scene.control.TextField
fun TextField.focusNextControl() {
this.impl_traverse(Direction.NEXT)
}

View File

@ -17,6 +17,8 @@ import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium
import net.dankito.banking.util.IBankIconFinder import net.dankito.banking.util.IBankIconFinder
import net.dankito.banking.fints.banks.IBankFinder import net.dankito.banking.fints.banks.IBankFinder
import net.dankito.banking.fints.model.BankInfo import net.dankito.banking.fints.model.BankInfo
import net.dankito.banking.search.IRemitteeSearcher
import net.dankito.banking.search.Remittee
import net.dankito.banking.ui.model.parameters.GetTransactionsParameter import net.dankito.banking.ui.model.parameters.GetTransactionsParameter
import net.dankito.banking.ui.model.settings.AppSettings import net.dankito.banking.ui.model.settings.AppSettings
import net.dankito.utils.IThreadPool import net.dankito.utils.IThreadPool
@ -41,6 +43,7 @@ open class BankingPresenter(
protected val databaseFolder: File, protected val databaseFolder: File,
protected val dataFolder: File, protected val dataFolder: File,
protected val persister: IBankingPersistence, protected val persister: IBankingPersistence,
protected val remitteeSearcher: IRemitteeSearcher,
protected val bankIconFinder: IBankIconFinder, protected val bankIconFinder: IBankIconFinder,
protected val router: IRouter, protected val router: IRouter,
protected val serializer: ISerializer = JacksonJsonSerializer(), protected val serializer: ISerializer = JacksonJsonSerializer(),
@ -410,6 +413,10 @@ open class BankingPresenter(
return bankFinder.findBankByNameBankCodeOrCity(query) return bankFinder.findBankByNameBankCodeOrCity(query)
} }
open fun findRemitteesForName(name: String): List<Remittee> {
return remitteeSearcher.findRemittees(name)
}
open fun searchSelectedAccountTransactions(query: String): List<AccountTransaction> { open fun searchSelectedAccountTransactions(query: String): List<AccountTransaction> {
val queryLowercase = query.trim().toLowerCase() val queryLowercase = query.trim().toLowerCase()