diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/BannerMessage.tsx b/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/BannerMessage.tsx new file mode 100644 index 00000000000..ea1355cf350 --- /dev/null +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/BannerMessage.tsx @@ -0,0 +1,100 @@ +/* + * Copyright 2017-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with + * the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import React from 'react'; +import { Image, Text, View } from 'react-native'; +import isEmpty from 'lodash/isEmpty'; + +import icons from '../../../icons'; +import { useInAppMessageButtonStyle, useInAppMessageImage } from '../../hooks'; +import { Button, IconButton } from '../../ui'; + +import { ICON_BUTTON_HIT_SLOP, ICON_BUTTON_SIZE } from '../constants'; +import MessageWrapper from '../MessageWrapper'; +import { styles } from './styles'; +import { BannerMessageProps } from './types'; + +export default function BannerMessage({ + body, + container, + header, + image, + layout, + onClose, + position, + primaryButton, + secondaryButton, + style, +}: BannerMessageProps) { + const { imageStyle, shouldDelayMessageRendering, shouldRenderImage } = useInAppMessageImage(image, layout); + + const messageButtonStyle = { primaryButton: primaryButton?.style, secondaryButton: secondaryButton?.style }; + const { primaryButtonStyle, secondaryButtonStyle } = useInAppMessageButtonStyle({ + baseButtonStyle: styles, + messageButtonStyle, + overrideButtonStyle: style, + }); + + const hasPrimaryButton = !isEmpty(primaryButton); + const hasSecondaryButton = !isEmpty(secondaryButton); + + return shouldDelayMessageRendering ? null : ( + + + + + {shouldRenderImage && ( + + + + )} + + {header?.content && {header.content}} + {body?.content && {body.content}} + + + + {(hasPrimaryButton || hasSecondaryButton) && ( + + {hasSecondaryButton && ( + + )} + {hasPrimaryButton && ( + + )} + + )} + + + + ); +} diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/index.ts b/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/index.ts index 31d036fc8c8..6b0dfc6aeee 100644 --- a/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/index.ts +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/index.ts @@ -1 +1,2 @@ +export { default } from './BannerMessage'; export { BannerMessageProps } from './types'; diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/styles.ts b/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/styles.ts new file mode 100644 index 00000000000..696846a17f2 --- /dev/null +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/styles.ts @@ -0,0 +1,110 @@ +/* + * Copyright 2017-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with + * the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { StyleSheet } from 'react-native'; + +import { getLineHeight } from '../utils'; +import { + BANNER_ELEVATION, + BANNER_SHADOW_HEIGHT, + BANNER_SHADOW_OPACITY, + BANNER_SHADOW_RADIUS, + BANNER_SHADOW_WIDTH, + BLACK, + BORDER_RADIUS_BASE, + FONT_SIZE_BASE, + FONT_SIZE_LARGE, + FONT_WEIGHT_BASE, + LIGHT_GREY, + SPACING_EXTRA_LARGE, + SPACING_LARGE, + SPACING_MEDIUM, + SPACING_SMALL, + WHITE, +} from '../constants'; +import { BannerMessageStyle } from './types'; + +export const styles: BannerMessageStyle = StyleSheet.create({ + // position style + positionContainer: { + flex: 1, + backgroundColor: 'transparent', + }, + bottom: { + justifyContent: 'flex-end', + }, + middle: { + justifyContent: 'center', + }, + top: { + justifyContent: 'flex-start', + }, + + // shared style + buttonContainer: { + backgroundColor: LIGHT_GREY, + borderRadius: BORDER_RADIUS_BASE, + flex: 1, + margin: SPACING_MEDIUM, + padding: SPACING_LARGE, + }, + buttonsContainer: { + flexDirection: 'row', + justifyContent: 'center', + paddingHorizontal: SPACING_SMALL, + }, + buttonText: { + fontSize: FONT_SIZE_BASE, + fontWeight: FONT_WEIGHT_BASE, + lineHeight: getLineHeight(FONT_SIZE_BASE), + textAlign: 'center', + }, + container: { + backgroundColor: WHITE, + elevation: BANNER_ELEVATION, + margin: SPACING_EXTRA_LARGE, + shadowColor: BLACK, + shadowOffset: { + width: BANNER_SHADOW_WIDTH, + height: BANNER_SHADOW_HEIGHT, + }, + shadowOpacity: BANNER_SHADOW_OPACITY, + shadowRadius: BANNER_SHADOW_RADIUS, + }, + contentContainer: { + flexDirection: 'row', + padding: SPACING_LARGE, + }, + header: { + fontSize: FONT_SIZE_LARGE, + fontWeight: FONT_WEIGHT_BASE, + lineHeight: getLineHeight(FONT_SIZE_LARGE), + }, + iconButton: { + alignSelf: 'flex-start', + marginLeft: 'auto', + }, + imageContainer: { + justifyContent: 'center', + }, + message: { + fontSize: FONT_SIZE_BASE, + fontWeight: FONT_WEIGHT_BASE, + lineHeight: getLineHeight(FONT_SIZE_BASE), + }, + textContainer: { + marginHorizontal: SPACING_SMALL, + paddingLeft: SPACING_MEDIUM, + flex: 1, + }, +}); diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/types.ts b/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/types.ts index 5d34c9167ca..b5499557ab0 100644 --- a/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/types.ts +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/BannerMessage/types.ts @@ -11,8 +11,29 @@ * and limitations under the License. */ +import { TextStyle, ViewStyle } from 'react-native'; import { InAppMessageComponentPosition, InAppMessageComponentBaseProps } from '../types'; export interface BannerMessageProps extends InAppMessageComponentBaseProps { position: InAppMessageComponentPosition; } + +export interface BannerMessageStyle { + // position specific style + bottom: ViewStyle; + middle: ViewStyle; + top: ViewStyle; + + // component style + buttonContainer: ViewStyle; + buttonsContainer: ViewStyle; + buttonText: TextStyle; + container: ViewStyle; + contentContainer: ViewStyle; + header: TextStyle; + iconButton: ViewStyle; + imageContainer: ViewStyle; + message: TextStyle; + positionContainer: ViewStyle; + textContainer: ViewStyle; +} diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/MessageWrapper.tsx b/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/MessageWrapper.tsx new file mode 100644 index 00000000000..0ffa3ddc45a --- /dev/null +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/MessageWrapper.tsx @@ -0,0 +1,22 @@ +/* + * Copyright 2017-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with + * the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import React from 'react'; +import { SafeAreaView } from 'react-native'; + +import { styles } from './styles'; +import { MessageWrapperProps } from './types'; + +export default function MessageWrapper({ children, style }: MessageWrapperProps) { + return {children}; +} diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/index.ts b/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/index.ts new file mode 100644 index 00000000000..f70050fdcee --- /dev/null +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/index.ts @@ -0,0 +1,2 @@ +export { default } from './MessageWrapper'; +export { MessageWrapperProps, MessageWrapperStyle } from './types'; diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/styles.ts b/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/styles.ts new file mode 100644 index 00000000000..6d2a95714b7 --- /dev/null +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/styles.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2017-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with + * the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { StyleSheet } from 'react-native'; +import { MessageWrapperStyle } from './types'; + +export const styles: MessageWrapperStyle = StyleSheet.create({ + safeArea: { + ...StyleSheet.absoluteFillObject, + }, +}); diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/types.ts b/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/types.ts new file mode 100644 index 00000000000..a1f7b41ff85 --- /dev/null +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/MessageWrapper/types.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2017-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with + * the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { ReactNode } from 'react'; +import { StyleProp, ViewStyle } from 'react-native'; + +export interface MessageWrapperProps { + children: ReactNode; + style?: StyleProp; +} + +export interface MessageWrapperStyle { + safeArea: ViewStyle; +} diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/constants.ts b/packages/aws-amplify-react-native/src/InAppMessaging/components/constants.ts new file mode 100644 index 00000000000..087966fc224 --- /dev/null +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/constants.ts @@ -0,0 +1,55 @@ +/* + * Copyright 2017-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with + * the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +/** + * Style constants either match or approximate the values used in the Pinpoint console preview. + * Some values, such as spacing, are slightly different to allow for a more mobile friendly UX + */ + +// color +export const BLACK = '#000'; +export const LIGHT_GREY = '#e8e8e8'; +export const WHITE = '#fff'; + +// spacing +export const SPACING_SMALL = 4; +export const SPACING_MEDIUM = 8; +export const SPACING_LARGE = 12; +export const SPACING_EXTRA_LARGE = 16; + +// border radius +export const BORDER_RADIUS_BASE = 4; + +// font +export const FONT_SIZE_BASE = 16; +export const FONT_SIZE_LARGE = 18; + +export const FONT_WEIGHT_BASE = '400'; + +export const LINE_HEIGHT_MULTIPLIER = 1.5; + +// icon +export const ICON_BUTTON_SIZE = 20; +export const ICON_BUTTON_HIT_SLOP = 10; + +// component specific constants + +// BannerMessage +// iOS shadow values +export const BANNER_SHADOW_HEIGHT = 2; +export const BANNER_SHADOW_WIDTH = 2; +export const BANNER_SHADOW_OPACITY = 0.1; +export const BANNER_SHADOW_RADIUS = 2; + +// android shadow values +export const BANNER_ELEVATION = 3; diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/index.ts b/packages/aws-amplify-react-native/src/InAppMessaging/components/index.ts index 57210cd4687..25bb2d0a74b 100644 --- a/packages/aws-amplify-react-native/src/InAppMessaging/components/index.ts +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/index.ts @@ -1,4 +1,4 @@ -export { BannerMessageProps } from './BannerMessage'; +export { default as BannerMessage, BannerMessageProps } from './BannerMessage'; export { CarouselMessageProps } from './CarouselMessage'; export { FullScreenMessageProps } from './FullScreenMessage'; export { default as InAppMessageDisplay } from './InAppMessageDisplay'; diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/types.ts b/packages/aws-amplify-react-native/src/InAppMessaging/components/types.ts index 991309bd912..070c0082ac6 100644 --- a/packages/aws-amplify-react-native/src/InAppMessaging/components/types.ts +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/types.ts @@ -12,7 +12,8 @@ */ import { ColorValue, StyleProp, TextStyle, ViewStyle } from 'react-native'; -import { InAppMessageButton, InAppMessageContent } from '@aws-amplify/notifications'; + +import { InAppMessageButton, InAppMessageContent, InAppMessageLayout } from '@aws-amplify/notifications'; export type InAppMessageComponentButtonStyle = { container?: StyleProp; @@ -49,6 +50,7 @@ export interface InAppMessageComponentContentProps } export interface InAppMessageComponentBaseProps extends InAppMessageComponentContentProps { + layout: InAppMessageLayout; id: string; onClose?: () => void; style?: InAppMessageComponentStyle; diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/components/utils.ts b/packages/aws-amplify-react-native/src/InAppMessaging/components/utils.ts new file mode 100644 index 00000000000..9eb0c1edbdf --- /dev/null +++ b/packages/aws-amplify-react-native/src/InAppMessaging/components/utils.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2017-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with + * the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { LINE_HEIGHT_MULTIPLIER } from './constants'; + +export const getLineHeight = (fontSize: number) => fontSize * LINE_HEIGHT_MULTIPLIER; diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessage/useInAppMessage.ts b/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessage/useInAppMessage.ts index a33cc86b009..fa364f389a9 100644 --- a/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessage/useInAppMessage.ts +++ b/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessage/useInAppMessage.ts @@ -13,13 +13,8 @@ import { ConsoleLogger as Logger } from '@aws-amplify/core'; -import { - BannerMessageProps, - CarouselMessageProps, - FullScreenMessageProps, - InAppMessageComponents, - useInAppMessaging, -} from '../..'; +import { BannerMessageProps, CarouselMessageProps, FullScreenMessageProps } from '../../components'; +import { InAppMessageComponents, useInAppMessaging } from '../../context'; import { InAppMessageComponent, InAppMessageComponentProps } from './types'; import { getInAppMessage, getContentProps, getPositionProp } from './utils'; @@ -61,6 +56,7 @@ export default function useInAppMessage(): { const props: BannerMessageProps = { ...getContentProps(content?.[0], onActionCallback), id, + layout, onClose, position: getPositionProp(layout), style: style?.BannerMessage, @@ -71,6 +67,7 @@ export default function useInAppMessage(): { const props: CarouselMessageProps = { data: content?.map((item) => getContentProps(item, onActionCallback)), id, + layout, onClose, style: style?.CarouselMessage, }; @@ -80,6 +77,7 @@ export default function useInAppMessage(): { const props: FullScreenMessageProps = { ...getContentProps(content?.[0], onActionCallback), id, + layout, onClose, style: style?.FullScreenMessage, }; diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessageButtonStyle/useInAppMessageButtonStyle.ts b/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessageButtonStyle/useInAppMessageButtonStyle.ts index 646899e960a..ef92b3ca65b 100644 --- a/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessageButtonStyle/useInAppMessageButtonStyle.ts +++ b/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessageButtonStyle/useInAppMessageButtonStyle.ts @@ -27,14 +27,14 @@ const getButtonComponentStyle = ( const { buttonContainer, buttonText } = baseStyle; // message specific styles in the in-app message payload, overrides default component styles - const { backgroundColor, color, textAlign } = messageStyle ?? {}; + const { backgroundColor, borderRadius, color } = messageStyle ?? {}; // custom component override styles passed as style prop, overrides all previous styles const { container, text } = overrideStyle ?? {}; return { - container: [buttonContainer, { backgroundColor }, container], - text: [buttonText, { color, textAlign }, text], + container: [buttonContainer, { backgroundColor, borderRadius }, container], + text: [buttonText, { color }, text], }; }; diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessageImage/useInAppMessageImage.ts b/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessageImage/useInAppMessageImage.ts index 53c8b1b4cd1..80862cd56e0 100644 --- a/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessageImage/useInAppMessageImage.ts +++ b/packages/aws-amplify-react-native/src/InAppMessaging/hooks/useInAppMessageImage/useInAppMessageImage.ts @@ -30,9 +30,10 @@ const inAppMessageImageSizes: Record = { }; export default function useInAppMessageImage( - { src }: InAppMessageImage, + image: InAppMessageImage, layout: InAppMessageLayout ): UseInAppMessageImage { + const { src } = image ?? {}; const hasImage = !!src; const [imageLoadingState, setImageLoadingState] = useState(hasImage ? 'loading' : null); diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/index.ts b/packages/aws-amplify-react-native/src/InAppMessaging/index.ts index 16e4fec9012..24a99750664 100644 --- a/packages/aws-amplify-react-native/src/InAppMessaging/index.ts +++ b/packages/aws-amplify-react-native/src/InAppMessaging/index.ts @@ -7,6 +7,7 @@ export { useInAppMessaging, } from './context'; export { + BannerMessage, BannerMessageProps, CarouselMessageProps, FullScreenMessageProps, diff --git a/packages/aws-amplify-react-native/src/InAppMessaging/ui/index.ts b/packages/aws-amplify-react-native/src/InAppMessaging/ui/index.ts index 3f3851cb299..ca5a64fea87 100644 --- a/packages/aws-amplify-react-native/src/InAppMessaging/ui/index.ts +++ b/packages/aws-amplify-react-native/src/InAppMessaging/ui/index.ts @@ -1,2 +1,3 @@ +export { default as Button, ButtonProps } from './Button'; export { default as IconButton, IconButtonProps, IconButtonStyles } from './IconButton'; export { default as Paragraph, ParagraphProps } from './Paragraph'; diff --git a/packages/aws-amplify-react-native/src/icons/close.png b/packages/aws-amplify-react-native/src/icons/close.png new file mode 100644 index 00000000000..abf708b234b Binary files /dev/null and b/packages/aws-amplify-react-native/src/icons/close.png differ diff --git a/packages/aws-amplify-react-native/src/icons/index.ts b/packages/aws-amplify-react-native/src/icons/index.ts index be9a88e0320..4d6f32ca554 100644 --- a/packages/aws-amplify-react-native/src/icons/index.ts +++ b/packages/aws-amplify-react-native/src/icons/index.ts @@ -1,5 +1,8 @@ +import { ImageSourcePropType } from 'react-native'; + const icons = { - warning: require('./warning.png'), + close: require('./close.png') as ImageSourcePropType, + warning: require('./warning.png') as ImageSourcePropType, }; export default icons;