Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: horizontal section and label for Clear Signing #7529

Merged
merged 15 commits into from
Aug 21, 2024
5 changes: 5 additions & 0 deletions .changeset/calm-lizards-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"live-mobile": patch
---

Add label clear signing
8 changes: 8 additions & 0 deletions apps/ledger-live-mobile/src/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -6848,12 +6848,16 @@
"components": {
"disclaimer": {
"clearSigningEnabled": "Clear signing enabled",
"clearSigningDisabled": "Clear signing disabled",
"checkbox": "Do not remind me again.",
"CTA": "Open {{app}}"
},
"manifestsList": {
"title": "Explore",
"description": "Discover the best of web3 curated by Ledger"
},
"label": {
"clearSigning": "clear signing"
}
},
"main": {
Expand All @@ -6863,6 +6867,10 @@
"header": {
"title": "Explore web3",
"placeholder": "Search or type a URL"
},
"clearSigning": {
"title": "Clear signing",
"description": "Clear signing allows you to sign a message without revealing the content of the message to the app. This is useful for privacy and security reasons."
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,32 @@ describe("Web3Hub integration test", () => {
expect(screen.getByRole("searchbox")).toBeDisabled();
});

it("Should be able to see Clear Signing section and label on disclaimer", async () => {
const { user } = render(<Web3HubTest />);
expect(await screen.findByText("Explore web3")).toBeOnTheScreen();
await waitForLoader();

expect((await screen.findAllByText("Clear signing"))[0]).toBeOnTheScreen();
expect((await screen.findAllByRole("banner", { name: /clear signing/i }))[0]).toBeOnTheScreen();

expect((await screen.findAllByText("Clear-signing"))[0]).toBeOnTheScreen();
await user.press(screen.getAllByText("Clear-signing")[0]);
expect(await screen.findByText("Clear signing enabled")).toBeOnTheScreen();

expect(await screen.findByText("Open Clear-signing")).toBeOnTheScreen();
await user.press(screen.getByText("Open Clear-signing"));
expect(await screen.findByText("clear-signing-0")).toBeOnTheScreen();
expect(await screen.findByText("Clear-signing")).toBeOnTheScreen();

expect(await screen.findByRole("button", { name: /back/i })).toBeOnTheScreen();
await user.press(screen.getByRole("button", { name: /back/i }));
expect(await screen.findByText("Explore web3")).toBeOnTheScreen();

expect((await screen.findAllByText("Dummy Wallet App"))[0]).toBeOnTheScreen();
await user.press(screen.getAllByText("Dummy Wallet App")[0]);
expect(await screen.findByText("Clear signing disabled")).toBeOnTheScreen();
});

it("Should only show the confirmation bottom modal if not dismissed previously", async () => {
const { user } = render(<Web3HubTest />);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export default function Disclaimer({
: undefined;
}, [locale, manifest?.content.description]);

const clearSigningEnabled = useMemo(() => {
return manifest?.categories.includes("clear signing");
}, [manifest?.categories]);

return (
<QueuedDrawer isRequestingToBeOpened={isOpened} onClose={onClose}>
{manifest ? (
Expand All @@ -43,14 +47,25 @@ export default function Disclaimer({

<Box mt={6} height="1px" width="100%" backgroundColor={"translucentGrey"} />

<Flex mt={6} flexDirection={"row"} alignItems={"center"}>
<Box mr={2}>
<Icons.Eye color={"smoke"} />
</Box>
<Text fontSize={14} color="smoke">
{t("web3hub.components.disclaimer.clearSigningEnabled")}
</Text>
</Flex>
{clearSigningEnabled ? (
<Flex mt={6} flexDirection={"row"} alignItems={"center"}>
<Box mr={2}>
<Icons.Eye color={"smoke"} />
</Box>
<Text fontSize={14} color="smoke">
{t("web3hub.components.disclaimer.clearSigningEnabled")}
</Text>
</Flex>
) : (
<Flex mt={6} flexDirection={"row"} alignItems={"center"}>
<Box mr={2}>
<Icons.EyeCross color={"smoke"} />
</Box>
<Text fontSize={14} color="smoke">
{t("web3hub.components.disclaimer.clearSigningDisabled")}
</Text>
</Flex>
)}

<Flex mt={6}>
<Checkbox
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react";
import { Text } from "@ledgerhq/native-ui";

type ItemStyle = {
badgeColor: string;
borderColor: string;
backgroundColor: string;
};

type Props = {
text: string;
style: ItemStyle;
};

const Label: React.FC<Props> = ({ text, style }) => {
const { badgeColor, borderColor, backgroundColor } = style;
return (
<Text
role="banner"
fontSize="9px"
width="auto"
paddingX={2}
paddingY={1}
borderWidth={1}
borderRadius={3}
borderStyle={"solid"}
flexGrow={0}
flexShrink={0}
overflow={"hidden"}
textTransform="uppercase"
color={badgeColor}
borderColor={borderColor}
backgroundColor={backgroundColor}
fontWeight="semiBold"
marginLeft={3}
>
{text}
</Text>
);
};

export default Label;
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AppBranch, AppManifest } from "@ledgerhq/live-common/wallet-api/types";
import type { MainProps, SearchProps } from "LLM/features/Web3Hub/types";
import AppIcon from "LLM/features/Web3Hub/components/AppIcon";
import { Theme } from "~/colors";
import Label from "./Label";

export type NavigationProp = MainProps["navigation"] | SearchProps["navigation"];

Expand Down Expand Up @@ -45,7 +46,6 @@ function getBranchStyle(branch: AppBranch, colors: Theme["colors"]) {
};
}
}

export default function ManifestItem({
manifest,
onPress,
Expand Down Expand Up @@ -78,40 +78,44 @@ export default function ManifestItem({
return manifest.icon?.trim();
}, [manifest.icon]);

const clearSigningEnabled = useMemo(() => {
return manifest?.categories.includes("clear signing");
}, [manifest?.categories]);

return (
<TouchableOpacity disabled={isDisabled} onPress={handlePress}>
<Flex flexDirection="row" alignItems="center" height={72} paddingX={4} paddingY={2}>
<AppIcon isDisabled={isDisabled} size={48} name={manifest.name} icon={icon} />
<Flex marginX={16} height="100%" flexGrow={1} flexShrink={1} justifyContent={"center"}>
<Flex flexDirection="row" alignItems={"center"} mb={2}>
<Flex flexDirection="row" alignItems={"center"} mb={2} columnGap={4}>
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure we'll go with columnGap while we use margins for everything else, and we'll probably remove or update the other because I don't think an experimental badge followed by a clear-signing one would probably take a lot of space on the line

Also columnGap below wouldn't add some margin left before the first badge so you instead have to add a columnGap above while it would not be needed with simpler margins

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the fact that margins are used for everything else is the main argument, then okay

Otherwise, I think gap is a better practice since we're using flex, and it's cleaner for managing spacing in this context. As we deal with flexbox and want to add spacing between items, it's better to avoid affecting the first or last element

<Text variant="large" color={color} numberOfLines={1} fontWeight="semiBold">
{manifest.name}
</Text>
{manifest.branch !== "stable" && (
<Text
fontSize="9px"
width="auto"
paddingX={2}
paddingY={1}
borderWidth={1}
borderRadius={3}
borderStyle={"solid"}
flexGrow={0}
flexShrink={0}
marginLeft={3}
overflow={"hidden"}
textTransform="uppercase"
color={badgeColor}
borderColor={borderColor}
backgroundColor={backgroundColor}
fontWeight="semiBold"
>
{t(`platform.catalog.branch.${manifest.branch}`, {
defaultValue: manifest.branch,
})}
</Text>
)}
<Flex flexDirection="row" alignItems={"center"}>
{manifest.branch !== "stable" && (
<Label
text={t(`platform.catalog.branch.${manifest.branch}`, {
defaultValue: manifest.branch,
})}
style={{ badgeColor, borderColor, backgroundColor }}
/>
)}

{clearSigningEnabled && (
<Label
text={t(`web3hub.components.label.clearSigning`, {
defaultValue: "Clear Signing",
})}
style={{
badgeColor: colors.live,
borderColor: colors.live,
backgroundColor: "transparent",
}}
/>
)}
</Flex>
</Flex>

<Text fontSize={13} color={colors.smoke} numberOfLines={1}>
{url}
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Props = {
title?: string;
pt?: number;
pb?: number;
headerComponent?: React.ReactNode;
};

const AnimatedFlashList = Animated.createAnimatedComponent<FlashListProps<AppManifest>>(FlashList);
Expand All @@ -39,7 +40,14 @@ const renderItem = ({
return <ManifestItem manifest={item} onPress={extraData} />;
};

export default function ManifestsList({ navigation, onScroll, title, pt = 0, pb = 0 }: Props) {
export default function ManifestsList({
navigation,
onScroll,
title,
pt = 0,
pb = 0,
headerComponent,
}: Props) {
const { t } = useTranslation();
const [selectedCategory, selectCategory] = useState("all");
const { data, isLoading, onEndReached } = useManifestsListViewModel(selectedCategory);
Expand Down Expand Up @@ -68,13 +76,13 @@ export default function ManifestsList({ navigation, onScroll, title, pt = 0, pb
}}
ListHeaderComponent={
<>
{headerComponent}
<Text mt={5} numberOfLines={1} variant="h5" mx={5} accessibilityRole="header">
{title ?? t("web3hub.components.manifestsList.title")}
</Text>
<Text mt={2} mb={5} numberOfLines={1} variant="body" mx={5} accessibilityRole="header">
{t("web3hub.components.manifestsList.description")}
</Text>

<View style={{ height: 32, marginBottom: 2 }}>
<CategoriesList selectedCategory={selectedCategory} selectCategory={selectCategory} />
</View>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { useCallback, useMemo } from "react";
import { TouchableOpacity } from "react-native";
import { Flex, Text } from "@ledgerhq/native-ui";
import AppIcon from "LLM/features/Web3Hub/components/AppIcon";
import { AppManifest } from "@ledgerhq/live-common/wallet-api/types";

export default function MinimalAppCard({
item,
onPress,
}: {
item: AppManifest;
onPress: (manifest: AppManifest) => void;
}) {
const disabled = useMemo(() => item.branch === "soon", [item]);
const handlePress = useCallback(() => {
if (!disabled) {
onPress(item);
}
}, [disabled, item, onPress]);

return (
<TouchableOpacity disabled={disabled} onPress={handlePress}>
<Flex rowGap={6} marginRight={3} width={70} alignItems={"center"}>
<AppIcon isDisabled={disabled} size={48} name={item.name} icon={item.icon} />
<Text numberOfLines={1}>{item.name}</Text>
</Flex>
</TouchableOpacity>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from "react";
import { StyleSheet } from "react-native";
import { FlashList } from "@shopify/flash-list";
import { Box, Flex, InfiniteLoader, Text } from "@ledgerhq/native-ui";
import { AppManifest } from "@ledgerhq/live-common/wallet-api/types";
import MinimalAppCard from "./MinimalAppCard";

type Props = {
title: string;
isLoading: boolean;
data: AppManifest[];
onEndReached?: () => void;
onPressItem: (manifest: AppManifest) => void;
testID?: string;
};

type PropRenderItem = {
item: AppManifest;
extraData?: (manifest: AppManifest) => void;
};

const identityFn = (item: AppManifest) => item.id;

const renderItem = ({ item, extraData: onPressItem = () => {} }: PropRenderItem) => (
<MinimalAppCard item={item} onPress={onPressItem} />
);

export default function HorizontalList({
title,
isLoading,
data,
onEndReached,
onPressItem,
testID,
}: Props) {
return (
<>
<Text mt={2} mb={5} numberOfLines={1} variant="h5" mx={5} accessibilityRole="header">
{title}
</Text>
<Box mb={2}>
<FlashList
testID={testID}
horizontal
contentContainerStyle={styles.container}
keyExtractor={identityFn}
renderItem={renderItem}
ListFooterComponent={
isLoading ? (
<Flex marginRight={4} justifyContent={"center"} paddingTop={3}>
<InfiniteLoader size={30} />
</Flex>
) : null
}
estimatedItemSize={70}
data={data}
showsHorizontalScrollIndicator={false}
extraData={onPressItem}
onEndReached={onEndReached}
/>
</Box>
</>
);
}

const styles = StyleSheet.create({
container: {
paddingHorizontal: 5,
},
});
Loading
Loading