Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2d6c3a8
fix: prevent multiple calls
LeleDallas Nov 7, 2025
ac9f130
Revert "fix: prevent multiple calls"
LeleDallas Nov 10, 2025
e2af20d
fix: duplicated wallet calls
LeleDallas Nov 10, 2025
b280663
chore: removed comment
LeleDallas Nov 10, 2025
3be60e4
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Nov 10, 2025
9f4d3b3
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Nov 10, 2025
2256346
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Nov 10, 2025
f962fef
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Nov 11, 2025
52dc0cd
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Nov 11, 2025
e0b52cc
chore: restore old navigation behavior
LeleDallas Nov 11, 2025
c04f79f
chore: removed unnecessary force update calls
LeleDallas Nov 11, 2025
d8d7e29
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Nov 12, 2025
fd53bf4
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Nov 19, 2025
707b9a7
chore: [IOBP-2166] Saga improvements (#7618)
LeleDallas Nov 24, 2025
25168e6
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Nov 24, 2025
e194044
chore: restore comment
LeleDallas Nov 25, 2025
cff94fa
chore: removed duplicated const
LeleDallas Nov 25, 2025
1d5358b
chore: removed wrong path
LeleDallas Nov 25, 2025
211717e
refactor: call eyca only if is CGN linking
LeleDallas Nov 25, 2025
1541a50
chore: include idpay path
LeleDallas Nov 25, 2025
c71a061
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Nov 25, 2025
8a507b7
feat: startup completed action
LeleDallas Nov 26, 2025
76c6d3a
Revert "feat: startup completed action"
LeleDallas Nov 27, 2025
0a01895
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Nov 27, 2025
8c1f05d
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Nov 27, 2025
bbade26
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Dec 1, 2025
350295b
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Dec 1, 2025
f1327d3
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Dec 2, 2025
b13ad8f
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
Hantex9 Dec 4, 2025
cc040da
Merge branch 'master' into IOBP-000-fix-wallet-update-multiple-calls
LeleDallas Dec 5, 2025
e04919b
refactor: moved duplicated link in const
LeleDallas Dec 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
checkCurrentSession,
sessionInvalid
} from "../../../authentication/common/store/actions";
import { maybeHandlePendingBackgroundActions } from "../../../pushNotifications/sagas/common";
import { maybeHandlePendingBackgroundActions } from "../../../../sagas/backgroundActions";
import { isFastLoginEnabledSelector } from "../../../authentication/fastLogin/store/selectors";
import { startApplicationInitialization } from "../../../../store/actions/application";
import { PinString } from "../../../../types/PinString";
Expand Down
2 changes: 1 addition & 1 deletion ts/features/identification/sagas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
sessionInvalid
} from "../../authentication/common/store/actions";
import { isFastLoginEnabledSelector } from "../../authentication/fastLogin/store/selectors/index";
import { maybeHandlePendingBackgroundActions } from "../../pushNotifications/sagas/common";
import { maybeHandlePendingBackgroundActions } from "../../../sagas/backgroundActions";
import {
identificationCancel,
identificationForceLogout,
Expand Down
14 changes: 14 additions & 0 deletions ts/features/linking/sagas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import { initiateAarFlow } from "../../pn/aar/store/actions";
import { isSendAARLink } from "../../pn/aar/utils/deepLinking";
import { clearLinkingUrl } from "../actions";
import { storedLinkingUrlSelector } from "../reducers";
import {
isCGNLinking,
shouldTriggerWalletUpdate
} from "../../../utils/deepLinkUtils";
import { walletUpdate } from "../../wallet/store/actions";
import { cgnEycaStatus } from "../../bonus/cgn/store/actions/eyca/details";

export function* handleStoredLinkingUrlIfNeeded() {
const storedLinkingUrl = yield* select(storedLinkingUrlSelector);
Expand All @@ -14,6 +20,14 @@ export function* handleStoredLinkingUrlIfNeeded() {

return true;
}
if (shouldTriggerWalletUpdate(storedLinkingUrl)) {
yield* put(clearLinkingUrl());
yield* put(walletUpdate());
// If the stored linking URL is a CGN linking, we also need to get EYCA status
if (isCGNLinking(storedLinkingUrl)) {
yield* put(cgnEycaStatus.request());
}
}
}
return false;
}
36 changes: 10 additions & 26 deletions ts/features/pushNotifications/sagas/__tests__/common.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { testSaga } from "redux-saga-test-plan";
import NavigationService from "../../../../navigation/NavigationService";
import { maybeHandlePendingBackgroundActions } from "../../../../sagas/backgroundActions";
import { navigateToMainNavigatorAction } from "../../../../store/actions/navigation";
import { handleStoredLinkingUrlIfNeeded } from "../../../linking/sagas";
import { resetMessageArchivingAction } from "../../../messages/store/actions/archiving";
Expand All @@ -16,14 +17,11 @@ import { checkNotificationPermissions } from "../../utils";
import { navigateToMessageRouterAction } from "../../utils/navigation";
import {
checkAndUpdateNotificationPermissionsIfNeeded,
maybeHandlePendingBackgroundActions,
testable,
handlePushNotificationIfNeeded,
updateNotificationPermissionsIfNeeded
} from "../common";

describe("maybeHandlePendingBackgroundActions", () => {
const pushHandler = testable?.handlePushNotificationIfNeeded;

describe("main functionality", () => {
const generateTestName = (
linkingUrlHandled: boolean,
Expand All @@ -40,19 +38,17 @@ describe("maybeHandlePendingBackgroundActions", () => {
[true, false].forEach(linkingUrlHandled =>
[true, false].forEach(pushNotifHandled =>
it(generateTestName(linkingUrlHandled, pushNotifHandled), () => {
if (pushHandler === undefined) {
fail(
"testable export does not contain handlePushNotificationIfNeeded"
);
}
const saga = testSaga(maybeHandlePendingBackgroundActions, false)
.next()
.call(handleStoredLinkingUrlIfNeeded)
.next(linkingUrlHandled);
if (linkingUrlHandled) {
saga.isDone();
} else {
saga.call(pushHandler, false).next(pushNotifHandled).isDone();
saga
.call(handlePushNotificationIfNeeded, false)
.next(pushNotifHandled)
.isDone();
}
})
)
Expand All @@ -71,10 +67,7 @@ describe("maybeHandlePendingBackgroundActions", () => {
fromNotification: true
});

if (pushHandler === undefined) {
fail("testable export does not contain handlePushNotificationIfNeeded");
}
testSaga(pushHandler, false)
testSaga(handlePushNotificationIfNeeded, false)
.next()
.select(pendingMessageStateSelector)
.next(mockedPendingMessageState)
Expand All @@ -96,10 +89,7 @@ describe("maybeHandlePendingBackgroundActions", () => {
fromNotification: true
});

if (pushHandler === undefined) {
fail("testable export does not contain pushHandler");
}
testSaga(pushHandler, true)
testSaga(handlePushNotificationIfNeeded, true)
.next()
.select(pendingMessageStateSelector)
.next(mockedPendingMessageState)
Expand All @@ -123,10 +113,7 @@ describe("maybeHandlePendingBackgroundActions", () => {
fromNotification: true
});

if (pushHandler === undefined) {
fail("testable export does not contain pushHandler");
}
testSaga(pushHandler, false)
testSaga(handlePushNotificationIfNeeded, false)
.next()
.select(pendingMessageStateSelector)
.next(mockedPendingMessageState)
Expand All @@ -145,10 +132,7 @@ describe("maybeHandlePendingBackgroundActions", () => {
});

it("does nothing if there are not pending messages", () => {
if (pushHandler === undefined) {
fail("testable export does not contain pushHandler");
}
testSaga(pushHandler, false)
testSaga(handlePushNotificationIfNeeded, false)
.next()
.select(pendingMessageStateSelector)
.next(null)
Expand Down
35 changes: 1 addition & 34 deletions ts/features/pushNotifications/sagas/common.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { call, put, select } from "typed-redux-saga/macro";
import NavigationService from "../../../navigation/NavigationService";
import { navigateToMainNavigatorAction } from "../../../store/actions/navigation";
import { isTestEnv } from "../../../utils/environment";
import { handleStoredLinkingUrlIfNeeded } from "../../linking/sagas";
import { resetMessageArchivingAction } from "../../messages/store/actions/archiving";
import { isArchivingDisabledSelector } from "../../messages/store/reducers/archiving";
import { trackNotificationPermissionsStatus } from "../analytics";
Expand Down Expand Up @@ -52,32 +50,7 @@ export function* updateNotificationPermissionsIfNeeded(
}
}

/**
* this method is used to handle all actions that
* are triggered when the app is resumed from scratch or transitions from
* background to foreground, and also need to be handled
* later on in the app's life cycle.
*
* two examples are Universal/App Links and Push Notifications, which
* can transition the app from background or from a closed state to foreground,
* and need to be handled once the app has finished loading/initializing.
*/
export function* maybeHandlePendingBackgroundActions(
shouldResetToMainNavigator: boolean = false
) {
// check if we have a stored linking URL to process
if (yield* call(handleStoredLinkingUrlIfNeeded)) {
return true;
}
// Check if we have a pending notification message
if (yield* call(handlePushNotificationIfNeeded, shouldResetToMainNavigator)) {
return true;
}

return false;
}

function* handlePushNotificationIfNeeded(
export function* handlePushNotificationIfNeeded(
shouldResetToMainNavigator: boolean = false
) {
const pendingMessageState = yield* select(pendingMessageStateSelector);
Expand Down Expand Up @@ -115,9 +88,3 @@ function* handlePushNotificationIfNeeded(
}
return false;
}

export const testable = isTestEnv
? {
handlePushNotificationIfNeeded
}
: undefined;
2 changes: 1 addition & 1 deletion ts/features/wallet/saga/handleWalletUpdateSaga.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { put } from "typed-redux-saga/macro";
import { getCdcStatusWallet } from "../../bonus/cdc/wallet/store/actions";
import { cgnDetails } from "../../bonus/cgn/store/actions/details";
import { idPayWalletGet } from "../../idpay/wallet/store/actions";
import { getPaymentsWalletUserMethods } from "../../payments/wallet/store/actions";
import { getCdcStatusWallet } from "../../bonus/cdc/wallet/store/actions";

/**
* This saga handles the update of the wallet screen
Expand Down
12 changes: 2 additions & 10 deletions ts/navigation/AppStackNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ import { idPayLinkingOptions } from "../features/idpay/common/navigation/linking
import { IngressScreen } from "../features/ingress/screens/IngressScreen";
import { ITW_ROUTES } from "../features/itwallet/navigation/routes";
import { useItwLinkingOptions } from "../features/itwallet/navigation/useItwLinkingOptions";
import { storeLinkingUrl } from "../features/linking/actions";
import { MESSAGES_ROUTES } from "../features/messages/navigation/routes";
import { SERVICES_ROUTES } from "../features/services/common/navigation/routes";
import { SETTINGS_ROUTES } from "../features/settings/common/navigation/routes";
import { processUtmLink } from "../features/utmLink";
import { startApplicationInitialization } from "../store/actions/application";
import { setDebugCurrentRouteName } from "../store/actions/debug";
import { storeLinkingUrl } from "../features/linking/actions";
import { useIODispatch, useIOSelector, useIOStore } from "../store/hooks";
import { trackScreen } from "../store/middlewares/navigation";
import { isCGNEnabledAfterLoadSelector } from "../store/reducers/backendStatus/remoteConfig";
Expand Down Expand Up @@ -150,15 +150,7 @@ const InnerNavigationContainer = (props: InnerNavigationContainerProps) => {
[ROUTES.PAGE_NOT_FOUND]: "*"
}
},
subscribe: linkingSubscription(dispatch, store),
getInitialURL: async () => {
const initialUrl = await Linking.getInitialURL();
// check if the url contains main/wallet to remap the url
if (initialUrl && /\/main\/wallet(?:[/?#]|$)/.test(initialUrl)) {
return initialUrl.replace(/\/main\/wallet(?=\/|[?#]|$)/, "");
}
return initialUrl;
}
subscribe: linkingSubscription(dispatch, store)
};

/**
Expand Down
8 changes: 8 additions & 0 deletions ts/navigation/linkingSubscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { resetMessageArchivingAction } from "../features/messages/store/actions/
import { isArchivingDisabledSelector } from "../features/messages/store/reducers/archiving";
import { isSendAARLink } from "../features/pn/aar/utils/deepLinking";
import { processUtmLink } from "../features/utmLink";
import { walletUpdate } from "../features/wallet/store/actions";
import { shouldTriggerWalletUpdate } from "../utils/deepLinkUtils";
import { GlobalState } from "../store/reducers/types";
import { initiateAarFlow } from "../features/pn/aar/store/actions";

Expand All @@ -29,6 +31,12 @@ export const linkingSubscription =
if (isSendAARLink(state, url)) {
dispatch(initiateAarFlow({ aarUrl: url }));
}

// Trigger wallet update for external Universal Links and specific internal paths
// when the user is authenticated and the app is already running
if (shouldTriggerWalletUpdate(url)) {
dispatch(walletUpdate());
}
} else {
// If we are not logged in, we store the URL to be processed later
dispatch(storeLinkingUrl(url));
Expand Down
28 changes: 28 additions & 0 deletions ts/sagas/backgroundActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { call } from "typed-redux-saga/macro";
import { handleStoredLinkingUrlIfNeeded } from "../features/linking/sagas";
import { handlePushNotificationIfNeeded } from "../features/pushNotifications/sagas/common";

/**
* this method is used to handle all actions that
* are triggered when the app is resumed from scratch or transitions from
* background to foreground, and also need to be handled
* later on in the app's life cycle.
*
* two examples are Universal/App Links and Push Notifications, which
* can transition the app from background or from a closed state to foreground,
* and need to be handled once the app has finished loading/initializing.
*/
export function* maybeHandlePendingBackgroundActions(
shouldResetToMainNavigator: boolean = false
) {
// check if we have a stored linking URL to process
if (yield* call(handleStoredLinkingUrlIfNeeded)) {
return true;
}
// Check if we have a pending notification message
if (yield* call(handlePushNotificationIfNeeded, shouldResetToMainNavigator)) {
return true;
}

return false;
}
15 changes: 8 additions & 7 deletions ts/sagas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
*/
import { all, call } from "typed-redux-saga/macro";
import versionInfoSaga from "../common/versionInfo/saga/versionInfo";
import { watchTokenRefreshSaga } from "../features/authentication/fastLogin/saga/tokenRefreshSaga";
import { watchPendingActionsSaga } from "../features/authentication/fastLogin/saga/pendingActionsSaga";
import { watchZendeskSupportSaga } from "../features/zendesk/saga";
import { zendeskEnabled } from "../config";
import { watchUtmLinkSaga } from "../features/utmLink/saga";
import { watchLogoutSaga } from "../features/authentication/common/saga/watchLogoutSaga";
import { watchPendingActionsSaga } from "../features/authentication/fastLogin/saga/pendingActionsSaga";
import { watchTokenRefreshSaga } from "../features/authentication/fastLogin/saga/tokenRefreshSaga";
import connectivityStatusSaga from "../features/connectivity/saga";
import { watchIdentification } from "../features/identification/sagas";
import { watchLogoutSaga } from "../features/authentication/common/saga/watchLogoutSaga";
import { watchUtmLinkSaga } from "../features/utmLink/saga";
import { watchWalletSaga } from "../features/wallet/saga";
import { watchZendeskSupportSaga } from "../features/zendesk/saga";
import backendStatusSaga from "./backendStatus";
import { watchContentSaga } from "./contentLoaders";
import { loadSystemPreferencesSaga } from "./preferences";
import { startupSaga } from "./startup";
import { removePersistedStatesSaga } from "./removePersistedStates";

import { startupSaga } from "./startup";
import { watchApplicationActivitySaga } from "./startup/watchApplicationActivitySaga";

export default function* root() {
Expand All @@ -34,6 +34,7 @@ export default function* root() {
call(watchPendingActionsSaga),
call(watchUtmLinkSaga),
call(watchLogoutSaga),
call(watchWalletSaga),
zendeskEnabled ? call(watchZendeskSupportSaga) : undefined
]);
}
6 changes: 1 addition & 5 deletions ts/sagas/startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ import { watchAbortOnboardingSaga } from "../features/onboarding/saga/watchAbort
import { watchPaymentsSaga } from "../features/payments/common/saga";
import { watchAarFlowSaga } from "../features/pn/aar/saga/watchAARFlowSaga";
import { watchPnSaga } from "../features/pn/store/sagas/watchPnSaga";
import { maybeHandlePendingBackgroundActions } from "../features/pushNotifications/sagas/common";
import { notificationPermissionsListener } from "../features/pushNotifications/sagas/notificationPermissionsListener";
import { profileAndSystemNotificationsPermissions } from "../features/pushNotifications/sagas/profileAndSystemNotificationsPermissions";
import { pushNotificationTokenUpload } from "../features/pushNotifications/sagas/pushNotificationTokenUpload";
Expand All @@ -98,7 +97,6 @@ import { loadUserDataProcessing } from "../features/settings/common/store/action
import { isProfileFirstOnBoarding } from "../features/settings/common/store/utils/guards";
import { handleApplicationStartupTransientError } from "../features/startup/sagas";
import { watchTrialSystemSaga } from "../features/trialSystem/store/sagas/watchTrialSystemSaga";
import { watchWalletSaga } from "../features/wallet/saga";
import {
watchGetZendeskTokenSaga,
watchZendeskGetSessionSaga
Expand Down Expand Up @@ -142,6 +140,7 @@ import { navigateToActiveSessionLogin } from "../features/authentication/activeS
import { showSessionExpirationBlockingScreenSelector } from "../features/authentication/activeSessionLogin/store/selectors";
import { watchCdcSaga } from "../features/bonus/cdc/common/saga";
import { watchMessagesSaga } from "../features/messages/saga";
import { maybeHandlePendingBackgroundActions } from "./backgroundActions";
import { previousInstallationDataDeleteSaga } from "./installation";
import {
askMixpanelOptIn,
Expand Down Expand Up @@ -614,9 +613,6 @@ export function* initializeApplicationSaga(
// active session login watcher
yield* fork(watchActiveSessionLoginSaga);

// Start wathing new wallet sagas
yield* fork(watchWalletSaga);

// Here we can be sure that the session information is loaded and valid
const bpdToken = maybeSessionInformation.value.bpdToken as string;

Expand Down
2 changes: 1 addition & 1 deletion ts/sagas/startup/watchApplicationActivitySaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
StartupStatusEnum,
isStartupLoaded
} from "../../store/reducers/startup";
import { maybeHandlePendingBackgroundActions } from "../../features/pushNotifications/sagas/common";
import { maybeHandlePendingBackgroundActions } from "../backgroundActions";
import { areTwoMinElapsedFromLastActivity } from "../../features/authentication/fastLogin/store/actions/sessionRefreshActions";
import { isFastLoginEnabledSelector } from "../../features/authentication/fastLogin/store/selectors";
import { IdentificationBackActionType } from "../../features/identification/store/reducers";
Expand Down
18 changes: 18 additions & 0 deletions ts/utils/deepLinkUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const CGN_ROUTE_PATH = "ioit://cgn-details/detail";
// Internal deep link paths that require wallet update
const WALLET_UPDATE_PATHS = [
CGN_ROUTE_PATH,
"ioit://main/wallet",
"ioit://idpay/initiative"
];

/**
* Check if the URL requires wallet update based on specific criteria
* - External Universal Links from web
* - Specific internal paths that need wallet refresh
*/
export const shouldTriggerWalletUpdate = (url: string): boolean =>
WALLET_UPDATE_PATHS.some(path => url.startsWith(path));

export const isCGNLinking = (url: string): boolean =>
url.startsWith(CGN_ROUTE_PATH);
Loading