Skip to content

Commit

Permalink
feat(llm): optimize the ui of stacked braze cards
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasWerey committed Mar 27, 2024
1 parent ce66ea7 commit 2b8a75f
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 67 deletions.
5 changes: 5 additions & 0 deletions .changeset/fifty-emus-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"live-mobile": patch
---

Change the UI of stacked horizontal cards. The UI will fit with the design if there is more than one card from braze and if these cards have full width
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ const HorizontalCard = ContentCardBuilder<Props>(
<Flex
bg={colors.opacityDefault.c05}
p="13px"
{...(itemStyle ?? { borderRadius: 12 })}
flexDirection="row"
justifyContent="space-between"
alignItems="center"
columnGap={13}
{...(itemStyle ?? { borderRadius: 12 })}
>
{image ? <Image uri={image} /> : null}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ type Props = {
tag?: string;
cta?: string;
filledImage?: boolean;
index?: number;
};

const VerticalCard = ContentCardBuilder<Props>(
Expand Down
69 changes: 17 additions & 52 deletions apps/ledger-live-mobile/src/contentCards/layouts/grid/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { useWindowDimensions } from "react-native";
import { ContentLayoutBuilder } from "~/contentCards/layouts/utils";
import { Flex } from "@ledgerhq/native-ui";
import { WidthFactor } from "~/contentCards/layouts/types";
import { useTheme } from "styled-components/native";
import { useItemStyle as getItemStyle } from "./utils";
import { ContentCardsType } from "~/dynamicContent/types";

type Props = {
styles?: {
Expand All @@ -13,77 +14,41 @@ type Props = {
};

const defaultStyles = {
gap: 12,
gap: 16,
widthFactor: WidthFactor.Half,
};

const stylesMap = {
firstStackCard: {
borderTopLeftRadius: 12,
borderTopRightRadius: 12,
},
lastStackCard: {
borderBottomLeftRadius: 12,
borderBottomRightRadius: 12,
},
middleCards: {
borderTopWidth: 1,
borderBottomWidth: 1,
},
};

const Grid = ContentLayoutBuilder<Props>(({ items, styles: _styles = defaultStyles }) => {
const { width: windowWidth } = useWindowDimensions();
const styles = {
gap: _styles.gap ?? defaultStyles.gap,
widthFactor: _styles.widthFactor ?? defaultStyles.widthFactor,
};
const { colors } = useTheme();

const width = useWindowDimensions().width * styles.widthFactor - 20;
const marginStack = (useWindowDimensions().width - width) / 2;
const marginSquare = (useWindowDimensions().width - 2 * width - styles.gap) / 2;
const isStack = styles.widthFactor === WidthFactor.Full;

const getItemStyle = (isNotSingleCard: boolean, isFirstItem: boolean, isLastItem: boolean) => {
if (isNotSingleCard) {
if (isLastItem) {
return stylesMap.lastStackCard;
}
if (isFirstItem) {
return {
...stylesMap.firstStackCard,
...(items.length === 2
? { borderBottomWidth: 1, borderBottomColor: colors.opacityDefault.c10 }
: {}),
};
}
return {
...stylesMap.middleCards,
borderTopColor: colors.opacityDefault.c10,
borderBottomColor: colors.opacityDefault.c10,
};
}
return { borderRadius: 12 };
};
const cardWidth =
styles.widthFactor === WidthFactor.Full
? windowWidth * styles.widthFactor - styles.gap * 2
: windowWidth * styles.widthFactor - styles.gap * 1.5;
const isStack =
styles.widthFactor === WidthFactor.Full && items[0].props.type === ContentCardsType.action;

return (
<Flex
style={{
marginLeft: isStack ? marginStack : marginSquare,
marginHorizontal: styles.gap,
justifyContent: "flex-start",
width: "100%",
flexDirection: "row",
flexWrap: "wrap",
gap: isStack ? 0 : 16,
gap: isStack ? 0 : styles.gap,
}}
>
{items.map((item, index) => {
const isLastItem = index === items.length - 1;
const isFirstItem = index === 0;
const itemStyle = getItemStyle(items.length > 1, isFirstItem, isLastItem);
return (
<Flex key={item.props.metadata.id} style={{ width }}>
<item.component {...item.props} itemStyle={itemStyle} />
<Flex key={item.props.metadata.id} style={{ width: cardWidth }}>
<item.component
{...item.props}
itemStyle={getItemStyle(items.length === 1, index === 0, index === items.length - 1)}
/>
</Flex>
);
})}
Expand Down
35 changes: 35 additions & 0 deletions apps/ledger-live-mobile/src/contentCards/layouts/grid/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useTheme } from "styled-components/native";

export const useItemStyle = (isSingleCard: boolean, isFirstItem: boolean, isLastItem: boolean) => {
const { colors } = useTheme();

const stylesMap = {
singleCard: {
borderRadius: 12,
},
firstStackCard: {
borderTopLeftRadius: 12,
borderTopRightRadius: 12,
},
lastStackCard: {
borderBottomLeftRadius: 12,
borderBottomRightRadius: 12,
borderTopWidth: 1,
borderTopColor: colors.opacityDefault.c10,
borderBottomColor: colors.opacityDefault.c10,
},
middleCards: {
borderTopWidth: 1,
borderTopColor: colors.opacityDefault.c10,
borderBottomColor: colors.opacityDefault.c10,
},
};

if (isSingleCard) return stylesMap.singleCard;

if (isLastItem) return stylesMap.lastStackCard;

if (isFirstItem) return stylesMap.firstStackCard;

return stylesMap.middleCards;
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { useCallback } from "react";
import Braze from "@braze/react-native-sdk";

export const useBrazeContentCard = () => {
const logDismissCard = useCallback(() => console.log("dismiss card"), []);
const logDismissCard = useCallback((cardId: string) => Braze.logContentCardDismissed(cardId), []);

const logClickCard = useCallback(() => console.log("dismiss card"), []);
const logClickCard = useCallback((cardId: string) => Braze.logContentCardClicked(cardId), []);

const logImpressionCard = useCallback(
(cardId: string) => Braze.logContentCardImpression(cardId),
Expand Down
1 change: 1 addition & 0 deletions apps/ledger-live-mobile/src/dynamicContent/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type ContentCardCommonProperties = {
order?: number;
carouselWidthFactor?: WidthFactor;
gridWidthFactor?: WidthFactor;
type?: ContentCardsType;
};

type CategoryContentCard = ContentCardCommonProperties & {
Expand Down
39 changes: 28 additions & 11 deletions apps/ledger-live-mobile/src/dynamicContent/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const mapAsCategoryContentCard = (card: BrazeContentCard): CategoryConten
description: card.extras.description,
link: card.extras.link,
cta: card.extras.cta,
isDismissable: Boolean(card.extras.isDismissable === "true"),
isDismissable: Boolean(card.extras?.isDismissable === "true"),
hasPagination: Boolean(card.extras?.hasPagination === "true"),
});

Expand Down Expand Up @@ -136,12 +136,12 @@ export const mapAsNotificationContentCard = (card: BrazeContentCard): Notificati
});

export const mapAsHorizontalContentCard = (card: BrazeContentCard): HorizontalContentCard => ({
type: ContentCardsType.action,
id: card.id,
tag: "rezae",
title: "Ledger Recover",
description: "Wallet recovery made easy",
image:
"https://s3-alpha-sig.figma.com/img/fc0f/d719/94af17d847ffbc4c43e4bc8b7a7e716a?Expires=1712534400&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=kS3h1eVswgs9zzDXFVcfHmdn2cRbyVBiXGoB-JavkDMGuo8htls3jWzyFIYbUscW~aVYdM9JMUygkfDrtRavxqkAGYbTPfyWFBBrsHrhE0IM69~vFUQRROJ44FbHgTeNW30ZgK9Kzvu4FG-zZOjPg-6C8~Kl40ZG9JxUbxLxbWzyLjjaXImzTVUzS3oj7VnuNcb0dnHOrWnWRjq6T9uiux2AMxgKQcTciRTdohEfleXggOHHj5qELJ~2wjjFMpFMe29DdmWocvDgWtS5lwVH5DKXt93~7XCoRI3OqXuUkXSuAKAzZn4ohHCB1KAi119U6HbuHxVrK15euyjg9owRhg__",
tag: card.extras.tag,
title: card.extras.title,
description: card.extras.description,
image: card.extras.image,
link: card.extras.link,
createdAt: card.created,
viewed: card.viewed,
Expand All @@ -152,8 +152,11 @@ export const mapAsHorizontalContentCard = (card: BrazeContentCard): HorizontalCo
const mapAsSquareContentCard = (
card: BrazeContentCard,
size: Size,
widthFactor: WidthFactor,
type: ContentCardsType,
carouselWidthFactor: WidthFactor,
gridWidthFactor: WidthFactor,
): VerticalContentCard => ({
type,
id: card.id,
tag: card.extras.tag,
title: card.extras.title,
Expand All @@ -166,11 +169,13 @@ const mapAsSquareContentCard = (
createdAt: card.created,
viewed: card.viewed,
order: parseInt(card.extras.order) ? parseInt(card.extras.order) : undefined,
carouselWidthFactor: widthFactor,
carouselWidthFactor,
gridWidthFactor,
filledImage: Boolean(card.extras.filledImage),
});

export const mapAsHeroContentCard = (card: BrazeContentCard): HeroContentCard => ({
type: ContentCardsType.hero,
id: card.id,
tag: card.extras.tag,
title: card.extras.title,
Expand All @@ -183,10 +188,22 @@ export const mapAsHeroContentCard = (card: BrazeContentCard): HeroContentCard =>
});

export const mapAsSmallSquareContentCard = (card: BrazeContentCard): VerticalContentCard =>
mapAsSquareContentCard(card, "S", WidthFactor.Half);
mapAsSquareContentCard(
card,
"S",
ContentCardsType.smallSquare,
WidthFactor.Half,
WidthFactor.Half,
);

export const mapAsMediumSquareContentCard = (card: BrazeContentCard): VerticalContentCard =>
mapAsSquareContentCard(card, "M", WidthFactor.ThreeQuarters);
mapAsSquareContentCard(
card,
"M",
ContentCardsType.mediumSquare,
WidthFactor.ThreeQuarters,
WidthFactor.Half,
);

export const mapAsBigSquareContentCard = (card: BrazeContentCard): VerticalContentCard =>
mapAsSquareContentCard(card, "L", WidthFactor.Full);
mapAsSquareContentCard(card, "L", ContentCardsType.bigSquare, WidthFactor.Full, WidthFactor.Full);

0 comments on commit 2b8a75f

Please sign in to comment.