From d1bb7d81c355769a01ba4a8d99bc8e739d1cf14e Mon Sep 17 00:00:00 2001 From: dankito Date: Thu, 4 Jun 2020 13:48:40 +0200 Subject: [PATCH] Implemented autocomplete list for potential remittees in TransferMoneyDialog --- .../LuceneBankingPersistence/build.gradle | 2 +- .../adapter/presenter/RemitteePresenter.kt | 7 ++-- .../banking/ui/android/di/BankingModule.kt | 5 ++- .../ui/android/dialogs/TransferMoneyDialog.kt | 5 +-- ui/BankingJavaFxApp/build.gradle | 2 +- .../javafx/dialogs/mainwindow/MainWindow.kt | 8 ++-- .../ui/javafx/dialogs/AddAccountDialog.kt | 7 +--- .../cashtransfer/RemitteeListCellFragment.kt | 21 ++++++++++ .../dialogs/cashtransfer/RemitteeViewModel.kt | 14 +++++++ .../cashtransfer/TransferMoneyDialog.kt | 39 ++++++++++++++++++- .../javafx/extensions/TextfieldExtensions.kt | 9 +++++ .../banking/ui/presenter/BankingPresenter.kt | 7 ++++ 12 files changed, 105 insertions(+), 21 deletions(-) create mode 100644 ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/RemitteeListCellFragment.kt create mode 100644 ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/RemitteeViewModel.kt create mode 100644 ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/extensions/TextfieldExtensions.kt diff --git a/persistence/LuceneBankingPersistence/build.gradle b/persistence/LuceneBankingPersistence/build.gradle index e851d381..fe208776 100644 --- a/persistence/LuceneBankingPersistence/build.gradle +++ b/persistence/LuceneBankingPersistence/build.gradle @@ -16,7 +16,7 @@ compileTestKotlin { dependencies { implementation project(":BankingUiCommon") - implementation project(":BankingPersistenceJson") + api project(":BankingPersistenceJson") implementation "net.dankito.search:lucene-4-utils:$luceneUtilsVersion" diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/presenter/RemitteePresenter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/presenter/RemitteePresenter.kt index 0895fe62..cb57ed57 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/presenter/RemitteePresenter.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/presenter/RemitteePresenter.kt @@ -5,12 +5,11 @@ import androidx.recyclerview.widget.RecyclerView import com.otaliastudios.autocomplete.RecyclerViewPresenter import kotlinx.coroutines.* import net.dankito.banking.ui.android.adapter.RemitteeListAdapter -import net.dankito.banking.search.IRemitteeSearcher 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(context) { +open class RemitteePresenter(protected val bankingPresenter: BankingPresenter, context: Context) : RecyclerViewPresenter(context) { protected val adapter = RemitteeListAdapter { dispatchClick(it) } @@ -25,7 +24,7 @@ open class RemitteePresenter(protected val remitteeSearcher: IRemitteeSearcher, lastSearchRemitteeJob?.cancel() lastSearchRemitteeJob = GlobalScope.launch(Dispatchers.IO) { - val potentialRemittees = Stopwatch.logDuration("findRemittees()") { remitteeSearcher.findRemittees(query?.toString() ?: "") } + val potentialRemittees = bankingPresenter.findRemitteesForName(query?.toString() ?: "") withContext(Dispatchers.Main) { adapter.items = potentialRemittees diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt index 4657d165..944613ef 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt @@ -78,9 +78,10 @@ class BankingModule(private val applicationContext: Context) { @Singleton fun provideBankingPresenter(bankingClientCreator: IBankingClientCreator, bankFinder: IBankFinder, @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 { - return BankingPresenter(bankingClientCreator, bankFinder, databaseFolder, dataFolder, persister, bankIconFinder, router, serializer, threadPool) + return BankingPresenter(bankingClientCreator, bankFinder, databaseFolder, dataFolder, persister, + remitteeSearcher, bankIconFinder, router, serializer, threadPool) } @Provides diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/TransferMoneyDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/TransferMoneyDialog.kt index e96ace5b..88a34773 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/TransferMoneyDialog.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/TransferMoneyDialog.kt @@ -60,9 +60,6 @@ open class TransferMoneyDialog : DialogFragment() { @Inject protected lateinit var presenter: BankingPresenter - @Inject - protected lateinit var remitteeSearcher: IRemitteeSearcher - init { BankingComponent.component.inject(this) @@ -181,7 +178,7 @@ open class TransferMoneyDialog : DialogFragment() { .with(6f) .with(ColorDrawable(Color.WHITE)) .with(autocompleteCallback) - .with(RemitteePresenter(remitteeSearcher, edtxtRemitteeName.context)) + .with(RemitteePresenter(presenter, edtxtRemitteeName.context)) .build() .closePopupOnBackButtonPress(dialog) } diff --git a/ui/BankingJavaFxApp/build.gradle b/ui/BankingJavaFxApp/build.gradle index 7a8f1b55..73c9f666 100644 --- a/ui/BankingJavaFxApp/build.gradle +++ b/ui/BankingJavaFxApp/build.gradle @@ -24,7 +24,7 @@ dependencies { implementation project(':fints4kBankingClient') implementation project(':hbci4jBankingClient') - implementation project(':BankingPersistenceJson') + implementation project(':LuceneBankingPersistence') implementation "ch.qos.logback:logback-classic:$logbackVersion" } diff --git a/ui/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt b/ui/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt index a867c170..a5864c7e 100755 --- a/ui/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt +++ b/ui/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt @@ -2,7 +2,6 @@ package net.dankito.banking.ui.javafx.dialogs.mainwindow import javafx.scene.control.SplitPane import net.dankito.banking.fints4kBankingClientCreator -import net.dankito.banking.persistence.BankingPersistenceJson import net.dankito.banking.ui.javafx.RouterJavaFx import net.dankito.banking.ui.javafx.controls.AccountTransactionsView 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.util.BankIconFinder 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 tornadofx.* import tornadofx.FX.Companion.messages @@ -26,9 +27,10 @@ class MainWindow : View(messages["application.title"]) { private val indexFolder = File(dataFolder, "index") 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, -// dataFolder, BankingPersistenceJson(File(databaseFolder, "accounts.json")), BankIconFinder(), RouterJavaFx()) +// dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), LuceneRemitteeSearcher(indexFolder), BankIconFinder(), RouterJavaFx()) diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/AddAccountDialog.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/AddAccountDialog.kt index 4e3295fe..8f20f6a4 100755 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/AddAccountDialog.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/AddAccountDialog.kt @@ -17,6 +17,7 @@ import net.dankito.banking.ui.model.responses.AddAccountResponse import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.fints.model.BankInfo 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.ProcessingIndicatorButton 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) { - unfocusBankCodeTextField() + txtfldBankCode.focusNextControl() 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) { val errorMessage = String.format(messages["add.account.dialog.error.bank.does.not.support.fints.3.error.message"], bank.name) diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/RemitteeListCellFragment.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/RemitteeListCellFragment.kt new file mode 100644 index 00000000..e43d362c --- /dev/null +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/RemitteeListCellFragment.kt @@ -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() { + + open val remittee = RemitteeViewModel().bindTo(this) + + + override val root = vbox { + label(remittee.name) + + label(remittee.iban) + } + +} \ No newline at end of file diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/RemitteeViewModel.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/RemitteeViewModel.kt new file mode 100644 index 00000000..05920da8 --- /dev/null +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/RemitteeViewModel.kt @@ -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() { + + val name = bind { SimpleStringProperty(item?.name) } + + val iban = bind { SimpleStringProperty(item?.iban) } + +} \ No newline at end of file diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt index d5830c14..860e22ae 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt @@ -10,6 +10,7 @@ import javafx.geometry.Pos import javafx.scene.control.ContentDisplay import javafx.scene.image.ImageView import javafx.scene.layout.Priority +import kotlinx.coroutines.* import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService import net.dankito.banking.ui.model.BankAccount 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.util.InputValidator 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.dialogs.Window import net.dankito.utils.javafx.ui.extensions.ensureOnlyUsesSpaceIfVisible @@ -69,6 +74,11 @@ open class TransferMoneyDialog @JvmOverloads constructor( protected val requiredDataEntered = SimpleBooleanProperty(false) + protected var txtfldRemitteeName: AutoCompletionSearchTextField by singleAssign() + + protected var lastSearchRemitteeJob: Job? = null + + protected val inputValidator = InputValidator() protected val dialogService = JavaFxDialogService() @@ -124,8 +134,13 @@ open class TransferMoneyDialog @JvmOverloads constructor( field(messages["transfer.money.dialog.remittee.name.label"]) { fixedHeight = FieldHeight - textfield(this@TransferMoneyDialog.remitteeName) { + txtfldRemitteeName = autocompletionsearchtextfield(this@TransferMoneyDialog.remitteeName) { 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) { presenter.findUniqueBankForIbanAsync(enteredIban) { foundBank -> runLater { diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/extensions/TextfieldExtensions.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/extensions/TextfieldExtensions.kt new file mode 100644 index 00000000..6a4719b9 --- /dev/null +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/extensions/TextfieldExtensions.kt @@ -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) +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/presenter/BankingPresenter.kt b/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/presenter/BankingPresenter.kt index 69503bfe..4ee646ed 100644 --- a/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/presenter/BankingPresenter.kt +++ b/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/presenter/BankingPresenter.kt @@ -17,6 +17,8 @@ import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium import net.dankito.banking.util.IBankIconFinder import net.dankito.banking.fints.banks.IBankFinder 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.settings.AppSettings import net.dankito.utils.IThreadPool @@ -41,6 +43,7 @@ open class BankingPresenter( protected val databaseFolder: File, protected val dataFolder: File, protected val persister: IBankingPersistence, + protected val remitteeSearcher: IRemitteeSearcher, protected val bankIconFinder: IBankIconFinder, protected val router: IRouter, protected val serializer: ISerializer = JacksonJsonSerializer(), @@ -410,6 +413,10 @@ open class BankingPresenter( return bankFinder.findBankByNameBankCodeOrCity(query) } + open fun findRemitteesForName(name: String): List { + return remitteeSearcher.findRemittees(name) + } + open fun searchSelectedAccountTransactions(query: String): List { val queryLowercase = query.trim().toLowerCase()