Skip to content

Commit

Permalink
DS UI for message attachment's preview
Browse files Browse the repository at this point in the history
  • Loading branch information
Vangaorth committed Jan 23, 2024
1 parent 9634f37 commit 42c230a
Show file tree
Hide file tree
Showing 12 changed files with 506 additions and 16 deletions.
2 changes: 1 addition & 1 deletion ts/features/messages/analytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export function trackThirdPartyMessageAttachmentBadFormat(

export function trackThirdPartyMessageAttachmentCorruptedFile(
messageId: UIMessageId,
serviceId: ServiceId | undefined
serviceId?: ServiceId
) {
void mixpanelTrack(
"THIRD_PARTY_MESSAGE_ATTACHMENT_CORRUPTED_FILE",
Expand Down
49 changes: 49 additions & 0 deletions ts/features/messages/components/MessageAttachment/DSPdfViewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { useState } from "react";
import { StyleSheet } from "react-native";
import Pdf from "react-native-pdf";
import { IOColors } from "@pagopa/io-app-design-system";
import { DSLoadingSpinnerOverlay } from "../../designsystem/DSLoadingSpinnerOverlay";
import I18n from "../../../../i18n";

const styles = StyleSheet.create({
pdf: {
flex: 1,
backgroundColor: IOColors.bluegrey
}
});

type OwnProps = {
downloadPath: string;
};

type Props = OwnProps & Omit<React.ComponentProps<typeof Pdf>, "source">;

export const DSPdfViewer = ({
style,
downloadPath,
onError,
onLoadComplete,
...rest
}: Props) => {
const [isLoading, setIsLoading] = useState(true);
return (
<DSLoadingSpinnerOverlay
isLoading={isLoading}
loadingCaption={I18n.t("messageDetails.attachments.loading")}
>
<Pdf
{...rest}
source={{ uri: downloadPath, cache: false }}
style={[styles.pdf, style]}
onLoadComplete={(...args) => {
setIsLoading(false);
onLoadComplete?.(...args);
}}
onError={(...args) => {
setIsLoading(false);
onError?.(...args);
}}
/>
</DSLoadingSpinnerOverlay>
);
};
7 changes: 5 additions & 2 deletions ts/features/messages/components/MessageDetail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,17 +155,20 @@ const MessageDetailsComponent = ({
const messageTitle =
useIOSelector(state => messageTitleSelector(state, messageId)) ?? title;

const serviceIdOpt = service?.id;
const openAttachment = useCallback(
(attachment: UIAttachment) => {
navigation.navigate(MESSAGES_ROUTES.MESSAGES_NAVIGATOR, {
screen: MESSAGES_ROUTES.MESSAGE_DETAIL_ATTACHMENT,
params: {
messageId,
attachmentId: attachment.id
serviceId: serviceIdOpt,
attachmentId: attachment.id,
isPN: false
}
});
},
[messageId, navigation]
[messageId, navigation, serviceIdOpt]
);

const renderThirdPartyAttachments = useCallback(
Expand Down
65 changes: 65 additions & 0 deletions ts/features/messages/designsystem/DSInfoScreenComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as React from "react";
import { StyleSheet, Text, View } from "react-native";
import { Body, H2, VSpacer } from "@pagopa/io-app-design-system";
import { useFocusEffect } from "@react-navigation/native";
import { setAccessibilityFocus } from "../../../utils/accessibility";

type Props = {
image: React.ReactNode;
title: string;
// this is necessary in order to render text with different formatting
body?: string | React.ReactNode;
};

const styles = StyleSheet.create({
main: {
padding: 24,
flex: 1,
alignItems: "center",
justifyContent: "center"
},
textAlignCenter: {
textAlign: "center"
}
});

const renderNode = (body: string | React.ReactNode) => {
if (typeof body === "string") {
return (
<Body testID="infoScreenBody" style={styles.textAlignCenter}>
{body}
</Body>
);
}

return body;
};

/**
* A base screen that displays one image, text, and one bottom button
* @param props
* @constructor
*/
export const DSInfoScreenComponent: React.FunctionComponent<Props> = props => {
const elementRef = React.createRef<Text>();
useFocusEffect(
React.useCallback(() => setAccessibilityFocus(elementRef), [elementRef])
);

return (
<View style={styles.main} testID="InfoScreenComponent">
{props.image}
<VSpacer size={24} />
<H2
testID="infoScreenTitle"
accessible
ref={elementRef}
style={styles.textAlignCenter}
>
{props.title}
</H2>
<VSpacer size={16} />
{renderNode(props.body)}
</View>
);
};
100 changes: 100 additions & 0 deletions ts/features/messages/designsystem/DSLoadingSpinnerOverlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as React from "react";
import { StyleSheet, View } from "react-native";
import {
Body,
ButtonOutline,
IOColors,
IOStyles,
LoadingSpinner,
hexToRgba
} from "@pagopa/io-app-design-system";
import I18n from "../../../i18n";

const styles = StyleSheet.create({
back: {
zIndex: 0
},
container: {
flex: 1
},
overlay: {
position: "absolute",
inset: 0,
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: IOColors.white,
zIndex: 1,
justifyContent: "center",
opacity: 1
},
refreshBox: {
height: 100,
flex: 1,
justifyContent: "center",
alignItems: "center"
},
textCaption: {
padding: 24
},
whiteBg: {
backgroundColor: IOColors.white
}
});

type Props = Readonly<{
backgroundColor?: string;
isLoading: boolean;
loadingCaption?: string;
opacity?: number;
onCancel?: () => void;
children: React.ReactNode;
}>;

/**
* A Component to display and overlay spinner conditionally
*/
export const DSLoadingSpinnerOverlay = ({
backgroundColor,
isLoading,
loadingCaption,
opacity = 1,
onCancel,
children
}: Props) => (
<View style={styles.container} testID={"overlayComponent"}>
{isLoading && (
<View
style={[
styles.overlay,
{
opacity,
backgroundColor:
backgroundColor ?? hexToRgba(IOColors.white, opacity)
}
]}
>
<View style={[styles.refreshBox, styles.whiteBg]}>
<LoadingSpinner testID="refreshIndicator" />
<View style={styles.textCaption}>
<Body accessible={true} style={{ textAlign: "center" }}>
{loadingCaption || I18n.t("global.remoteStates.wait")}
</Body>
</View>
{onCancel && (
<View style={IOStyles.selfCenter}>
<ButtonOutline
accessibilityLabel={I18n.t("global.buttons.cancel")}
onPress={onCancel}
testID="loadingSpinnerOverlayCancelButton"
label={I18n.t("global.buttons.cancel")}
/>
</View>
)}
</View>
</View>
)}
<View style={[styles.container, styles.back]}>{children}</View>
</View>
);
29 changes: 26 additions & 3 deletions ts/features/messages/navigation/MessagesNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,65 @@ import { useIOSelector } from "../../../store/hooks";
import { isGestureEnabled } from "../../../utils/navigation";
import { isPnEnabledSelector } from "../../../store/reducers/backendStatus";
import { MessageDetailAttachment } from "../screens/MessageAttachment";
import { isDesignSystemEnabledSelector } from "../../../store/reducers/persistedPreferences";
import { DSMessageAttachment } from "../screens/DSMessageAttachment";
import { MessagesParamsList } from "./params";
import { MESSAGES_ROUTES } from "./routes";

const Stack = createStackNavigator<MessagesParamsList>();

export const MessagesStackNavigator = () => {
const isDesignSystemEnabled = useIOSelector(isDesignSystemEnabledSelector);
const isPnEnabled = useIOSelector(isPnEnabledSelector);

return (
<Stack.Navigator
initialRouteName={MESSAGES_ROUTES.MESSAGE_ROUTER}
headerMode={"none"}
headerMode={"screen"}
screenOptions={{ gestureEnabled: isGestureEnabled }}
>
<Stack.Screen
name={MESSAGES_ROUTES.MESSAGE_ROUTER}
component={MessageRouterScreen}
options={{
headerShown: false
}}
/>

<Stack.Screen
name={MESSAGES_ROUTES.MESSAGE_DETAIL}
component={MessageDetailScreen}
options={{
headerShown: false
}}
/>

<Stack.Screen
name={MESSAGES_ROUTES.MESSAGE_DETAIL_ATTACHMENT}
component={MessageDetailAttachment}
component={
isDesignSystemEnabled ? DSMessageAttachment : MessageDetailAttachment
}
options={{
headerShown: isDesignSystemEnabled
}}
/>

<Stack.Screen
name={EUCOVIDCERT_ROUTES.MAIN}
component={EUCovidCertStackNavigator}
options={{
headerShown: false
}}
/>

{isPnEnabled && (
<Stack.Screen name={PN_ROUTES.MAIN} component={PnStackNavigator} />
<Stack.Screen
name={PN_ROUTES.MAIN}
component={PnStackNavigator}
options={{
headerShown: false
}}
/>
)}
</Stack.Navigator>
);
Expand Down
4 changes: 2 additions & 2 deletions ts/features/messages/navigation/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import EUCOVIDCERT_ROUTES from "../../euCovidCert/navigation/routes";
import PN_ROUTES from "../../pn/navigation/routes";
import { MessageRouterScreenNavigationParams } from "../screens/MessageRouterScreen";
import { MessageDetailScreenNavigationParams } from "../screens/MessageDetailScreen";
import { MessageDetailAttachmentNavigationParams } from "../screens/MessageAttachment";
import { EUCovidCertParamsList } from "../../euCovidCert/navigation/params";
import { PnParamsList } from "../../pn/navigation/params";
import { DSMessageAttachmentNavigationParams } from "../screens/DSMessageAttachment";
import { MESSAGES_ROUTES } from "./routes";

export type MessagesParamsList = {
[MESSAGES_ROUTES.MESSAGE_ROUTER]: MessageRouterScreenNavigationParams;
[MESSAGES_ROUTES.MESSAGE_DETAIL]: MessageDetailScreenNavigationParams;
[MESSAGES_ROUTES.MESSAGE_DETAIL_ATTACHMENT]: MessageDetailAttachmentNavigationParams;
[MESSAGES_ROUTES.MESSAGE_DETAIL_ATTACHMENT]: DSMessageAttachmentNavigationParams;
[EUCOVIDCERT_ROUTES.MAIN]: NavigatorScreenParams<EUCovidCertParamsList>;
[PN_ROUTES.MAIN]: NavigatorScreenParams<PnParamsList>;
};
Loading

0 comments on commit 42c230a

Please sign in to comment.