Skip to content
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
11 changes: 11 additions & 0 deletions locales/en/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,11 @@
"contextualHelp": {
"title": "Startup problems?"
},
"offline_access_banner": {
"title": "Ti servono i documenti?",
"content": "Non attendere, sono sempre disponibili, anche se non hai connessione!",
"action": "Vai ai documenti"
},
"connection_lost": {
"title": "It looks like you don't have an active internet connection",
"description": "Check your connection and restart the app."
Expand Down Expand Up @@ -5337,6 +5342,12 @@
"footerAction": "Ricarica l'app IO"
}
},
"timeout": {
"alert": {
"content": " Hai bisogno di tornare alla versione completa di IO?",
Copy link
Member

Choose a reason for hiding this comment

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

Why do we have a trailing blank space? 🤔

"action": "Ricarica l’app IO"
}
},
"back_online": {
"alert": {
"content": "Sei di nuovo online. Per usare tutti i servizi ",
Expand Down
11 changes: 11 additions & 0 deletions locales/it/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,11 @@
"contextualHelp": {
"title": "Problemi di inizializzazione?"
},
"offline_access_banner": {
"title": "Ti servono i documenti?",
"content": "Non attendere, sono sempre disponibili, anche se non hai connessione!",
"action": "Vai ai documenti"
},
"connection_lost": {
"title": "Non hai una connessione a internet attiva",
"description": "Controlla la tua connessione e riavvia l’app."
Expand Down Expand Up @@ -5337,6 +5342,12 @@
"footerAction": "Ricarica l'app IO"
}
},
"timeout": {
"alert": {
"content": " Hai bisogno di tornare alla versione completa di IO?",
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

"action": "Ricarica l’app IO"
}
},
"back_online": {
"alert": {
"content": "Sei di nuovo online. Per usare tutti i servizi ",
Expand Down
24 changes: 20 additions & 4 deletions ts/components/screens/LoadingScreenContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* An ingress screen to choose the real first screen the user must navigate to.
*/
import {
Banner,
ContentWrapper,
H3,
IOColors,
Expand All @@ -18,6 +19,7 @@ import {
AnimatedPictogram,
IOAnimatedPictograms
} from "../ui/AnimatedPictogram";
import I18n from "../../i18n";

const styles = StyleSheet.create({
container: {
Expand All @@ -33,6 +35,7 @@ type LoadingScreenContentProps = WithTestID<{
children?: ReactNode;
headerVisible?: boolean;
animatedPictogramSource?: IOAnimatedPictograms;
banner?: { showBanner?: boolean; onPress: () => void };
}>;

export const LoadingScreenContent = (props: LoadingScreenContentProps) => {
Expand All @@ -42,7 +45,8 @@ export const LoadingScreenContent = (props: LoadingScreenContentProps) => {
children,
headerVisible,
testID,
animatedPictogramSource
animatedPictogramSource,
banner = { showBanner: false }
} = props;

useEffect(() => {
Expand All @@ -66,10 +70,10 @@ export const LoadingScreenContent = (props: LoadingScreenContentProps) => {
edges={headerVisible ? ["bottom"] : undefined}
testID={testID}
>
<ContentWrapper>
<ContentWrapper style={{ flex: 1 }}>
<VStack
space={SPACE_BETWEEN_SPINNER_AND_TEXT}
style={{ alignItems: "center" }}
style={{ alignItems: "center", flex: 1, justifyContent: "center" }}
>
<View
accessible={false}
Expand All @@ -93,9 +97,21 @@ export const LoadingScreenContent = (props: LoadingScreenContentProps) => {
>
{contentTitle}
</H3>
{children}
</VStack>
</ContentWrapper>
{children}
<ContentWrapper style={{ marginBottom: 16 }}>
{banner.showBanner && (
<Banner
pictogramName="identityCheck"
color="neutral"
title={I18n.t("startup.offline_access_banner.title")}
content={I18n.t("startup.offline_access_banner.content")}
action={I18n.t("startup.offline_access_banner.action")}
onPress={banner.onPress}
/>
)}
Comment on lines +103 to +113
Copy link
Member

@shadowsheep1 shadowsheep1 Sep 1, 2025

Choose a reason for hiding this comment

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

Since this screen is used app-wide, should we configure the banner externally instead of tying it to the startup version? This might give us more flexibility. What do you think?

</ContentWrapper>
</SafeAreaView>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ exports[`LoadingScreenContent should match the snapshot with title, a child, hea
<View
style={
{
"flex": 1,
"paddingHorizontal": 24,
}
}
Expand All @@ -399,8 +400,10 @@ exports[`LoadingScreenContent should match the snapshot with title, a child, hea
{
"alignItems": "center",
"display": "flex",
"flex": 1,
"flexDirection": "column",
"gap": 24,
"justifyContent": "center",
}
}
>
Expand Down Expand Up @@ -612,11 +615,19 @@ exports[`LoadingScreenContent should match the snapshot with title, a child, hea
>
Test Content Title
</Text>
<Text>
My test child
</Text>
</View>
</View>
<Text>
My test child
</Text>
<View
style={
{
"marginBottom": 16,
"paddingHorizontal": 24,
}
}
/>
</RNCSafeAreaView>
</View>
</View>
Expand Down Expand Up @@ -1021,6 +1032,7 @@ exports[`LoadingScreenContent should match the snapshot with title, a child, hea
<View
style={
{
"flex": 1,
"paddingHorizontal": 24,
}
}
Expand All @@ -1030,8 +1042,10 @@ exports[`LoadingScreenContent should match the snapshot with title, a child, hea
{
"alignItems": "center",
"display": "flex",
"flex": 1,
"flexDirection": "column",
"gap": 24,
"justifyContent": "center",
}
}
>
Expand Down Expand Up @@ -1243,11 +1257,19 @@ exports[`LoadingScreenContent should match the snapshot with title, a child, hea
>
Test Content Title
</Text>
<Text>
My test child
</Text>
</View>
</View>
<Text>
My test child
</Text>
<View
style={
{
"marginBottom": 16,
"paddingHorizontal": 24,
}
}
/>
</RNCSafeAreaView>
</View>
</View>
Expand Down Expand Up @@ -1652,6 +1674,7 @@ exports[`LoadingScreenContent should match the snapshot with title, no children,
<View
style={
{
"flex": 1,
"paddingHorizontal": 24,
}
}
Expand All @@ -1661,8 +1684,10 @@ exports[`LoadingScreenContent should match the snapshot with title, no children,
{
"alignItems": "center",
"display": "flex",
"flex": 1,
"flexDirection": "column",
"gap": 24,
"justifyContent": "center",
}
}
>
Expand Down Expand Up @@ -1876,6 +1901,14 @@ exports[`LoadingScreenContent should match the snapshot with title, no children,
</Text>
</View>
</View>
<View
style={
{
"marginBottom": 16,
"paddingHorizontal": 24,
}
}
/>
</RNCSafeAreaView>
</View>
</View>
Expand Down Expand Up @@ -2280,6 +2313,7 @@ exports[`LoadingScreenContent should match the snapshot with title, no children,
<View
style={
{
"flex": 1,
"paddingHorizontal": 24,
}
}
Expand All @@ -2289,8 +2323,10 @@ exports[`LoadingScreenContent should match the snapshot with title, no children,
{
"alignItems": "center",
"display": "flex",
"flex": 1,
"flexDirection": "column",
"gap": 24,
"justifyContent": "center",
}
}
>
Expand Down Expand Up @@ -2504,6 +2540,14 @@ exports[`LoadingScreenContent should match the snapshot with title, no children,
</Text>
</View>
</View>
<View
style={
{
"marginBottom": 16,
"paddingHorizontal": 24,
}
}
/>
</RNCSafeAreaView>
</View>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ exports[`IngressScreen Should match the snapshot 1`] = `
<View
style={
{
"flex": 1,
"paddingHorizontal": 24,
}
}
Expand All @@ -400,8 +401,10 @@ exports[`IngressScreen Should match the snapshot 1`] = `
{
"alignItems": "center",
"display": "flex",
"flex": 1,
"flexDirection": "column",
"gap": 24,
"justifyContent": "center",
}
}
>
Expand Down Expand Up @@ -557,6 +560,14 @@ exports[`IngressScreen Should match the snapshot 1`] = `
</Text>
</View>
</View>
<View
style={
{
"marginBottom": 16,
"paddingHorizontal": 24,
}
}
/>
</RNCSafeAreaView>
</View>
</View>
Expand Down
37 changes: 27 additions & 10 deletions ts/features/ingress/screens/IngressScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* An ingress screen to choose the real first screen the user must navigate to.
*/
import { memo, useEffect, useRef, useState } from "react";
import { memo, useCallback, useEffect, useRef, useState } from "react";
import { Millisecond } from "@pagopa/ts-commons/lib/units";
import { AccessibilityInfo, View } from "react-native";
import I18n from "../../../i18n";
Expand Down Expand Up @@ -43,6 +43,7 @@ export const IngressScreen = () => {

const [showBlockingScreen, setShowBlockingScreen] = useState(false);
const [contentTitle, setContentTitle] = useState(I18n.t("startup.title"));
const [showBanner, setShowBanner] = useState(false);

useEffect(() => {
// Since the screen is shown for a very short time,
Expand All @@ -66,6 +67,7 @@ export const IngressScreen = () => {
timeouts.push(
setTimeout(() => {
setContentTitle(I18n.t("startup.title2"));
setShowBanner(true);
timeouts.shift();
}, TIMEOUT_CHANGE_LABEL)
);
Expand All @@ -83,14 +85,9 @@ export const IngressScreen = () => {
};
}, [dispatch]);

useEffect(() => {
const visualizeOfflineWallet =
isConnected === false && isOfflineAccessAvailable;

if (visualizeOfflineWallet) {
// This dispatch could be placed inside `onSuccess`,
// but executing it here ensures the startup saga stops immediately.
dispatch(setOfflineAccessReason(OfflineAccessReasonEnum.DEVICE_OFFLINE));
const navgateOnOfflineMiniApp = useCallback(
Copy link
Member

Choose a reason for hiding this comment

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

Now that we've adopted React 19, should we avoid using useCallback by default and only introduce it when we identify a performance issue? What do you think?

(offlineReason: OfflineAccessReasonEnum) => {
dispatch(setOfflineAccessReason(offlineReason));
dispatch(
identificationRequest(false, false, undefined, undefined, {
onSuccess: () => {
Expand All @@ -101,8 +98,23 @@ export const IngressScreen = () => {
}
})
);
},
[dispatch]
);

useEffect(() => {
const visualizeOfflineWallet =
isConnected === false && isOfflineAccessAvailable;

if (visualizeOfflineWallet) {
navgateOnOfflineMiniApp(OfflineAccessReasonEnum.DEVICE_OFFLINE);
}
}, [dispatch, isConnected, isOfflineAccessAvailable]);
}, [
dispatch,
isConnected,
isOfflineAccessAvailable,
navgateOnOfflineMiniApp
]);

if (isConnected === false && !isOfflineAccessAvailable) {
return <IngressScreenNoInternetConnection />;
Expand All @@ -123,6 +135,11 @@ export const IngressScreen = () => {
testID="ingress-screen-loader-id"
contentTitle={contentTitle}
animatedPictogramSource="waiting"
banner={{
showBanner: isOfflineAccessAvailable && showBanner,
onPress: () =>
navgateOnOfflineMiniApp(OfflineAccessReasonEnum.TIMEOUT)
}}
/>
</>
);
Expand Down
3 changes: 2 additions & 1 deletion ts/features/ingress/store/reducer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { Action } from "../../../../store/actions/types";
export enum OfflineAccessReasonEnum {
DEVICE_OFFLINE = "device_offline", // The device is offline when the app is started
SESSION_REFRESH = "session_refresh", // Error on session refresh
SESSION_EXPIRED = "session_expired" // Session has expired or user has logged out
SESSION_EXPIRED = "session_expired", // Session has expired or user has logged out
TIMEOUT = "timeout" // The app has not been able to connect to the backend within a certain time
}
export type IngressScreenState = {
isBlockingScreen: boolean;
Expand Down
Loading
Loading