Skip to content

Commit

Permalink
Begin work on Reward Tile
Browse files Browse the repository at this point in the history
  • Loading branch information
iamgraeme committed Sep 24, 2024
1 parent d75495c commit 822bc24
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 70 deletions.
Binary file added lib/assets/reward.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 3 additions & 7 deletions lib/components/atoms/BaseTile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { ImagePropsNoSource } from '../../../types/common';
import { Tile, TileConfig, TileHeight } from '../../../types/tile';
import { createResponsiveStyle } from '../../../utils/responsiveHelper';
import { Icon, Text } from '../../atoms';
import { useSectionContext } from '../../organisms/Section';
import LoadingIndicator from '../LoadingIndicator';

const TileContext = createContext<Tile | null>(null);

Expand Down Expand Up @@ -35,15 +33,12 @@ type LayoutProps = FlexStyle & {
};

const BaseTileInner: FC<BaseTileProps> = ({ tile, children, style }) => {
const { loading: isLoading } = useSectionContext();
const { theme } = useWllSdk();

const layout: LayoutProps = {
flexDirection: 'column',
justifyContent:
tile.tileHeight === TileHeight.Half || !tile.configuration.imageUrl
? 'center'
: 'flex-start',
tile.tileHeight === TileHeight.Half ? 'center' : 'flex-start',
alignItems: 'stretch',
};
const responsiveStyles = createResponsiveStyle({
Expand All @@ -53,6 +48,7 @@ const BaseTileInner: FC<BaseTileProps> = ({ tile, children, style }) => {
theme.sizes.borderRadiusLg,
],
maxWidth: [175, 175, 258],
backgroundColor: tile.tileHeight === 'FULL' ? 'red' : 'blue',
});

return (
Expand All @@ -69,7 +65,7 @@ const BaseTileInner: FC<BaseTileProps> = ({ tile, children, style }) => {
style,
]}
>
{isLoading ? <LoadingIndicator /> : children}
{children}
</View>
</TileContext.Provider>
);
Expand Down
9 changes: 4 additions & 5 deletions lib/components/organisms/BadgeTile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import React, { FC } from 'react';
import { Image, StyleSheet, View } from 'react-native';
import { useWllSdk } from '../../../context/WllSdkContext';
import { ImagePropsNoSource } from '../../../types/common';
import { Tile, TileConfig } from '../../../types/tile';
import { Badge } from '../../../types/wll';
import { BadgeTileConfig, Tile } from '../../../types/tile';
import { createResponsiveStyle } from '../../../utils/responsiveHelper';
import { BaseTile, Icon, Text } from '../../atoms';
import { useTileContext } from '../../atoms/BaseTile';
Expand Down Expand Up @@ -36,14 +35,14 @@ const BadgeTileInner: FC<BadgeTileProps> = ({ tile }) => {

const BadgeTileImage: FC<ImagePropsNoSource> = (props) => {
const tile = useTileContext();
const { badge } = tile.configuration as TileConfig & { badge?: Badge };
const { badge } = tile.configuration as BadgeTileConfig;
if (!badge?.artworkUrl) return null;
return <Image {...props} source={{ uri: badge.artworkUrl }} />;
};

const BadgeTileTitle: FC = (props) => {
const tile = useTileContext();
const { badge } = tile.configuration as TileConfig & { badge?: Badge };
const { badge } = tile.configuration as BadgeTileConfig;
if (!badge?.name) return null;
return (
<Text variant="title" {...props}>
Expand All @@ -54,7 +53,7 @@ const BadgeTileTitle: FC = (props) => {

const BadgeTileBody: FC = (props) => {
const tile = useTileContext();
const { badge } = tile.configuration as TileConfig & { badge?: Badge };
const { badge } = tile.configuration as BadgeTileConfig;
if (!badge?.description) return null;
return (
<Text variant="body" {...props}>
Expand Down
7 changes: 4 additions & 3 deletions lib/components/organisms/PointsTile/PointsTile.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import React from 'react';
import { TileHeight, TileType } from '../../../types/tile';
import { TileWrapper } from '../../../utils/storybookHelpers';
import PointsTile from './index';

const pointsImage = '../../../assets/points.png';
const pointsFull = '../../../assets/pointsFull.png';
// @ts-ignore
import pointsImage from '../../../assets/points.png';
// @ts-ignore
import pointsFull from '../../../assets/pointsFull.png';

export default {
title: 'components/organisms/PointsTile',
Expand Down
109 changes: 104 additions & 5 deletions lib/components/organisms/RewardTile/RewardTile.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,114 @@
import { Meta, StoryFn } from "@storybook/react";
import React from "react";
import RewardTile from "./index";
import { Meta, StoryFn } from '@storybook/react';
import React from 'react';
import { TileHeight, TileType } from '../../../types/tile';
import RewardTile from './index';
// @ts-ignore
import { undefined } from 'zod';
import rewardImage from '../../../assets/reward.png';

export default {
title: "components/organisms/RewardTile",
title: 'components/organisms/RewardTile',
component: RewardTile,
} as Meta;

const Template: StoryFn<typeof RewardTile> = (args) => <RewardTile {...args} />;

export const Default = Template.bind({});
Default.args = {
// Add default props here
tile: {
tileHeight: TileHeight.Full,
active: true,
type: TileType.Reward,
configuration: {
rewardId: '02fdea07-e3eb-4a2d-a66a-cb0368427bd2',
showPrice: false,
reward: {
id: '02fdea07-e3eb-4a2d-a66a-cb0368427bd2',
createdAt: '2024-08-20T08:46:43.851Z',
updatedAt: '2024-08-20T08:46:43.851Z',
name: 'Spotify Premium',
description: 'Get 1 year subscription',
pictureUrl: rewardImage,
value: 0,
price: 0,
priority: 0,
availability: {
start: '2024-08-20T08:46:43.848Z',
end: '2024-09-14T03:04:54.056Z',
},
purchasable: true,
terms: '',
tier: undefined,
venues: [],
category: null,
discounts: [],
summary: undefined,
redemptionChannels: ['IN_STORE', 'ONLINE'],
purchasableForAudiences: [],
logoUrl: null,
redemptionMessage: undefined,
visibilityCriteria: undefined,
type: 'VOUCHER',
codeType: 'HUMAN',
code: null,
purchaseExpiration: null,
hideCode: false,
notificationConfig: null,
},
},
id: '511b2a95-5094-4a01-b958-05643f813822',
createdAt: '2024-08-20T13:05:16.974Z',
updatedAt: '2024-08-20T13:05:16.974Z',
visibilityCriteria: null,
},
};

export const DefaultWithPoints = Template.bind({});
DefaultWithPoints.args = {
tile: {
tileHeight: TileHeight.Full,
active: true,
type: TileType.Reward,
configuration: {
rewardId: '02fdea07-e3eb-4a2d-a66a-cb0368427bd2',
showPrice: false,
reward: {
id: '02fdea07-e3eb-4a2d-a66a-cb0368427bd2',
createdAt: '2024-08-20T08:46:43.851Z',
updatedAt: '2024-08-20T08:46:43.851Z',
name: 'Spotify Premium',
description: 'Get 1 year subscription',
pictureUrl: rewardImage,
value: 0,
price: 61,
priority: 0,
availability: {
start: '2024-08-20T08:46:43.848Z',
end: '2024-09-14T03:04:54.056Z',
},
purchasable: true,
terms: '',
tier: undefined,
venues: [],
category: null,
discounts: [],
summary: undefined,
redemptionChannels: ['IN_STORE', 'ONLINE'],
purchasableForAudiences: [],
logoUrl: null,
redemptionMessage: undefined,
visibilityCriteria: undefined,
type: 'VOUCHER',
codeType: 'HUMAN',
code: null,
purchaseExpiration: null,
hideCode: false,
notificationConfig: null,
},
},
id: '511b2a95-5094-4a01-b958-05643f813822',
createdAt: '2024-08-20T13:05:16.974Z',
updatedAt: '2024-08-20T13:05:16.974Z',
visibilityCriteria: null,
},
};
159 changes: 121 additions & 38 deletions lib/components/organisms/RewardTile/index.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,134 @@
import React, { createContext, useContext } from "react";
import { RewardTileConfig } from "../../../types/tile";
import {
Button as ButtonAtom,
Image as ImageAtom,
Text,
Tile,
} from "../../atoms";

type RewardTileContextType = {
configuration: RewardTileConfig;
};

const RewardTileContext = createContext<RewardTileContextType | undefined>(
undefined
);
import React from 'react';
import { Image, StyleSheet, View } from 'react-native';
import { useWllSdk } from '../../../context/WllSdkContext';
import { RewardTileConfig, Tile } from '../../../types/tile';
import { createResponsiveStyle } from '../../../utils/responsiveHelper';
import { BaseTile, Icon, Text } from '../../atoms';
import { useTileContext } from '../../atoms/BaseTile';
import { useSectionContext } from '../Section';

type RewardTileProps = {
configuration: RewardTileConfig;
children: React.ReactNode;
tile: Tile;
};

const RewardTile: React.FC<RewardTileProps> & {
Image: typeof ImageAtom;
Title: typeof Text;
Description: typeof Text;
Button: typeof ButtonAtom;
} = ({ configuration, children }) => {
Image: typeof RewardTileImage;
Title: typeof RewardTileTitle;
Description: typeof RewardTileDescription;
Points: typeof RewardTilePoints;
} = ({ tile }) => {
const { theme } = useWllSdk();
return (
<BaseTile tile={tile}>
<RewardTile.Image />
<View style={styles.textContainer}>
<View style={styles.row}>
<RewardTile.Title />
<Icon name="ChevronRight" color={theme.derivedSurfaceText[20]} />
</View>
<RewardTile.Description />
<RewardTile.Points />
</View>
</BaseTile>
);
};

const RewardTileImage: React.FC = () => {
const { configuration } = useTileContext();
const { reward } = configuration as RewardTileConfig;

if (!reward?.pictureUrl) return null;
return (
<RewardTileContext.Provider value={{ configuration }}>
<Tile>{children}</Tile>
</RewardTileContext.Provider>
<View style={styles.imageContainer}>
<Image source={{ uri: reward?.pictureUrl }} style={styles.image} />
</View>
);
};

const useRewardTileContext = () => {
const context = useContext(RewardTileContext);
if (!context) {
throw new Error(
"RewardTile compound components must be used within RewardTile"
);
}
return context;
const RewardTileTitle: React.FC = () => {
const { configuration } = useTileContext();
const { reward } = configuration as RewardTileConfig;

if (!reward?.name) return null;
return (
<Text variant="title" ellipsizeMode="tail" numberOfLines={1}>
{reward?.name}
</Text>
);
};

RewardTile.Image = ImageAtom;
RewardTile.Title = Text;
RewardTile.Description = Text;
RewardTile.Button = ButtonAtom;
const RewardTileDescription: React.FC = () => {
const { configuration } = useTileContext();
const { reward } = configuration as RewardTileConfig;

if (!reward?.description) return null;
return <Text variant="body">{reward?.description}</Text>;
};

const RewardTilePoints: React.FC = () => {
const { sectionData } = useSectionContext();
const { configuration } = useTileContext();
const { theme } = useWllSdk();
const { reward } = configuration as RewardTileConfig;

const effectiveMultiplier = sectionData?.pointsMultiplier ?? 1;
const effectivePrefix = sectionData?.pointsPrefix ?? '';
const effectiveSuffix = sectionData?.pointsSuffix ?? 'pts';
const calculatedPoints =
reward?.price !== undefined ? reward.price * effectiveMultiplier : null;
if (reward?.price === 0) return null;
return (
<Text
style={[
styles.points,
{
color: theme.primary,
},
]}
>
{effectivePrefix}
{calculatedPoints}
{effectiveSuffix}
</Text>
);
};

const styles = StyleSheet.create({
imageContainer: createResponsiveStyle({
width: '100%',
flexBasis: '50%',
marginBottom: [8, 8, 12],
}),
image: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
width: '100%',
height: '100%',
resizeMode: 'cover',
},
textContainer: createResponsiveStyle({
paddingHorizontal: [8, 8, 12],
flex: 1,
}),
row: createResponsiveStyle({
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: [4, 4, 8],
}),
points: {
marginTop: 8,
fontSize: 14,
fontWeight: 'bold',
},
});

RewardTile.Image = RewardTileImage;
RewardTile.Title = RewardTileTitle;
RewardTile.Description = RewardTileDescription;
RewardTile.Points = RewardTilePoints;

export default RewardTile;
Loading

0 comments on commit 822bc24

Please sign in to comment.