Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
Improve invite card layout.
Browse files Browse the repository at this point in the history
Prepare for separate links for each invite which will help prevent a
rushing adversary attack, see #70.
  • Loading branch information
tmpfs committed Apr 26, 2022
1 parent 7365d60 commit 7f4dfae
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 64 deletions.
75 changes: 15 additions & 60 deletions snap/dapp/src/keys/create/invite-people.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,17 @@
import React, { useState, useEffect, useContext } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useSelector } from "react-redux";

import {
Box,
Stack,
Button,
Typography,
Paper,
Link,
CircularProgress,
} from "@mui/material";

import { keysSelector } from "../../store/keys";
import { setSnackbar } from "../../store/snackbars";
import { copyToClipboard } from "../../utils";
import { WebSocketContext } from "../../websocket-provider";

import { StepProps } from "./index";

type InviteProps = {
onCopy: () => void;
};

function InviteCard(props: InviteProps) {
const dispatch = useDispatch();
const { onCopy } = props;
const { group, session } = useSelector(keysSelector);
const href = `${location.protocol}//${location.host}/#/keys/join/${group.uuid}/${session.uuid}`;

const copy = async () => {
await copyToClipboard(href);

dispatch(setSnackbar({
message: 'Link copied to clipboard',
severity: 'success'
}));

onCopy();
};

return (
<>
<Paper variant="outlined">
<Box padding={4}>
<details>
<summary>
<Typography
variant="body2"
component="span"
color="text.secondary"
>
Send this link via email or private message to the people you
wish to invite.
</Typography>
</summary>
<Link href={href} onClick={(e) => e.preventDefault()}>
{href}
</Link>
</details>
<Button onClick={copy} sx={{ mt: 4 }}>
Copy link to clipboard
</Button>
</Box>
</Paper>
</>
);
}
import InviteCard, { inviteHref } from '../invite-card';

export default function InvitePeople(props: StepProps) {
const { next } = props;
Expand All @@ -77,15 +23,18 @@ export default function InvitePeople(props: StepProps) {
// All parties signed up to key generation
websocket.once("sessionSignup", async (sessionId: string) => {
if (sessionId === session.uuid) {
console.log("Owner got session ready...");
next();
} else {
throw new Error("Session id is for another session");
}
});
}, []);

const onCopy = () => setShowProgress(true);
const onCopy = () => {
if (!showProgress) {
setShowProgress(true);
}
}

const Progress = () => (
<Stack>
Expand All @@ -98,6 +47,10 @@ export default function InvitePeople(props: StepProps) {
</Stack>
);

const totalInvites = group.params.parties - 1;
const href = inviteHref("keys/join", group.uuid, session.uuid)
const links = Array(totalInvites).fill("").map(() => href);

return (
<Stack padding={2} spacing={2} marginTop={2}>
<Stack>
Expand All @@ -111,12 +64,14 @@ export default function InvitePeople(props: StepProps) {
Invite people to share the new key with you.
</Typography>
<Typography variant="body2" component="div" color="text.secondary">
You must invite {group.params.parties - 1} people to continue creating
You must invite {totalInvites} people to continue creating
a key.
</Typography>
</Stack>
<Stack>
<InviteCard onCopy={onCopy} />
<InviteCard
onCopy={onCopy}
links={links} />
</Stack>
{showProgress ? <Progress /> : null}
</Stack>
Expand Down
4 changes: 2 additions & 2 deletions snap/dapp/src/keys/import.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,9 @@ function ImportStepper() {
};

const components = [
<UploadKeyStore key="upload" setFile={setFile} />,
<UploadKeyStore key={0} setFile={setFile} />,
<EnterPassword
key="password"
key={1}
onSubmit={handleNext}
onChange={onPasswordChange}
value={password} />,
Expand Down
141 changes: 141 additions & 0 deletions snap/dapp/src/keys/invite-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React from "react";
import { useDispatch } from "react-redux";

import {
Box,
Stack,
Typography,
Paper,
Link,
} from "@mui/material";

import ContentCopyIcon from '@mui/icons-material/ContentCopy';

import { setSnackbar } from "../store/snackbars";
import { copyToClipboard } from "../utils";

export function inviteHref(
hrefPrefix: string,
groupId: string,
sessionId: string) {
return `${location.protocol}//${location.host}/#/${hrefPrefix}/${groupId}/${sessionId}`;
}

type InviteProps = {
onCopy: () => void;
links: string[];
};

type InviteLinkProps = {
onCopy: () => void;
href: string;
index: number;
}

function InviteLink(props: InviteLinkProps) {
const dispatch = useDispatch();
const {href, onCopy, index} = props;

const copy = async (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
await copyToClipboard(href);
dispatch(setSnackbar({
message: 'Link copied to clipboard',
severity: 'success'
}));
onCopy();
};

return (
<Stack direction="row"
onClick={copy}
sx={{
cursor: 'pointer',
}}>
<Box component="span"
sx={{
border: '1px solid gray',
padding: '8px',
borderTopLeftRadius: '4px',
borderBottomLeftRadius: '4px',
}}>
<Typography
variant="body2"
component="span"
color="text.secondary"
>
#{index + 1}
</Typography>
</Box>
<Box
sx={{
border: '1px solid gray',
overflow: 'hidden',
padding: '0 8px',
alignItems: 'center',
display: 'flex',
}}
component="span">
<Link
href={href}
onClick={copy}
sx={{
whiteSpace: 'nowrap',
textDecoration: 'none',
}}>
{href}
</Link>
</Box>
<Box
component="span"
sx={{
border: '1px solid gray',
padding: '8px',
borderTopRightRadius: '4px',
borderBottomRightRadius: '4px',
}}>
<Stack direction="row" spacing={1}>
<ContentCopyIcon color="primary" />
</Stack>
</Box>
</Stack>
);
}

export default function InviteCard(props: InviteProps) {
const { onCopy, links } = props;

return (
<Paper variant="outlined">
<Stack padding={4} spacing={2}>
<Stack>
<Typography
variant="body1"
component="span"
>
Send these links via email or private message to the people you
wish to invite.
</Typography>
<Typography
variant="body2"
component="span"
color="text.secondary"
>
Click a link to copy it to your clipboard
</Typography>
</Stack>
{
links.map((href, index) => {
return (
<InviteLink
key={index}
href={href}
onCopy={onCopy}
index={index} />
);
})
}
</Stack>
</Paper>
);
}
18 changes: 16 additions & 2 deletions snap/dapp/src/keys/sign/message/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,32 @@ import {
Typography,
} from "@mui/material";

import {
keccak256,
} from "@metamask/mpc-snap-wasm";

import {encode} from '../../../utils';

import NotFound from '../../../not-found';
import PublicAddress from "../../../components/public-address";
import {keysSelector, KeyShareGroup} from '../../../store/keys';
import SignStepper from '../../../components/stepper';
import KeysLoader from '../../loader';

import CreateMessage from './create-message';
import InvitePeople from './invite-people';

const steps = [
"Create message",
"Invite participants",
"Invite people",
"Compute",
"Save Proof"
];

const getStepComponent = (activeStep: number, props: SignMessageProps) => {
const stepComponents = [
<CreateMessage key={0} {...props} />,
<InvitePeople key={1} {...props} />,
];
return stepComponents[activeStep];
};
Expand All @@ -35,6 +44,7 @@ export type SignMessageProps = {
onShareChange: (partyNumber: number) => void;
selectedParty: number;
message: string;
messageHash: Uint8Array,
onMessage: (message: string) => void;
};

Expand All @@ -43,6 +53,7 @@ export default function SignMessage() {
const { keyShares, loaded } = useSelector(keysSelector);
const [activeStep, setActiveStep] = useState(0);
const [message, setMessage] = useState("");
const [messageHash, setMessageHash] = useState(new Uint8Array());
const [selectedParty, setSelectedParty] = useState(null);

if (!loaded) {
Expand Down Expand Up @@ -73,8 +84,10 @@ export default function SignMessage() {
setSelectedParty(n);
}

const onMessage = (message: string) => {
const onMessage = async (message: string) => {
setMessage(message);
const digest = await keccak256(Array.from(encode(message)));
setMessageHash(digest);
handleNext();
}

Expand All @@ -85,6 +98,7 @@ export default function SignMessage() {
onShareChange,
message,
onMessage,
messageHash,
};

return (
Expand Down
34 changes: 34 additions & 0 deletions snap/dapp/src/keys/sign/message/invite-people.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';

import {
Stack,
Paper,
Typography,
Divider,
} from "@mui/material";

import { SignMessageProps } from './index';
import {toHexString} from '../../../utils';

export default function InvitePeople(props: SignMessageProps) {
const { message, messageHash } = props;
return (
<Stack spacing={4}>
<Paper variant="outlined">
<Stack padding={2} spacing={2}>
<Typography variant="subtitle1" component="div">
Message
</Typography>
<Divider />
<Typography variant="body1" component="div">
{message}
</Typography>
<Divider />
<Typography variant="body2" component="div" color="text.secondary">
{toHexString(messageHash)}
</Typography>
</Stack>
</Paper>
</Stack>
)
}
11 changes: 11 additions & 0 deletions snap/dapp/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ export const abbreviateAddress = (address: string): string => {
return `${start}...${end}`;
};

export function fromHexString(hex: string) {
return new Uint8Array(
hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
}

export function toHexString(bytes: Uint8Array) {
return bytes.reduce(
(str: string, byte: number) => str + byte.toString(16).padStart(2, '0'),
'');
}

export type Dictionary<T> = {
[key: string]: T;
};
Expand Down

0 comments on commit 7f4dfae

Please sign in to comment.