Skip to content

[FEAT] History list improvements#342

Merged
danilosilvackl merged 11 commits intomainfrom
ds-history-store
Sep 18, 2025
Merged

[FEAT] History list improvements#342
danilosilvackl merged 11 commits intomainfrom
ds-history-store

Conversation

@danilosilvackl
Copy link
Copy Markdown
Contributor

@danilosilvackl danilosilvackl commented Sep 9, 2025

We created a new history store for managing history data

  • it handles the initial fetching of the data
  • the polling for next history updates (30s)
  • when and where to update
  • different filtered lists (filtered by token or to hide received payments smaller than 0.1 XLM)
  • programmatically update after swap or send transactions
  • manual loading to simulate the pull to refresh spinner when updating from swap or send transactions

iOS 

Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-09-08.at.20.25.55.mp4

Android 🤖

Screen_recording_20250908_210638.mp4

closes #300
closes #299

Comment thread src/ducks/history.ts Outdated
const year = date.getFullYear();
const monthYear = `${month}:${year}`;

const lastSection = sections.length > 0 && sections[sections.length - 1];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: do you need both checks? directly sections[sections.length -1] or !!sections.length would already identify it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leofelix077 what would happen when we have this case: const lastSection = sections[-1] ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think javascript does not support it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hummm it appears it would result in lastSection = undefined on javascript, which would work in this case. But I'd test it to make sure

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in JS an array is just an object under the hood at the end of day

const myArray = {
 0: 'firstItem'
 1: 'someitem'
 
}

myArray[-1] is undefined

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice one, done!

Comment thread src/ducks/history.ts Outdated

const { fetchAccountBalances, getBalances } = useBalancesStore.getState();

await fetchAccountBalances({
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be a Promise.all with the getAccountHistory?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

Comment thread src/ducks/history.ts
history: historySections,
};
},
startPolling: (params) => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

general question: why do we need the polling?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the initial idea was to keep the same pattern we use for balances where we have a central polling (on TabNavigator) which updates balances every 30 seconds. But it appears on this PR we don't have a central history polling but instead it starts/stops polling whenever users navigate to History screens which I agree does not seem very effective.

Ideally I think we should either have a central and constant polling or don't have the polling at all then just trigger a background refresh whenever user visits a history list (and take care not run concurrent history fetching).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the way I first did was wrong (polling only after it was fetched the first time). Now we are using a centralized polling system like the balance list

Comment thread src/hooks/useGetHistoryData.ts Outdated
error: shouldShowError ? error : null,
isLoading: shouldShowFullScreenLoading,
isRefreshing,
isNavigationRefresh: isNavigationRefresh || shouldRefreshAfterNavigation,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we could have a useFocusEffect instead a param to refresh after navigation?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not against using useFocusEffect, but in case we decide on fetching history every time users navigate to history we will need to take care not to stack duplicate requests since it's a pretty heavy request

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the idea is to prevent the fetch to happen every time we visit this screen, I prefer to stick with the param.
we have a controlled way to refresh, in this case we only force this refresh when we navigate from we finish a swap or send transaction

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the shouldRefreshAfterNavigation param works, but I have a suggestion to make it less specific so it's potentially easier to main:

I'm thinking maybe we could call something like fetchAccountHistory({ hasRecentTransaction }) right after the transaction submission is complete (or right before navigating to the history screen). Then on History screen we display the CustomRefreshIndicator if hasRecentTransaction && isFetching as I'm understanding that the goal of the custom shouldRefreshAfterNavigation param is to display the CustomRefreshIndicator so we make it clear for users that History is being updated after a recent transaction has been submitted.

Also, if users stay on the "Transaction Success" screen long enough for the fetchAccountHistory({ hasRecentTransaction }) fetching to complete I think it's fine to not display the CustomRefreshIndicator when they navigate to History as the recent transaction should already be appearing there.

What do you think?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, I made the change and this helped to tone down the amount of code by a fair bit, thanks for the suggestion

Comment thread src/i18n/locales/en/translations.json Outdated
},
"hideDust": {
"title": "Hide small payments",
"title": "Hide received payments smaller than 0.1 XLM",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this intended? this is on the preferences screen. the title will break in two lines

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the title here should remain Hide small payments and the description should be updated to Hide received payments smaller than 0.1 XLM

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, my mistake, fixed!

Comment thread src/helpers/history.ts Outdated
* This is typically called after completing a transaction (send/swap) to ensure
* the latest transaction appears in the history with a smooth UX
*/
export const markHistoryForRefreshAfterTransaction = () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious on why you've chosen to create a helper for it in place of calling markForRefreshAfterNavigation() directly on the components

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was actually redundant, removed

Comment thread src/i18n/locales/pt/translations.json Outdated
},
"hideDust": {
"title": "Ocultar pagamentos pequenos",
"title": "Ocultar pagamentos recebidos menores que 0.1 XLM",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing here

{ListHeaderComponent}
<HistoryWrapper>
<Spinner testID="spinner" />
<Spinner size="small" testID="spinner" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we make it a large spinner? I think it would look better since History takes a lot of space

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

@danilosilvackl
Copy link
Copy Markdown
Contributor Author

@CassioMG @leofelix077 also added the improvement to the pull to refresh from #299

Before

before.mov

After

after.mov


// Mark history to refresh when user navigates to history screen
markHistoryForRefreshAfterTransaction();
useHistoryStore.getState().markForRefreshAfterNavigation();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not extracting it at the beginning of the component?

const { markForRefreshAfterNavigation } = useHistoryStore();


// Mark history to refresh when user navigates to history screen
markHistoryForRefreshAfterTransaction();
useHistoryStore.getState().markForRefreshAfterNavigation();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Comment thread src/ducks/history.ts Outdated
isLoading: boolean;
error: string | null;
shouldRefreshAfterNavigation: boolean;
isFetching: boolean; // Track if a fetch is currently in progress
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we review/update the HistoryState JSDocs above?

Copy link
Copy Markdown
Contributor

@CassioMG CassioMG left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm - left a few suggestions

@danilosilvackl danilosilvackl merged commit 48dc2ca into main Sep 18, 2025
4 checks passed
@danilosilvackl danilosilvackl deleted the ds-history-store branch September 18, 2025 14:08
@github-actions github-actions Bot mentioned this pull request Nov 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Only fetch History when needed History pull-to-refresh improvement

3 participants