better network error handling, fix crash when no network in transaction history page
All checks were successful
Auto Tag on Version Change / check-version (push) Successful in 6s
All checks were successful
Auto Tag on Version Change / check-version (push) Successful in 6s
This commit is contained in:
@@ -150,8 +150,15 @@ class AccountHistoryFragment : Fragment() {
|
||||
pendingIconUrls.clear()
|
||||
firstPageDone = false
|
||||
fetcher = HistoryFetcher(account)
|
||||
adapter.setTransactions(emptyList())
|
||||
binding.emptyView.visibility = View.GONE
|
||||
// Restore cache immediately so data stays visible while refreshing
|
||||
val cached = TransactionCache.load(requireContext(), account.accountNumber)
|
||||
if (cached.isNotEmpty()) {
|
||||
allTransactions.addAll(cached)
|
||||
filterAndDisplay()
|
||||
} else {
|
||||
adapter.setTransactions(emptyList())
|
||||
binding.emptyView.visibility = View.GONE
|
||||
}
|
||||
loadNextPage()
|
||||
}
|
||||
|
||||
@@ -179,6 +186,7 @@ class AccountHistoryFragment : Fragment() {
|
||||
}
|
||||
|
||||
isLoading = false
|
||||
if (_binding == null) return@launch
|
||||
|
||||
if (!firstPageDone) {
|
||||
firstPageDone = true
|
||||
@@ -191,6 +199,7 @@ class AccountHistoryFragment : Fragment() {
|
||||
if (allTransactions.isEmpty()) binding.emptyView.visibility = View.VISIBLE
|
||||
return@launch
|
||||
}
|
||||
(activity as? HomeActivity)?.hideConnectivityBanner()
|
||||
|
||||
if (transactions.isNotEmpty()) {
|
||||
val existingIds = allTransactions.map { it.id }.toHashSet()
|
||||
|
||||
@@ -147,7 +147,7 @@ class ContactPickerSheetFragment : BottomSheetDialogFragment() {
|
||||
viewModel.accounts.observe(viewLifecycleOwner) { pagerAdapter.rebuildAll() }
|
||||
viewModel.hideAmounts.observe(viewLifecycleOwner) { pagerAdapter.rebuildAll() }
|
||||
|
||||
(activity as? HomeActivity)?.loadAllContacts()
|
||||
(activity as? HomeActivity)?.triggerRefresh()
|
||||
}
|
||||
|
||||
private fun attachMediator(pages: List<TabDef>) {
|
||||
|
||||
@@ -137,7 +137,7 @@ class ContactsFragment : Fragment() {
|
||||
|
||||
binding.swipeRefresh.setOnRefreshListener {
|
||||
contactsRefreshing = true
|
||||
(activity as? HomeActivity)?.loadAllContacts()
|
||||
(activity as? HomeActivity)?.triggerRefresh()
|
||||
}
|
||||
|
||||
viewModel.contactCategories.observe(viewLifecycleOwner) { cats ->
|
||||
|
||||
@@ -47,7 +47,7 @@ class FinancingFragment : Fragment() {
|
||||
|
||||
binding.swipeRefresh.setOnRefreshListener {
|
||||
financingRefreshing = true
|
||||
(activity as? HomeActivity)?.triggerRefreshFinancing()
|
||||
(activity as? HomeActivity)?.triggerRefresh()
|
||||
}
|
||||
|
||||
viewModel.accounts.observe(viewLifecycleOwner) { rebuildAdapter() }
|
||||
|
||||
@@ -554,7 +554,7 @@ fun applyNavLabelVisibility() {
|
||||
binding.connectivityBanner.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun hideConnectivityBanner() {
|
||||
fun hideConnectivityBanner() {
|
||||
binding.connectivityBanner.visibility = View.GONE
|
||||
}
|
||||
|
||||
|
||||
@@ -859,7 +859,11 @@ class TransferFragment : Fragment() {
|
||||
activity.triggerRefresh()
|
||||
activity.showWithBackStack(TransferReceiptFragment.newInstance(receipt, capturedToAvatar))
|
||||
} else if (!ok) {
|
||||
Toast.makeText(requireContext(), msg, Toast.LENGTH_LONG).show()
|
||||
if (msg == "CONNECTIVITY") {
|
||||
(activity as? HomeActivity)?.showConnectivityBanner(getString(R.string.connectivity_no_internet))
|
||||
} else {
|
||||
Toast.makeText(requireContext(), msg, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1069,7 +1073,7 @@ class TransferFragment : Fragment() {
|
||||
Triple(false, result.errorMessage.ifBlank { "Transfer failed" }, null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Triple(false, e.message ?: "Transfer failed", null)
|
||||
Triple(false, if (e is java.io.IOException) "CONNECTIVITY" else (e.message ?: "Transfer failed"), null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1122,7 +1126,7 @@ class TransferFragment : Fragment() {
|
||||
// Step 1: initiate
|
||||
val initiated = try {
|
||||
BmlTransferClient().initiateTransfer(sess, debitAccount, creditAccount, amount, transferType, currency, bank)
|
||||
} catch (e: Exception) { return Triple(false, e.message ?: "Initiation failed", null) }
|
||||
} catch (e: Exception) { return Triple(false, if (e is java.io.IOException) "CONNECTIVITY" else (e.message ?: "Initiation failed"), null) }
|
||||
|
||||
if (!initiated) return Triple(false, "Failed to initiate transfer — check your session", null)
|
||||
|
||||
@@ -1154,7 +1158,7 @@ class TransferFragment : Fragment() {
|
||||
Triple(false, result.errorMessage.ifBlank { "Transfer failed" }, null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Triple(false, e.message ?: "Transfer failed", null)
|
||||
Triple(false, if (e is java.io.IOException) "CONNECTIVITY" else (e.message ?: "Transfer failed"), null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1396,7 +1400,7 @@ class TransferFragment : Fragment() {
|
||||
Triple(false, result.errorMessage.ifBlank { "Transfer failed" }, null as TransferReceiptData?)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Triple(false, e.message ?: "Transfer failed", null as TransferReceiptData?)
|
||||
Triple(false, if (e is java.io.IOException) "CONNECTIVITY" else (e.message ?: "Transfer failed"), null as TransferReceiptData?)
|
||||
}
|
||||
}
|
||||
(activity as? HomeActivity)?.setRefreshing(false)
|
||||
@@ -1410,7 +1414,11 @@ class TransferFragment : Fragment() {
|
||||
activity.showWithBackStack(TransferReceiptFragment.newInstance(receipt, capturedToAvatar))
|
||||
} else {
|
||||
binding.btnTransfer.isEnabled = true
|
||||
binding.tilBmlOtp.error = msg
|
||||
if (msg == "CONNECTIVITY") {
|
||||
(activity as? HomeActivity)?.showConnectivityBanner(getString(R.string.connectivity_no_internet))
|
||||
} else {
|
||||
binding.tilBmlOtp.error = msg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import sh.sar.basedbank.api.fahipay.FahipayHistoryClient
|
||||
import sh.sar.basedbank.api.models.BankAccount
|
||||
import sh.sar.basedbank.api.mib.MibContactsClient
|
||||
import sh.sar.basedbank.api.mib.MibHistoryClient
|
||||
import sh.sar.basedbank.api.models.BankServerException
|
||||
import sh.sar.basedbank.api.models.BankTransaction
|
||||
import sh.sar.basedbank.api.mib.TransactionCache
|
||||
import sh.sar.basedbank.databinding.FragmentTransferHistoryBinding
|
||||
@@ -161,8 +162,15 @@ class TransferHistoryFragment : Fragment() {
|
||||
val accounts = accountStates.map { it.account }
|
||||
accountStates.clear()
|
||||
accounts.forEach { accountStates.add(AccountState(it)) }
|
||||
adapter.setTransactions(emptyList())
|
||||
binding.emptyView.visibility = View.GONE
|
||||
// Restore cache immediately so data stays visible while refreshing
|
||||
val cached = TransactionCache.load(requireContext(), "transfer")
|
||||
if (cached.isNotEmpty()) {
|
||||
allTransactions.addAll(cached)
|
||||
filterAndDisplay()
|
||||
} else {
|
||||
adapter.setTransactions(emptyList())
|
||||
binding.emptyView.visibility = View.GONE
|
||||
}
|
||||
loadNextPages()
|
||||
}
|
||||
|
||||
@@ -178,6 +186,14 @@ class TransferHistoryFragment : Fragment() {
|
||||
val app = requireActivity().application as BasedBankApp
|
||||
|
||||
lifecycleScope.launch {
|
||||
val bannerMsg = java.util.concurrent.atomic.AtomicReference<String?>(null)
|
||||
fun trackError(e: Exception) {
|
||||
when {
|
||||
e is java.io.IOException -> bannerMsg.compareAndSet(null, "IO")
|
||||
e is BankServerException -> bannerMsg.compareAndSet(null, "SERVER:${e.bankName}")
|
||||
}
|
||||
}
|
||||
|
||||
val newTransactions = withContext(Dispatchers.IO) {
|
||||
val results = mutableListOf<BankTransaction>()
|
||||
|
||||
@@ -215,7 +231,7 @@ class TransferHistoryFragment : Fragment() {
|
||||
list
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) { emptyList<BankTransaction>() }
|
||||
} catch (e: Exception) { trackError(e); emptyList<BankTransaction>() }
|
||||
}
|
||||
}.awaitAll().flatten())
|
||||
|
||||
@@ -233,7 +249,7 @@ class TransferHistoryFragment : Fragment() {
|
||||
if (total > 0) state.fahipayTotal = total
|
||||
state.fahipayNextStart += list.size
|
||||
results.addAll(list)
|
||||
} catch (_: Exception) {}
|
||||
} catch (e: Exception) { trackError(e) }
|
||||
}
|
||||
|
||||
// MIB accounts: serialized per profile, protected by mutex to prevent session race
|
||||
@@ -242,23 +258,25 @@ class TransferHistoryFragment : Fragment() {
|
||||
val session = app.mibSessions[loginId] ?: continue
|
||||
for ((profileId, states) in loginStates.groupBy { it.account.profileId }) {
|
||||
app.mibMutex.withLock {
|
||||
val profiles = app.mibProfilesMap[loginId] ?: emptyList()
|
||||
val profile = profiles.firstOrNull { it.profileId == profileId }
|
||||
if (profile != null) app.mibFlowFor(loginId).switchProfile(session, profile)
|
||||
for (state in states) {
|
||||
try {
|
||||
val (list, total) = MibHistoryClient().fetchHistory(
|
||||
session = session,
|
||||
accountNo = state.account.accountNumber,
|
||||
accountDisplayName = state.account.accountBriefName,
|
||||
start = state.mibNextStart,
|
||||
pageSize = pageSize
|
||||
)
|
||||
if (total > 0) state.mibTotalCount = total
|
||||
state.mibNextStart += list.size.coerceAtLeast(pageSize)
|
||||
results.addAll(list)
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
try {
|
||||
val profiles = app.mibProfilesMap[loginId] ?: emptyList()
|
||||
val profile = profiles.firstOrNull { it.profileId == profileId }
|
||||
if (profile != null) app.mibFlowFor(loginId).switchProfile(session, profile)
|
||||
for (state in states) {
|
||||
try {
|
||||
val (list, total) = MibHistoryClient().fetchHistory(
|
||||
session = session,
|
||||
accountNo = state.account.accountNumber,
|
||||
accountDisplayName = state.account.accountBriefName,
|
||||
start = state.mibNextStart,
|
||||
pageSize = pageSize
|
||||
)
|
||||
if (total > 0) state.mibTotalCount = total
|
||||
state.mibNextStart += list.size.coerceAtLeast(pageSize)
|
||||
results.addAll(list)
|
||||
} catch (e: Exception) { trackError(e) }
|
||||
}
|
||||
} catch (e: Exception) { trackError(e) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,6 +285,16 @@ class TransferHistoryFragment : Fragment() {
|
||||
}
|
||||
|
||||
isLoading = false
|
||||
if (_binding == null) return@launch
|
||||
|
||||
val raw = bannerMsg.get()
|
||||
when {
|
||||
raw == null -> (activity as? HomeActivity)?.hideConnectivityBanner()
|
||||
raw == "IO" -> (activity as? HomeActivity)?.showConnectivityBanner(getString(R.string.connectivity_no_internet))
|
||||
raw.startsWith("SERVER:") -> (activity as? HomeActivity)?.showConnectivityBanner(
|
||||
getString(R.string.connectivity_server_error, raw.removePrefix("SERVER:"))
|
||||
)
|
||||
}
|
||||
|
||||
if (!firstBatchDone) {
|
||||
firstBatchDone = true
|
||||
|
||||
Reference in New Issue
Block a user