Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IOPID-2566] Refactor AuthErrorComponent #6565

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -171,6 +171,21 @@ exports[`featuresPersistor should match snapshot 1`] = `
"nativeLogin": {
"enabled": true,
},
"spidLogin": {
"nativeLogin": {
"requestInfo": {
"nativeAttempts": 0,
"requestState": "LOADING",
},
},
"standardLogin": {
"requestInfo": {
"requestState": {
"kind": "PotNoneLoading",
},
},
},
},
"testLogin": {
"kind": "idle",
},
Expand Down
8 changes: 7 additions & 1 deletion ts/features/common/store/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,18 @@ import {
landingScreenBannersReducer,
LandingScreenBannerState
} from "../../../landingScreenMultiBanner/store/reducer";
import {
spidLoginReducer,
SpidLoginState
} from "../../../spidLogin/store/reducers";

type LoginFeaturesState = {
testLogin: TestLoginState;
nativeLogin: NativeLoginState;
fastLogin: FastLoginState;
cieLogin: CieLoginState & PersistPartial;
loginInfo: LoginInfoState;
spidLogin: SpidLoginState;
};

export type FeaturesState = {
Expand Down Expand Up @@ -96,7 +101,8 @@ const rootReducer = combineReducers<FeaturesState, Action>({
nativeLogin: nativeLoginReducer,
fastLogin: fastLoginReducer,
cieLogin: cieLoginPersistor,
loginInfo: loginInfoReducer
loginInfo: loginInfoReducer,
spidLogin: spidLoginReducer
}),
wallet: walletReducer,
fims: fimsReducer,
Expand Down
24 changes: 24 additions & 0 deletions ts/features/spidLogin/store/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ActionType, createStandardAction } from "typesafe-actions";
import * as pot from "@pagopa/ts-commons/lib/pot";
import { ErrorType, NativeLoginRequestInfo } from "../../types";

export const setNativeLoginRequestInfo = createStandardAction(
"SET_NATIVE_LOGIN_REQUEST_INFO"
)<NativeLoginRequestInfo>();
export const incrementNativeLoginNativeAttempts = createStandardAction(
"INCREMENT_NATIVE_LOGIN_NATIVE_ATTEMPTS"
)();
export const setStandardLoginRequestState = createStandardAction(
"SET_STNDARD_LOGIN_REQUEST_STATE"
)<pot.Pot<true, ErrorType>>();
export const setStandardLoginInLoadingState = createStandardAction(
"SET_STANDARD_LOGIN_IN_LOADING_STATE"
)();
export const resetSpidLoginState = createStandardAction("RESET_LOGIN_STATE")();

export type SpidConfigActions =
| ActionType<typeof setNativeLoginRequestInfo>
| ActionType<typeof incrementNativeLoginNativeAttempts>
| ActionType<typeof setStandardLoginRequestState>
| ActionType<typeof setStandardLoginInLoadingState>
| ActionType<typeof resetSpidLoginState>;
87 changes: 87 additions & 0 deletions ts/features/spidLogin/store/reducers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { getType } from "typesafe-actions";
import * as pot from "@pagopa/ts-commons/lib/pot";
import { Action } from "../../../../store/actions/types";
import { StandardLoginRequestInfo, NativeLoginRequestInfo } from "../../types";
import {
incrementNativeLoginNativeAttempts,
setStandardLoginRequestState,
setNativeLoginRequestInfo,
setStandardLoginInLoadingState,
resetSpidLoginState
} from "../actions";

export type SpidLoginState = {
nativeLogin: {
requestInfo: NativeLoginRequestInfo;
};
standardLogin: {
requestInfo: StandardLoginRequestInfo;
};
};

const spidLoginInitialState: SpidLoginState = {
nativeLogin: {
requestInfo: {
requestState: "LOADING",
nativeAttempts: 0
}
},
standardLogin: {
requestInfo: {
requestState: pot.noneLoading
}
}
};

export const spidLoginReducer = (
state: SpidLoginState = spidLoginInitialState,
action: Action
): SpidLoginState => {
switch (action.type) {
case getType(setNativeLoginRequestInfo):
return {
...state,
nativeLogin: {
...state.nativeLogin,
requestInfo: action.payload
}
};
case getType(incrementNativeLoginNativeAttempts):
return {
...state,
nativeLogin: {
...state.nativeLogin,
requestInfo: {
requestState: "LOADING",
nativeAttempts: state.nativeLogin.requestInfo.nativeAttempts + 1
}
}
};
case getType(setStandardLoginRequestState):
return {
...state,
standardLogin: {
...state.standardLogin,
requestInfo: {
...state.standardLogin.requestInfo,
requestState: action.payload
}
}
};
case getType(setStandardLoginInLoadingState):
return {
...state,
standardLogin: {
...state.standardLogin,
requestInfo: {
...state.standardLogin.requestInfo,
requestState: pot.noneLoading
}
}
};
case getType(resetSpidLoginState):
return spidLoginInitialState;
default:
return state;
}
};
15 changes: 15 additions & 0 deletions ts/features/spidLogin/store/selectors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createSelector } from "reselect";
import { GlobalState } from "../../../../store/reducers/types";

export const spidLoginSelector = (state: GlobalState) =>
state.features.loginFeatures.spidLogin;

export const nativeLoginRequestInfoSelector = createSelector(
spidLoginSelector,
({ nativeLogin }) => nativeLogin.requestInfo
);

export const standardLoginRequestInfoSelector = createSelector(
spidLoginSelector,
({ standardLogin }) => standardLogin.requestInfo
);
26 changes: 26 additions & 0 deletions ts/features/spidLogin/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as pot from "@pagopa/ts-commons/lib/pot";

export enum ErrorType {
"LOADING_ERROR" = "LOADING_ERROR",
"LOGIN_ERROR" = "LOGIN_ERROR"
}

export type RequestInfoPositiveStates = {
requestState: "LOADING" | "AUTHORIZED" | "AUTHORIZING";
nativeAttempts: number;
};

export type RequestInfoError = {
requestState: "ERROR";
errorType: ErrorType;
errorCodeOrMessage?: string;
nativeAttempts: number;
};

export type NativeLoginRequestInfo =
| RequestInfoPositiveStates
| RequestInfoError;

export type StandardLoginRequestInfo = {
requestState: pot.Pot<true, ErrorType>;
};
23 changes: 17 additions & 6 deletions ts/screens/authentication/AuthErrorScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import { useIONavigation } from "../../navigation/params/AppParamsList";
import ROUTES from "../../navigation/routes";
import { CieIdLoginProps } from "../../features/cieLogin/components/CieIdLoginWebView";
import { AuthenticationParamsList } from "../../navigation/params/AuthenticationParamsList";
import { useIODispatch } from "../../store/hooks";
import {
incrementNativeLoginNativeAttempts,
resetSpidLoginState,
setStandardLoginInLoadingState
} from "../../features/spidLogin/store/actions";
import { UnlockAccessProps } from "./UnlockAccessComponent";
import AuthErrorComponent from "./components/AuthErrorComponent";

Expand All @@ -17,12 +23,11 @@ type CommonAuthErrorScreenProps = {

type SpidProps = {
authMethod: "SPID";
onRetry: () => void;
isNativeLogin?: boolean;
};

type CieIdProps = {
authMethod: "CIE_ID";
onRetry?: () => void;
params: CieIdLoginProps;
};

Expand All @@ -40,6 +45,7 @@ const authScreenByAuthMethod = {
};

const AuthErrorScreen = () => {
const dispatch = useIODispatch();
const route =
useRoute<Route<typeof ROUTES.AUTH_ERROR_SCREEN, AuthErrorScreenProps>>();
const { errorCodeOrMessage, authMethod, authLevel } = route.params;
Expand All @@ -61,17 +67,22 @@ const AuthErrorScreen = () => {
}, [authMethod, route.params]);

const onRetry = useCallback(() => {
if (authMethod === "SPID" || authMethod === "CIE_ID") {
route.params.onRetry?.();
if (authMethod === "SPID") {
dispatch(
route.params.isNativeLogin
? incrementNativeLoginNativeAttempts()
: setStandardLoginInLoadingState()
);
}
navigation.navigate(ROUTES.AUTHENTICATION, getNavigationParams());
}, [authMethod, navigation, route.params, getNavigationParams]);
}, [authMethod, navigation, route.params, getNavigationParams, dispatch]);

const onCancel = useCallback(() => {
dispatch(resetSpidLoginState());
navigation.navigate(ROUTES.AUTHENTICATION, {
screen: ROUTES.AUTHENTICATION_LANDING
});
}, [navigation]);
}, [navigation, dispatch]);

return (
<AuthErrorComponent
Expand Down
46 changes: 24 additions & 22 deletions ts/screens/authentication/IdpLoginScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ import {
handleSendAssistanceLog
} from "../../utils/supportAssistance";
import { getUrlBasepath } from "../../utils/url";
import { standardLoginRequestInfoSelector } from "../../features/spidLogin/store/selectors";
import { setStandardLoginRequestState } from "../../features/spidLogin/store/actions";
import { originSchemasWhiteList } from "./originSchemasWhiteList";

enum ErrorType {
Expand Down Expand Up @@ -79,7 +81,9 @@ const styles = StyleSheet.create({
*/
const IdpLoginScreen = () => {
const dispatch = useIODispatch();
const { navigate } = useIONavigation();
// The choice was made to use `replace` instead of `navigate` because the former unmounts the current screen,
// ensuring the re-execution of the `useLollipopLoginSource` hook.
const { replace } = useIONavigation();
const selectedIdp = useIOSelector(selectedIdentityProviderSelector, _isEqual);
const selectedIdpTextData = useIOSelector(
idpContextualHelpDataFromIdSelector(selectedIdp?.id),
Expand All @@ -95,25 +99,27 @@ const IdpLoginScreen = () => {
_isEqual
);

const [requestState, setRequestState] = useState<pot.Pot<true, ErrorType>>(
pot.noneLoading
);
const { requestState } = useIOSelector(standardLoginRequestInfoSelector);
const [errorCodeOrMessage, setErrorCodeOrMessage] = useState<
string | undefined
>(undefined);
const [loginTrace, setLoginTrace] = useState<string | undefined>(undefined);

const setRequestState = useCallback(
(req: pot.Pot<true, ErrorType>) => {
dispatch(setStandardLoginRequestState(req));
},
[dispatch]
);

const handleOnLollipopCheckFailure = useCallback(() => {
setRequestState(pot.noneError(ErrorType.LOGIN_ERROR));
}, []);
}, [setRequestState]);

const idpId = loggedOutWithIdpAuth?.idp.id;
const loginUri = idpId ? getIdpLoginUri(idpId, 2) : undefined;
const {
retryLollipopLogin,
shouldBlockUrlNavigationWhileCheckingLollipop,
webviewSource
} = useLollipopLoginSource(handleOnLollipopCheckFailure, loginUri);
const { shouldBlockUrlNavigationWhileCheckingLollipop, webviewSource } =
useLollipopLoginSource(handleOnLollipopCheckFailure, loginUri);

const choosenTool = useMemo(
() => assistanceToolRemoteConfig(assistanceToolConfig),
Expand All @@ -138,7 +144,7 @@ const IdpLoginScreen = () => {
setRequestState(pot.noneError(ErrorType.LOADING_ERROR));
}
},
[loggedOutWithIdpAuth?.idp.id]
[loggedOutWithIdpAuth?.idp.id, setRequestState]
);

const handleLoginFailure = useCallback(
Expand Down Expand Up @@ -170,7 +176,7 @@ const IdpLoginScreen = () => {
setRequestState(pot.noneError(ErrorType.LOGIN_ERROR));
setErrorCodeOrMessage(code || message);
},
[dispatch, choosenTool, idp]
[dispatch, choosenTool, idp, setRequestState]
);

const handleLoginSuccess = useCallback(
Expand All @@ -183,11 +189,6 @@ const IdpLoginScreen = () => {
[choosenTool, dispatch, idp]
);

const onRetryButtonPressed = useCallback((): void => {
setRequestState(pot.noneLoading);
retryLollipopLogin();
}, [retryLollipopLogin]);

const handleNavigationStateChange = useCallback(
(event: WebViewNavigation) => {
const url = event.url;
Expand All @@ -212,7 +213,7 @@ const IdpLoginScreen = () => {
event.loading || isAssertion ? pot.noneLoading : pot.some(true)
);
},
[dispatch, loginTrace]
[dispatch, loginTrace, setRequestState]
);

const handleShouldStartLoading = useCallback(
Expand Down Expand Up @@ -265,16 +266,17 @@ const IdpLoginScreen = () => {
};

const navigateToAuthErrorScreen = useCallback(() => {
navigate(ROUTES.AUTHENTICATION, {
// The choice was made to use `replace` instead of `navigate` because the former unmounts the current screen,
// ensuring the re-execution of the `useLollipopLoginSource` hook.
replace(ROUTES.AUTHENTICATION, {
Copy link
Member

Choose a reason for hiding this comment

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

As above.

screen: ROUTES.AUTH_ERROR_SCREEN,
params: {
errorCodeOrMessage,
authMethod: "SPID",
authLevel: "L2",
onRetry: onRetryButtonPressed
authLevel: "L2"
}
});
}, [errorCodeOrMessage, onRetryButtonPressed, navigate]);
}, [errorCodeOrMessage, replace]);

useEffect(() => {
if (pot.isError(requestState)) {
Expand Down
Loading
Loading