From ce1cc9bfffae3132b69acebbcb84b2805d53be2e Mon Sep 17 00:00:00 2001 From: Binali223 <156178015+Binali223@users.noreply.github.com> Date: Thu, 27 Aug 2026 16:47:35 +0100 Subject: [PATCH 1/6] feat: Add activity refresh error banner (#100) --- app/(tabs)/history.tsx | 139 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 2 deletions(-) diff --git a/app/(tabs)/history.tsx b/app/(tabs)/history.tsx index 052c0da..4b96e0a 100644 --- a/app/(tabs)/history.tsx +++ b/app/(tabs)/history.tsx @@ -77,6 +77,75 @@ const ListFooter = ({ return null; }; +/** + * Non-blocking banner shown when a refresh fails but cached transactions are + * still available. Keeps the list visible and offers an inline retry. + */ +const RefreshErrorBanner = ({ + message, + onRetry, + isRetrying, + colors, + styles, +}: { + message: string; + onRetry: () => void; + isRetrying: boolean; + colors: ThemeColors; + styles: ReturnType; +}) => ( + + {message} + + {isRetrying ? ( + + ) : ( + Retry + )} + + +); + +/** + * Full-screen error shown when a refresh fails and there is no cached activity + * to display. + */ +const ActivityErrorState = ({ + onRetry, + isRetrying, + colors, + styles, +}: { + onRetry: () => void; + isRetrying: boolean; + colors: ThemeColors; + styles: ReturnType; +}) => ( + + Couldn't load activity + + We couldn't refresh your activity right now. Please try again. + + + {isRetrying ? ( + + ) : ( + Retry + )} + + +); + /** * Shown when there are no transactions and the screen is not loading. */ @@ -248,6 +317,15 @@ export default function HistoryScreen() { onRetry={() => { refreshWalletData(); retry(); }} isRetrying={isLoading} /> + {error && transactions.length > 0 && ( + { refreshWalletData(); retry(); }} + isRetrying={isLoading} + colors={colors} + styles={styles} + /> + )} { refreshWalletData(); retry(); }} + isRetrying={isLoading} + colors={colors} + styles={styles} + /> + ) : !isLoading ? ( 0, + }} /> ); @@ -332,6 +424,49 @@ const createStyles = (colors: ThemeColors) => StyleSheet.create({ color: colors.textMuted, fontSize: 13, }, + refreshErrorBanner: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + backgroundColor: colors.surface, + borderLeftWidth: 3, + borderLeftColor: colors.error, + borderRadius: RADIUS.md, + paddingHorizontal: SIZES.md, + paddingVertical: SIZES.sm, + marginBottom: SIZES.md, + }, + refreshErrorText: { + flex: 1, + color: colors.textSecondary, + fontSize: 14, + marginRight: SIZES.sm, + }, + refreshErrorRetryButton: { + backgroundColor: colors.error, + borderRadius: RADIUS.md, + paddingHorizontal: SIZES.md, + paddingVertical: SIZES.xs, + minWidth: 64, + alignItems: 'center', + }, + refreshErrorRetryText: { + color: colors.background, + fontSize: 14, + fontWeight: '600', + }, + fullScreenError: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + padding: SIZES.xl, + }, + fullScreenErrorTitle: { + color: colors.textSecondary, + fontSize: 18, + fontWeight: '600', + marginBottom: SIZES.sm, + sectionHeader: { paddingTop: SIZES.sm, paddingBottom: SIZES.xs, From 98ea66a58657d9a9079b71db4cf6c603440fef7d Mon Sep 17 00:00:00 2001 From: Binali223 <156178015+Binali223@users.noreply.github.com> Date: Thu, 27 Aug 2026 16:47:37 +0100 Subject: [PATCH 2/6] feat: Add activity refresh error banner (#100) --- src/store/walletStore.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/store/walletStore.ts b/src/store/walletStore.ts index 6ee485a..f71f227 100644 --- a/src/store/walletStore.ts +++ b/src/store/walletStore.ts @@ -40,6 +40,7 @@ interface WalletState { isFunding: boolean; fundError: string | null; error: string | null; + refreshError: string | null; showBackupReminder: boolean; /** True once the initial `loadWalletFromStorage` call has resolved (success or failure). */ walletChecked: boolean; @@ -92,6 +93,7 @@ const resetWalletState = () => ({ nextCursor: null, balanceState: 'idle' as BalanceState, fundingStatus: 'unknown' as FundingStatus, + refreshError: null, }); const parseStoredSecret = (storedValue: string): string | null => { @@ -137,6 +139,7 @@ export const useWalletStore = create((set, get) => ({ isFunding: false, fundError: null, error: null, + refreshError: null, balanceState: 'idle' as BalanceState, fundingStatus: 'unknown' as FundingStatus, isLoadingMore: false, @@ -223,7 +226,7 @@ export const useWalletStore = create((set, get) => ({ const { publicKey } = get(); if (!publicKey) return; - set({ isLoading: true, error: null, balanceState: 'loading', isLoadingMore: false, nextCursor: null, hasMoreTransactions: false }); + set({ isLoading: true, error: null, refreshError: null, balanceState: 'loading', isLoadingMore: false, nextCursor: null, hasMoreTransactions: false }); try { const [balance, page] = await Promise.all([ fetchXlmBalance(publicKey), @@ -258,10 +261,12 @@ export const useWalletStore = create((set, get) => ({ }); } catch (err: any) { console.error('Failed to refresh wallet data'); + const hasExistingData = get().transactions.length > 0; set({ isLoading: false, - balanceState: 'unavailable', - error: err.message || 'Failed to sync data', + balanceState: hasExistingData ? 'available' : 'unavailable', + error: hasExistingData ? null : (err.message || 'Failed to sync data'), + refreshError: hasExistingData ? (err.message || 'Failed to sync data') : null, }); } }, From 296613e84cd23e2e2e533fa3311b740f48c2da77 Mon Sep 17 00:00:00 2001 From: Binali223 <156178015+Binali223@users.noreply.github.com> Date: Thu, 27 Aug 2026 16:47:39 +0100 Subject: [PATCH 3/6] feat: Add activity refresh error banner (#100) --- src/components/ErrorState.tsx | 43 ++++++++++++----------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/src/components/ErrorState.tsx b/src/components/ErrorState.tsx index 4784181..72dd4da 100644 --- a/src/components/ErrorState.tsx +++ b/src/components/ErrorState.tsx @@ -18,6 +18,7 @@ export interface ErrorStateProps { action?: ErrorStateAction; style?: StyleProp; testID?: string; + variant?: 'fullscreen' | 'banner'; } export const ErrorState: React.FC = ({ @@ -27,9 +28,10 @@ export const ErrorState: React.FC = ({ action, style, testID = 'error-state', + variant = 'fullscreen', }) => { const { colors } = useTheme(); - const styles = useMemo(() => createStyles(colors), [colors]); + const styles = useMemo(() => createStyles(colors, variant), [colors, variant]); return ( = ({ testID={testID} > - {icon ?? } + {icon ?? } + + + {title} + {message ? {message} : null} - {title} - {message ? {message} : null} {action ? (