Implemented showing fetch all transactions view at top and by clicking on 'x' button showing it at bottom (TODO on Android: show at bottom)

This commit is contained in:
dankito 2020-09-27 00:32:18 +02:00
parent e8aee07b34
commit 3be297c0d3
4 changed files with 200 additions and 80 deletions

View File

@ -52,6 +52,8 @@ class HomeFragment : Fragment() {
private var accountsForWhichNotAllTransactionsHaveBeenFetched = listOf<TypedBankAccount>() private var accountsForWhichNotAllTransactionsHaveBeenFetched = listOf<TypedBankAccount>()
private var showTopFetchAllTransactionsView = true // TODO: read from db
private val transactionAdapter: AccountTransactionAdapter private val transactionAdapter: AccountTransactionAdapter
@ -92,10 +94,18 @@ class HomeFragment : Fragment() {
registerForContextMenu(rcyvwAccountTransactions) // this is actually bad, splits code as context menu is created in AccountTransactionAdapter registerForContextMenu(rcyvwAccountTransactions) // this is actually bad, splits code as context menu is created in AccountTransactionAdapter
rootView.btnFetchAllTransactions.setOnClickListener { rootView.btnTopFetchAllTransactions.setOnClickListener {
fetchAllTransactions() fetchAllTransactions()
} }
rootView.btnBottomFetchAllTransactions.setOnClickListener {
fetchAllTransactions()
}
rootView.btnHideTopFetchAllTransactionsView.setOnClickListener {
hideTopFetchAllTransactionsView()
}
rootView.btnRetrieveTransactions.setOnClickListener { fetchTransactions() } rootView.btnRetrieveTransactions.setOnClickListener { fetchTransactions() }
rootView.btnAddAccount.setOnClickListener { presenter.showAddAccountDialog() } rootView.btnAddAccount.setOnClickListener { presenter.showAddAccountDialog() }
@ -286,16 +296,31 @@ class HomeFragment : Fragment() {
private fun setFetchAllTransactionsView() { private fun setFetchAllTransactionsView() {
accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedAccountsForWhichNotAllTransactionsHaveBeenFetched accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedAccountsForWhichNotAllTransactionsHaveBeenFetched
val showFetchAllTransactionsView = accountsForWhichNotAllTransactionsHaveBeenFetched.isNotEmpty()
|| presenter.selectedAccountsTransactionRetrievalState == TransactionsRetrievalState.RetrievedTransactions
val hideFetchAllTransactionsView = accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty() if (showFetchAllTransactionsView && showTopFetchAllTransactionsView) {
|| presenter.selectedAccountsTransactionRetrievalState != TransactionsRetrievalState.RetrievedTransactions lytTopFetchAllTransactions.visibility = View.VISIBLE
if (hideFetchAllTransactionsView) {
lytFetchAllTransactions.visibility = View.GONE
} }
else { else {
lytFetchAllTransactions.visibility = View.VISIBLE lytTopFetchAllTransactions.visibility = View.GONE
} }
if (showFetchAllTransactionsView && showTopFetchAllTransactionsView == false) {
// TODO: implement CoordinatorLayout to show lytBottomFetchAllTransactions below rcyvwAccountTransactions
// lytBottomFetchAllTransactions.visibility = View.VISIBLE
}
else {
lytBottomFetchAllTransactions.visibility = View.GONE
}
}
private fun hideTopFetchAllTransactionsView() {
// TODO: persist
showTopFetchAllTransactionsView = false
setFetchAllTransactionsView()
} }

View File

@ -57,9 +57,17 @@
</RelativeLayout> </RelativeLayout>
<LinearLayout <View
android:orientation="vertical" android:id="@+id/summaryAndTransactionsListDivider"
android:id="@+id/lytFetchAllTransactions" android:layout_width="match_parent"
android:layout_height="2dp"
android:paddingLeft="-12dp"
android:paddingRight="-12dp"
android:background="@color/formSectionDivideColor"
/>
<RelativeLayout
android:id="@+id/lytTopFetchAllTransactions"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/fragment_account_transaction_fetch_all_transactions_layout_height" android:layout_height="@dimen/fragment_account_transaction_fetch_all_transactions_layout_height"
android:gravity="center" android:gravity="center"
@ -67,18 +75,41 @@
> >
<Button <Button
android:id="@+id/btnFetchAllTransactions" android:id="@+id/btnTopFetchAllTransactions"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/fragment_account_transaction_fetch_all_transactions_button_height" android:layout_height="@dimen/fragment_account_transaction_fetch_all_transactions_button_height"
android:gravity="center_horizontal" android:layout_alignParentTop="true"
android:textAlignment="center" android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@+id/btnHideTopFetchAllTransactionsView"
android:layout_toStartOf="@+id/btnHideTopFetchAllTransactionsView"
android:gravity="center"
android:textAlignment="gravity"
style="?android:attr/buttonBarButtonStyle" style="?android:attr/buttonBarButtonStyle"
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="@color/linkColor" android:textColor="@color/linkColor"
android:text="@string/fragment_home_fetch_all_account_transactions" android:text="@string/fragment_home_fetch_all_account_transactions"
/> />
</LinearLayout> <ImageButton
android:id="@+id/btnHideTopFetchAllTransactionsView"
android:layout_width="@dimen/fragment_account_transaction_hide_fetch_all_transactions_button_height_and_width"
android:layout_height="@dimen/fragment_account_transaction_hide_fetch_all_transactions_button_height_and_width"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginLeft="@dimen/fragment_account_transaction_hide_fetch_all_transactions_button_margin_start_and_end"
android:layout_marginStart="@dimen/fragment_account_transaction_hide_fetch_all_transactions_button_margin_start_and_end"
android:layout_marginRight="@dimen/fragment_account_transaction_hide_fetch_all_transactions_button_margin_start_and_end"
android:layout_marginEnd="@dimen/fragment_account_transaction_hide_fetch_all_transactions_button_margin_start_and_end"
android:background="@null"
android:tint="#000000"
app:srcCompat="@drawable/ic_baseline_close_24"
/>
</RelativeLayout>
</LinearLayout> </LinearLayout>
@ -95,6 +126,34 @@
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"
/> />
<!-- TODO: add CoordinatorLayout to show lytBottomFetchAllTransactions below rcyvwAccountTransactions -->
<LinearLayout
android:orientation="vertical"
android:id="@+id/lytBottomFetchAllTransactions"
android:layout_width="match_parent"
android:layout_height="@dimen/fragment_account_transaction_fetch_all_transactions_layout_height"
android:layout_alignParentBottom="true"
android:gravity="center"
android:background="@color/formSectionDivideColor"
android:visibility="gone"
>
<Button
android:id="@+id/btnBottomFetchAllTransactions"
android:layout_width="match_parent"
android:layout_height="@dimen/fragment_account_transaction_fetch_all_transactions_button_height"
android:gravity="center"
android:textAlignment="gravity"
style="?android:attr/buttonBarButtonStyle"
android:textAllCaps="false"
android:textColor="@color/linkColor"
android:text="@string/fragment_home_fetch_all_account_transactions"
/>
</LinearLayout>
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"
android:id="@+id/lytNoTransactionsFetched" android:id="@+id/lytNoTransactionsFetched"

View File

@ -31,6 +31,8 @@
<dimen name="fragment_account_transaction_add_account_button_height">40dp</dimen> <dimen name="fragment_account_transaction_add_account_button_height">40dp</dimen>
<dimen name="fragment_account_transaction_fetch_all_transactions_layout_height">48dp</dimen> <dimen name="fragment_account_transaction_fetch_all_transactions_layout_height">48dp</dimen>
<dimen name="fragment_account_transaction_fetch_all_transactions_button_height">40dp</dimen> <dimen name="fragment_account_transaction_fetch_all_transactions_button_height">40dp</dimen>
<dimen name="fragment_account_transaction_hide_fetch_all_transactions_button_height_and_width">30dp</dimen>
<dimen name="fragment_account_transaction_hide_fetch_all_transactions_button_margin_start_and_end">2dp</dimen>
<dimen name="list_item_account_transaction_height">74dp</dimen> <dimen name="list_item_account_transaction_height">74dp</dimen>
<dimen name="list_item_account_transaction_padding">0dp</dimen> <dimen name="list_item_account_transaction_padding">0dp</dimen>

View File

@ -4,33 +4,37 @@ import BankingUiSwift
struct AccountTransactionsDialog: View { struct AccountTransactionsDialog: View {
static private let HideTopFetchAllTransactionsViewButtonWidth: CGFloat = 34
static private let RetrievedTransactionsPeriodDateFormat = DateFormatter() static private let RetrievedTransactionsPeriodDateFormat = DateFormatter()
private let title: String private let title: String
private let showBankIcons: Bool private let showBankIcons: Bool
@State private var showTransactionsList = true @State private var showTransactionsList = true
@State private var noTransactionsFetchedMessage: LocalizedStringKey = "" @State private var noTransactionsFetchedMessage: LocalizedStringKey = ""
@State private var showFetchTransactionsButton = true @State private var showFetchTransactionsButton = true
@State private var showFetchAllTransactionsView: Bool = false @State private var showFetchAllTransactionsView: Bool = false
@State private var showFetchAllTransactionsViewAtTop: Bool = true
@State private var accountsForWhichNotAllTransactionsHaveBeenFetched: [IBankAccount] = [] @State private var accountsForWhichNotAllTransactionsHaveBeenFetched: [IBankAccount] = []
@State private var filteredTransactions: [IAccountTransaction] = [] @State private var filteredTransactions: [IAccountTransaction] = []
@State private var balanceOfAllTransactions: CommonBigDecimal = CommonBigDecimal(decimal: "0") @State private var balanceOfAllTransactions: CommonBigDecimal = CommonBigDecimal(decimal: "0")
@State private var balanceOfFilteredTransactions: CommonBigDecimal = CommonBigDecimal(decimal: "0") @State private var balanceOfFilteredTransactions: CommonBigDecimal = CommonBigDecimal(decimal: "0")
@State private var searchText = "" @State private var searchText = ""
private var searchTextBinding: Binding<String> { private var searchTextBinding: Binding<String> {
Binding<String>( Binding<String>(
get: { self.searchText }, get: { self.searchText },
@ -39,11 +43,11 @@ struct AccountTransactionsDialog: View {
self.filterTransactions($0) self.filterTransactions($0)
}) })
} }
@State private var errorMessage: Message? = nil @State private var errorMessage: Message? = nil
@Inject private var presenter: BankingPresenterSwift @Inject private var presenter: BankingPresenterSwift
@ -52,28 +56,28 @@ struct AccountTransactionsDialog: View {
presenter.selectedAllAccounts() presenter.selectedAllAccounts()
} }
init(bank: IBankData) { init(bank: IBankData) {
self.init(bank.displayName, false) self.init(bank.displayName, false)
presenter.selectedBank(bank: bank) presenter.selectedBank(bank: bank)
} }
init(account: IBankAccount) { init(account: IBankAccount) {
self.init(account.displayName, false) self.init(account.displayName, false)
presenter.selectedAccount(account: account) presenter.selectedAccount(account: account)
} }
fileprivate init(_ title: String, _ showBankIcons: Bool) { fileprivate init(_ title: String, _ showBankIcons: Bool) {
self.title = title self.title = title
self.showBankIcons = showBankIcons self.showBankIcons = showBankIcons
Self.RetrievedTransactionsPeriodDateFormat.dateStyle = .medium Self.RetrievedTransactionsPeriodDateFormat.dateStyle = .medium
} }
var body: some View { var body: some View {
VStack { VStack {
Form { Form {
@ -90,17 +94,18 @@ struct AccountTransactionsDialog: View {
} }
} }
if showFetchAllTransactionsView { if showFetchAllTransactionsView && showFetchAllTransactionsViewAtTop {
SectionWithoutBackground { HStack(alignment: .center) {
HStack(alignment: .center) {
Spacer()
fetchAllTransactionsButton fetchAllTransactionsButton
.padding(.horizontal, 0)
Spacer()
}
.padding(.horizontal, 6)
} }
.padding(.horizontal, 0)
.padding(.trailing, Self.HideTopFetchAllTransactionsViewButtonWidth)
.frame(maxWidth: .infinity, minHeight: 44, maxHeight: .infinity) // has to have at least a height of 44 (iOS 14; iOS 13: 40), otherwise a white line at bottom gets displayed
.removeSectionBackground()
.overlay(hideTopFetchAllTransactionsViewButton, alignment: .trailing)
} }
if showTransactionsList { if showTransactionsList {
@ -109,12 +114,24 @@ struct AccountTransactionsDialog: View {
AccountTransactionListItem(transaction, self.showBankIcons) AccountTransactionListItem(transaction, self.showBankIcons)
} }
} }
if showFetchAllTransactionsView && showFetchAllTransactionsViewAtTop == false {
SectionWithoutBackground {
HStack {
Spacer()
fetchAllTransactionsButton
Spacer()
}
}
}
} }
else { else {
VStack(alignment: .center) { VStack(alignment: .center) {
Text(noTransactionsFetchedMessage) Text(noTransactionsFetchedMessage)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
if showFetchTransactionsButton { if showFetchTransactionsButton {
Button("Fetch transactions") { self.fetchTransactions() } Button("Fetch transactions") { self.fetchTransactions() }
.padding(.top, 6) .padding(.top, 6)
@ -131,44 +148,65 @@ struct AccountTransactionsDialog: View {
.showNavigationBarTitle(LocalizedStringKey(title)) .showNavigationBarTitle(LocalizedStringKey(title))
.navigationBarItems(trailing: UpdateButton { _, executingDone in self.updateTransactions(executingDone) }) .navigationBarItems(trailing: UpdateButton { _, executingDone in self.updateTransactions(executingDone) })
} }
private func setInitialValues() { private func setInitialValues() {
self.balanceOfAllTransactions = self.presenter.balanceOfSelectedAccounts self.balanceOfAllTransactions = self.presenter.balanceOfSelectedAccounts
self.filterTransactions("") self.filterTransactions("")
setTransactionsView() setTransactionsView()
} }
private func setTransactionsView() { private func setTransactionsView() {
let transactionsRetrievalState = presenter.selectedAccountsTransactionRetrievalState let transactionsRetrievalState = presenter.selectedAccountsTransactionRetrievalState
let haveTransactionsForSelectedAccountsBeenRetrieved = transactionsRetrievalState == .retrievedtransactions let haveTransactionsForSelectedAccountsBeenRetrieved = transactionsRetrievalState == .retrievedtransactions
self.accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedAccountsForWhichNotAllTransactionsHaveBeenFetched self.accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedAccountsForWhichNotAllTransactionsHaveBeenFetched
self.showFetchAllTransactionsView = shouldShowFetchAllTransactionsView && haveTransactionsForSelectedAccountsBeenRetrieved self.showFetchAllTransactionsView = accountsForWhichNotAllTransactionsHaveBeenFetched.isNotEmpty && haveTransactionsForSelectedAccountsBeenRetrieved
//self.showFetchAllTransactionsViewAtTop = true // TODO: read from database
self.showTransactionsList = haveTransactionsForSelectedAccountsBeenRetrieved self.showTransactionsList = haveTransactionsForSelectedAccountsBeenRetrieved
self.noTransactionsFetchedMessage = getNoTransactionsFetchedMessage(transactionsRetrievalState) self.noTransactionsFetchedMessage = getNoTransactionsFetchedMessage(transactionsRetrievalState)
self.showFetchTransactionsButton = transactionsRetrievalState != .accountdoesnotsupportfetchingtransactions && transactionsRetrievalState != .accounttypenotsupported self.showFetchTransactionsButton = transactionsRetrievalState != .accountdoesnotsupportfetchingtransactions && transactionsRetrievalState != .accounttypenotsupported
} }
private func hideTopFetchAllTransactionsView() {
private var fetchAllTransactionsButton: some View { // TODO: save that we shouldn't show showFetchAllTransactionsView at top anymore in database
Button("Fetch all account transactions") { for account in accountsForWhichNotAllTransactionsHaveBeenFetched {
self.fetchAllTransactions(self.accountsForWhichNotAllTransactionsHaveBeenFetched) //UserDefaults.standard.set(true, forKey: Self.DoNotShowFetchAllTransactionsOverlayForUserDefaultsKeyPrefix + account.technicalId)
} }
self.showFetchAllTransactionsViewAtTop = false
}
private var fetchAllTransactionsButton: some View {
Button(action: { self.fetchAllTransactions() } ) {
Text("Fetch all account transactions")
.multilineTextAlignment(.center)
}
.foregroundColor(Color.blue)
} }
private var hideTopFetchAllTransactionsViewButton: some View {
// do not set Button's action as then if any of the two buttons get pressed, both actions get executed (bug in iOS). Use .onTapGesture() instead
Button("X") { }
.onTapGesture {
self.hideTopFetchAllTransactionsView()
}
.frame(width: Self.HideTopFetchAllTransactionsViewButtonWidth, height: 44, alignment: .center)
}
private func updateTransactions(_ executingDone: @escaping () -> Void) { private func updateTransactions(_ executingDone: @escaping () -> Void) {
presenter.updateSelectedAccountsTransactionsAsync { response in presenter.updateSelectedAccountsTransactionsAsync { response in
executingDone() executingDone()
self.balanceOfAllTransactions = self.presenter.balanceOfSelectedAccounts self.balanceOfAllTransactions = self.presenter.balanceOfSelectedAccounts
if response.successful { if response.successful {
self.filterTransactions(self.searchText) self.filterTransactions(self.searchText)
} }
@ -179,7 +217,7 @@ struct AccountTransactionsDialog: View {
} }
} }
} }
private func fetchTransactions() { private func fetchTransactions() {
for account in presenter.selectedAccounts { for account in presenter.selectedAccounts {
if account.haveAllTransactionsBeenRetrieved { if account.haveAllTransactionsBeenRetrieved {
@ -190,16 +228,16 @@ struct AccountTransactionsDialog: View {
} }
} }
} }
private func fetchAllTransactions(_ accounts: [IBankAccount]) { private func fetchAllTransactions() {
accounts.forEach { account in accountsForWhichNotAllTransactionsHaveBeenFetched.forEach { account in
presenter.fetchAllAccountTransactionsAsync(account: account, callback: self.handleGetTransactionsResult) presenter.fetchAllAccountTransactionsAsync(account: account, callback: self.handleGetTransactionsResult)
} }
} }
private func handleGetTransactionsResult(_ response: GetTransactionsResponse) { private func handleGetTransactionsResult(_ response: GetTransactionsResponse) {
setTransactionsView() setTransactionsView()
if response.successful { if response.successful {
self.filterTransactions(self.searchText) self.filterTransactions(self.searchText)
} }
@ -209,7 +247,7 @@ struct AccountTransactionsDialog: View {
} }
} }
} }
private func getNoTransactionsFetchedMessage(_ state: TransactionsRetrievalState) -> LocalizedStringKey { private func getNoTransactionsFetchedMessage(_ state: TransactionsRetrievalState) -> LocalizedStringKey {
if state == .neverretrievedtransactions { if state == .neverretrievedtransactions {
return "No transactions fetched yet" return "No transactions fetched yet"
@ -225,29 +263,25 @@ struct AccountTransactionsDialog: View {
return "Account type not supported by app" return "Account type not supported by app"
} }
} }
private func mapDate(_ date: CommonDate?) -> String { private func mapDate(_ date: CommonDate?) -> String {
if let date = date?.date { if let date = date?.date {
return Self.RetrievedTransactionsPeriodDateFormat.string(from: date) return Self.RetrievedTransactionsPeriodDateFormat.string(from: date)
} }
return "" return ""
} }
private func filterTransactions(_ query: String) { private func filterTransactions(_ query: String) {
self.filteredTransactions = presenter.searchSelectedAccountTransactions(query: query).sorted { $0.valueDate.date > $1.valueDate.date } self.filteredTransactions = presenter.searchSelectedAccountTransactions(query: query).sorted { $0.valueDate.date > $1.valueDate.date }
self.balanceOfFilteredTransactions = query.isBlank ? balanceOfAllTransactions : filteredTransactions.sumAmounts() self.balanceOfFilteredTransactions = query.isBlank ? balanceOfAllTransactions : filteredTransactions.sumAmounts()
} }
private func getAccountThatFailed(_ response: GetTransactionsResponse) -> IBankAccount? { private func getAccountThatFailed(_ response: GetTransactionsResponse) -> IBankAccount? {
return response.retrievedData.first { $0.successfullyRetrievedData == false }?.account return response.retrievedData.first { $0.successfullyRetrievedData == false }?.account
} }
private var shouldShowFetchAllTransactionsView: Bool {
return accountsForWhichNotAllTransactionsHaveBeenFetched.isNotEmpty
}
} }