Skip to content

Commit

Permalink
chore: [IOPID-2050] Add session refresh Feature Flag (#6045)
Browse files Browse the repository at this point in the history
> [!Warning] 
> This PR depends on
pagopa/io-services-metadata#815
> When pagopa/io-services-metadata#815 will be
released i need to change the version into the "content_specs" in
package.json


## List of changes proposed in this pull request
- Edit package.json with correct version of io-services-metadata
- Add selector to remote FF (`fastLoginSessionRefreshFFEnabled`)
- Add selector to control if fast login is active
(`isFastLoginSessionRefreshEnabledSelector`)
- Add toggle to enabled/disabled this feature (this toggle now is
visible only if the remote FF is active because otherwise it would not
display the feature, so it will be displayed in the next pr (is
controlled using`isFastLoginSessionRefreshToggleActiveSelector` -> is a
persisted value)

## How to test
- Run the application using the latest version of the io-dev-api-server
- delete [this
line](https://github.com/pagopa/io-app/blob/128dfab2bfdcc7bc7ba1783daf7eb05ccfa18b23/ts/screens/profile/DeveloperModeSection.tsx#L368)
to see the toggle
- follow the video 

## Demo
<video
src="https://github.com/user-attachments/assets/d937f98f-6e0c-4ea9-b285-42c8b0d8b2a2"/>

---------

Co-authored-by: Fabio Bombardi <[email protected]>
  • Loading branch information
Ladirico and shadowsheep1 authored Jul 29, 2024
1 parent 8c0fe6b commit e36579b
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 7 deletions.
1 change: 1 addition & 0 deletions locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ profile:
idpayTestAlert: This change requires app reboot
designSystemEnvironment: Experimental Design System
newScanSection: New 'Scan' feature
sessionRefresh: Activate Session Refresh
appVersion: Version
backendVersion: Backend Version
debugMode: Debug mode
Expand Down
1 change: 1 addition & 0 deletions locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ profile:
titleSection: Partecipa alla sperimentazione
designSystemEnvironment: Design System sperimentale
newScanSection: Nuova funzione 'Inquadra'
sessionRefresh: Attiva il refresh della sessione
appVersion: Versione
backendVersion: Versione Backend
debugMode: Modalità debug
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "2.67.0-rc.3",
"io_backend_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_backend.yaml",
"io_public_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_public.yaml",
"io_content_specs": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.35/definitions.yml",
"io_content_specs": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.36/definitions.yml",
"io_cgn_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_cgn.yaml",
"io_cgn_merchants_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_cgn_operator_search.yaml",
"api_fci": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_io_sign.yaml",
Expand All @@ -13,9 +13,9 @@
"api_cdc": "assets/CdcSwagger.yml",
"pagopa_api": "assets/paymentManager/spec.json",
"fims_swagger": "assets/FimsSwager.yml",
"pagopa_api_walletv2": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.34/bonus/specs/bpd/pm/walletv2.json",
"pagopa_cobadge_configuration": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.34/pagopa/cobadge/abi_definitions.yml",
"pagopa_privative_configuration": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.34/pagopa/privative/definitions.yml",
"pagopa_api_walletv2": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.36/bonus/specs/bpd/pm/walletv2.json",
"pagopa_cobadge_configuration": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.36/pagopa/cobadge/abi_definitions.yml",
"pagopa_privative_configuration": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.36/pagopa/privative/definitions.yml",
"idpay_api": "https://raw.githubusercontent.com/pagopa/cstar-infrastructure/v6.9.1/src/domains/idpay-app/api/idpay_appio_full/openapi.appio.full.yml",
"services_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_services_app_backend.yaml",
"lollipop_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_lollipop_first_consumer.yaml",
Expand Down
2 changes: 2 additions & 0 deletions ts/features/fastLogin/store/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { fastLoginOptInActions } from "./optInActions";
import { SecurityAdviceActions } from "./securityAdviceActions";
import { automaticSessionRefreshActions } from "./sessionRefreshActions";
import { FastLoginTokenRefreshActions } from "./tokenRefreshActions";

export type FastLoginActions =
| fastLoginOptInActions
| automaticSessionRefreshActions
| FastLoginTokenRefreshActions
| SecurityAdviceActions;
10 changes: 10 additions & 0 deletions ts/features/fastLogin/store/actions/sessionRefreshActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ActionType, createStandardAction } from "typesafe-actions";
import { AutomaticSessionRefreshState } from "../reducers/sessionRefreshReducer";

export const setAutomaticSessionRefresh = createStandardAction(
"SET_AUTOMATIC_SESSION_REFRESH_AFTER_TWO_MIN_BACKGROUND"
)<AutomaticSessionRefreshState>();

export type automaticSessionRefreshActions = ActionType<
typeof setAutomaticSessionRefresh
>;
6 changes: 6 additions & 0 deletions ts/features/fastLogin/store/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@ import {
SecurityAdviceAcknowledgedState,
securityAdviceAcknowledgedPersistor
} from "./securityAdviceReducer";
import {
automaticSessionRefreshPersistor,
AutomaticSessionRefreshState
} from "./sessionRefreshReducer";

export type FastLoginState = {
optIn: FastLoginOptInState & PersistPartial;
automaticSessionRefresh: AutomaticSessionRefreshState & PersistPartial;
tokenRefreshHandler: FastLoginTokenRefreshState;
securityAdviceAcknowledged: SecurityAdviceAcknowledgedState & PersistPartial;
};

export const fastLoginReducer = combineReducers<FastLoginState, Action>({
optIn: fastLoginOptInPersistor,
automaticSessionRefresh: automaticSessionRefreshPersistor,
tokenRefreshHandler: FastLoginTokenRefreshReducer,
securityAdviceAcknowledged: securityAdviceAcknowledgedPersistor
});
50 changes: 50 additions & 0 deletions ts/features/fastLogin/store/reducers/sessionRefreshReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
import { PersistConfig, persistReducer } from "redux-persist";
import { getType } from "typesafe-actions";
import { setAutomaticSessionRefresh } from "../actions/sessionRefreshActions";
import { Action } from "../../../../store/actions/types";
import {
logoutFailure,
logoutSuccess
} from "../../../../store/actions/authentication";

export type AutomaticSessionRefreshState = {
enabled: boolean | undefined;
};

export const automaticSessionRefreshInitialState: AutomaticSessionRefreshState =
{
enabled: undefined
};

const AutomaticSessionRefreshReducer = (
state: AutomaticSessionRefreshState = automaticSessionRefreshInitialState,
action: Action
): AutomaticSessionRefreshState => {
switch (action.type) {
case getType(logoutSuccess):
case getType(logoutFailure):
return automaticSessionRefreshInitialState;
case getType(setAutomaticSessionRefresh):
return {
...state,
enabled: action.payload.enabled
};
default:
return state;
}
};

const CURRENT_REDUX_SESSION_REFRESH_STORE_VERSION = -1;

const persistConfig: PersistConfig = {
key: "sessionRefresh",
storage: AsyncStorage,
version: CURRENT_REDUX_SESSION_REFRESH_STORE_VERSION,
whitelist: ["enabled"]
};

export const automaticSessionRefreshPersistor = persistReducer<
AutomaticSessionRefreshState,
Action
>(persistConfig, AutomaticSessionRefreshReducer);
43 changes: 41 additions & 2 deletions ts/features/fastLogin/store/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { isPropertyWithMinAppVersionEnabled } from "../../../../store/reducers/f
export const fastLoginOptInSelector = (state: GlobalState) =>
state.features.loginFeatures.fastLogin.optIn;

export const isAutomaticSessionRefreshToggleActiveSelector = (
state: GlobalState
) => !!state.features.loginFeatures.fastLogin.automaticSessionRefresh.enabled;

const securityAdviceAcknowledgedSelector = (state: GlobalState) =>
state.features.loginFeatures.fastLogin.securityAdviceAcknowledged;

Expand Down Expand Up @@ -36,6 +40,25 @@ export const fastLoginOptInFFEnabled = createSelector(
})
);

/**
* return the remote config about AutomaticSessionRefresh enabled/disabled
* based on a minumum version of the app.
* if there is no data, false is the default value -> (AutomaticSessionRefresh disabled)
*/
export const automaticSessionRefreshFFEnabled = createSelector(
backendStatusSelector,
backendStatus =>
isPropertyWithMinAppVersionEnabled({
backendStatus,
mainLocalFlag: fastLoginEnabled,
configPropertyName: "fastLogin",
// In this case I do not have a local feature flag, but locally the
// value is chosen using a toogle. So I set this required field as true
optionalLocalFlag: true,
optionalConfig: "sessionRefresh"
})
);

const isFastLoginOptInEnabledSelector = createSelector(
fastLoginOptInFFEnabled,
fastLoginOptInSelector,
Expand Down Expand Up @@ -74,13 +97,29 @@ export const isFastLoginEnabledSelector = createSelector(
(fastloginFFEnabled, optInEnabled) => fastloginFFEnabled && !!optInEnabled
);

/**
* if the fast login is active and has been chosen by the user (opt-in is true)
* then if the remote FF of this functionality (automaticSessionRefreshFFEnabled)
* is active or the user has activated the toogle in the ‘dev’ section of
* the settings (isAutomaticSessionRefreshToggleActiveSelector), the user will
* see the implementation of the session refresh when
* returning to foreground after at least 2 minutes of background
*/
export const isAutomaticSessionRefreshEnabledSelector = createSelector(
isFastLoginEnabledSelector,
automaticSessionRefreshFFEnabled,
isAutomaticSessionRefreshToggleActiveSelector,
(isFastLoginEnabled, sessionRefresh, isSessionRefreshToggleActive) =>
isFastLoginEnabled && (sessionRefresh || isSessionRefreshToggleActive)
);

const fastLoginTokenRefreshHandlerSelector = (state: GlobalState) =>
state.features.loginFeatures.fastLogin.tokenRefreshHandler;

export const fastLoginPendingActionsSelector = createSelector(
fastLoginTokenRefreshHandlerSelector,
fastLoginTokenRefreshHandlerSelector =>
uniqWith(fastLoginTokenRefreshHandlerSelector.pendingActions, isEqual)
fastLoginTokenRefresh =>
uniqWith(fastLoginTokenRefresh.pendingActions, isEqual)
);

export const isFastLoginUserInteractionNeededForSessionExpiredSelector = (
Expand Down
34 changes: 33 additions & 1 deletion ts/screens/profile/DeveloperModeSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import I18n from "../../i18n";
import { AlertModal } from "../../components/ui/AlertModal";
import { LightModalContext } from "../../components/ui/LightModal";
import { isPlaygroundsEnabled } from "../../config";
import { isFastLoginEnabledSelector } from "../../features/fastLogin/store/selectors";
import {
automaticSessionRefreshFFEnabled,
isAutomaticSessionRefreshToggleActiveSelector,
isFastLoginEnabledSelector
} from "../../features/fastLogin/store/selectors";
import { lollipopPublicKeySelector } from "../../features/lollipop/store/reducers/lollipop";
import { toThumbprint } from "../../features/lollipop/utils/crypto";
import { notificationsInstallationSelector } from "../../features/pushNotifications/store/reducers/installation";
Expand Down Expand Up @@ -55,6 +59,7 @@ import { getDeviceId } from "../../utils/device";
import { isDevEnv } from "../../utils/environment";

import { ITW_ROUTES } from "../../features/itwallet/navigation/routes";
import { setAutomaticSessionRefresh } from "../../features/fastLogin/store/actions/sessionRefreshActions";
import DSEnableSwitch from "./components/DSEnableSwitch";

type PlaygroundsNavListItem = {
Expand Down Expand Up @@ -303,6 +308,21 @@ const DesignSystemSection = () => {
isNewScanSectionLocallyEnabledSelector
);

const isAutomaticSessionRefreshRemoteFFActive = useIOSelector(
automaticSessionRefreshFFEnabled
);

const isAutomaticSessionRefreshToggleActive = useIOSelector(
isAutomaticSessionRefreshToggleActiveSelector
);

const dispatchAutomaticSessionRefresh = React.useCallback(
(enabled: boolean) => {
dispatch(setAutomaticSessionRefresh({ enabled }));
},
[dispatch]
);

const onNewScanSectionToggle = (enabled: boolean) => {
dispatch(
preferencesNewScanSectionSetEnabled({
Expand Down Expand Up @@ -340,6 +360,18 @@ const DesignSystemSection = () => {
value={isNewScanSectionLocallyEnabled}
onSwitchValueChange={onNewScanSectionToggle}
/>
{/* this control isAutomaticSessionRefreshRemoteFFActive is a
workaround to hide this toogle before this task
(https://pagopa.atlassian.net/browse/IOPID-2051)
is completed because otherwise nothing would be activated using this toggle
*/}
{isAutomaticSessionRefreshRemoteFFActive && (
<ListItemSwitch
label={I18n.t("profile.main.sessionRefresh")}
value={isAutomaticSessionRefreshToggleActive}
onSwitchValueChange={dispatchAutomaticSessionRefresh}
/>
)}
</ContentWrapper>
);
};
Expand Down

0 comments on commit e36579b

Please sign in to comment.