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

Show messages in one card #1190

Merged
merged 5 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 0 additions & 33 deletions packages/keychain/src/components/session/CollapsibleRow.tsx

This file was deleted.

117 changes: 46 additions & 71 deletions packages/keychain/src/components/session/ContractCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from "react";
import {
Card,
CardContent,
Expand All @@ -7,17 +6,10 @@ import {
AccordionContent,
AccordionItem,
AccordionTrigger,
CardIcon,
CodeIcon,
} from "@cartridge/ui-next";
import { formatAddress } from "@cartridge/utils";
import {
Divider,
HStack,
Link,
Spacer,
Stack,
Text,
VStack,
} from "@chakra-ui/react";
import { useExplorer } from "@starknet-react/core";
import { constants } from "starknet";
import { Method } from "@cartridge/presets";
Expand All @@ -41,80 +33,63 @@ export function ContractCard({

return (
<Card>
<CardHeader icon={icon}>
<HStack py={4}>
<Text
color="text.primary"
fontSize="xs"
fontWeight="bold"
textTransform="uppercase"
>
{title}
</Text>
<Spacer />
<Link
color="text.secondary"
fontSize="xs"
<CardHeader
icon={
icon ?? (
<CardIcon>
<CodeIcon variant="solid" />
</CardIcon>
)
}
>
<div className="flex items-center justify-between">
<div className="text-xs font-bold uppercase">{title}</div>
<a
className="text-xs text-muted-foreground cursor-pointer hover:underline"
href={
chainId === constants.StarknetChainId.SN_MAIN ||
chainId === constants.StarknetChainId.SN_SEPOLIA
? explorer.contract(address)
: `#`
}
target="_blank"
rel="noreferrer"
>
{formatAddress(address, { first: 5, last: 5 })}
</Link>
</HStack>
</a>
</div>
</CardHeader>

<CardContent>
<Accordion type="multiple" defaultValue={["methods"]}>
<AccordionItem value="methods">
<AccordionTrigger>
<Text color="text.secondary" fontSize="xs">
Approve{" "}
<Text as="span" color="text.secondaryAccent" fontWeight="bold">
{methods.length} {methods.length > 1 ? "methods" : "method"}
</Text>
</Text>
<AccordionItem value="methods" className="flex flex-col gap-4">
<AccordionTrigger className="text-xs text-muted-foreground">
Approve{" "}
<span className="text-accent-foreground font-bold">
{methods.length} {methods.length > 1 ? "methods" : "method"}
</span>
</AccordionTrigger>
<AccordionContent>
<Stack
border="1px solid"
spacing={0}
borderColor="darkGray.800"
borderRadius="md"
mt="14px"
divider={<Divider borderColor="solid.bg" />}
>
{methods.map((method) => (
<VStack
key={method.entrypoint}
p={3}
gap={3}
align="flex-start"
>
<HStack w="full">
<Text
fontSize="xs"
color="text.primary"
fontWeight="bold"
>
{method.name ?? humanizeString(method.entrypoint)}
</Text>
<Spacer />
<Text fontSize="xs" color="text.secondaryAccent">
{method.entrypoint}
</Text>
</HStack>
{method.description && (
<Text fontSize="xs" color="text.secondary">
{method.description}
</Text>
)}
</VStack>
))}
</Stack>
<AccordionContent className="bg-background border border-background rounded-md gap-px">
{methods.map((method) => (
<div
key={method.entrypoint}
className="flex flex-col bg-secondary gap-4 p-3 first:rounded-t-md last:rounded-b-md text-xs"
>
<div className="flex items-center justify-between">
<div className="font-bold">
{method.name ?? humanizeString(method.entrypoint)}
</div>
<div className="text-accent-foreground">
{method.entrypoint}
</div>
</div>
{method.description && (
<div className="text-muted-foreground">
{method.description}
</div>
)}
</div>
))}
</AccordionContent>
</AccordionItem>
</Accordion>
Expand Down
180 changes: 109 additions & 71 deletions packages/keychain/src/components/session/MessageCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import { PropsWithChildren, useState } from "react";
import {
Card,
CardContent,
Expand All @@ -10,67 +10,17 @@ import {
CardIcon,
PencilIcon,
AccordionTrigger,
CheckboxIcon,
} from "@cartridge/ui-next";
import { ArrowTurnDownIcon, Badge } from "@cartridge/ui-next";
import { StarknetEnumType, StarknetMerkleType } from "@starknet-io/types-js";
import { SignMessagePolicy } from "@cartridge/presets";
import { Text } from "@chakra-ui/react";

import { CollapsibleRow } from "./CollapsibleRow";

interface MessageContentProps {
message: SignMessagePolicy;
}

function MessageContent({ message: m }: MessageContentProps) {
return (
<CardContent className="text-muted-foreground flex flex-col">
{/* Domain section */}
{Object.values(m.domain).filter((f) => typeof f !== "undefined").length >
0 && (
<CollapsibleRow key="domain" title="domain">
{m.domain.name && (
<ValueRow values={[{ name: "name", value: m.domain.name }]} />
)}
{/* ... other domain fields ... */}
</CollapsibleRow>
)}

<ValueRow values={[{ name: "primaryType", value: m.primaryType }]} />

<CollapsibleRow title="types">
{Object.entries(m.types).map(([name, types]) => (
<CollapsibleRow key={name} title={name}>
{types.map((t) => (
<ValueRow
key={t.name}
values={[
{ name: "name", value: t.name },
{ name: "type", value: t.type },
...(["enum", "merkletree"].includes(t.name)
? [
{
name: "contains",
value: (t as StarknetEnumType | StarknetMerkleType)
.contains,
},
]
: []),
]}
/>
))}
</CollapsibleRow>
))}
</CollapsibleRow>
</CardContent>
);
}

interface MessageCardProps {
message: SignMessagePolicy;
messages: SignMessagePolicy[];
}

export function MessageCard({ message }: MessageCardProps) {
export function MessageCard({ messages }: MessageCardProps) {
return (
<Card>
<CardHeader
Expand All @@ -83,33 +33,121 @@ export function MessageCard({ message }: MessageCardProps) {
<CardTitle className="text-foreground">Sign Message</CardTitle>
</CardHeader>

<Accordion type="single" defaultValue="message" collapsible>
<AccordionItem value="message">
<CardContent>
<AccordionTrigger>
<Text color="text.secondary" fontSize="xs">
The application will be able to sign the following message on
your behalf
</Text>
</AccordionTrigger>
</CardContent>

<AccordionContent>
<MessageContent message={message} />
</AccordionContent>
</AccordionItem>
</Accordion>
<CardContent>
<MessageContent messages={messages} />
</CardContent>
</Card>
);
}

interface MessageContentProps {
messages: SignMessagePolicy[];
}

function MessageContent({ messages }: MessageContentProps) {
return (
<Accordion type="single" defaultValue="message" collapsible>
<AccordionItem value="message" className="flex flex-col gap-3">
<AccordionTrigger
className="text-xs text-muted-foreground"
color="text.secondary"
>
Approve{" "}
<span className="text-accent-foreground font-bold">
{messages.length} {messages.length > 1 ? "messages" : "message"}
</span>
</AccordionTrigger>

<AccordionContent className="text-xs flex flex-col bg-background border border-background rounded-md gap-px">
{messages.map((m, i) => (
<div className="flex flex-col bg-secondary gap-4 p-3 first:rounded-t-md last:rounded-b-md">
<div className="font-bold">{m.name ?? `Message ${i + 1}`}</div>
JunichiSugiura marked this conversation as resolved.
Show resolved Hide resolved
<div className="flex flex-col bg-secondary gap-1 ">
{/* Domain section */}
{Object.values(m.domain).filter((f) => typeof f !== "undefined")
.length > 0 && (
<CollapsibleRow key="domain" title="domain">
{m.domain.name && (
<ValueRow
values={[{ name: "name", value: m.domain.name }]}
/>
)}
{/* ... other domain fields ... */}
</CollapsibleRow>
)}

<ValueRow
values={[{ name: "primaryType", value: m.primaryType }]}
/>

<CollapsibleRow title="types">
{Object.entries(m.types).map(([name, types]) => (
<CollapsibleRow key={name} title={name}>
{types.map((t) => (
<ValueRow
key={t.name}
values={[
{ name: "name", value: t.name },
{ name: "type", value: t.type },
...(["enum", "merkletree"].includes(t.name)
? [
{
name: "contains",
value: (
t as StarknetEnumType | StarknetMerkleType
).contains,
},
]
: []),
]}
/>
))}
</CollapsibleRow>
))}
</CollapsibleRow>
</div>
</div>
))}
</AccordionContent>
</AccordionItem>
</Accordion>
);
}
interface CollapsibleRowProps extends PropsWithChildren {
title: string;
}

export function CollapsibleRow({ title, children }: CollapsibleRowProps) {
const [value, setValue] = useState("");

return (
<Accordion type="single" collapsible value={value} onValueChange={setValue}>
<AccordionItem value={title} className="flex flex-col">
<AccordionTrigger hideIcon className="hover:bg-accent rounded-md">
<div className="flex items-center gap-1 py-1">
<CheckboxIcon
variant={value ? "minus-line" : "plus-line"}
size="sm"
/>
<div>{title}</div>
</div>
</AccordionTrigger>

<AccordionContent className="ml-5 flex flex-col">
{children}
</AccordionContent>
</AccordionItem>
</Accordion>
);
}

interface ValueRowProps {
values: { name: string; value: string | number }[];
}

export function ValueRow({ values }: ValueRowProps) {
return (
<div className="flex items-center py-2">
<div className="flex items-center py-1">
<ArrowTurnDownIcon />
<div className="flex items-center gap-2">
{values.map((f) => (
Expand Down
Loading
Loading