Enable recipient selection and message input

This commit is contained in:
Franck Royer 2021-06-18 15:11:54 +10:00
parent 0c825fe391
commit 66fcb2e48b
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
3 changed files with 180 additions and 65 deletions

View File

@ -12,13 +12,14 @@ import {
validatePublicKeyMessage,
} from './crypto';
import * as EthCrypto from 'eth-crypto';
import { DirectMessage, PublicKeyMessage } from './messages';
import { decode, DirectMessage, encode, PublicKeyMessage } from './messages';
import { Message, Messages } from './Messages';
import 'fontsource-roboto';
import { Button } from '@material-ui/core';
import { SendMessage } from './SendMessage';
const PublicKeyContentTopic = '/eth-dm/1/public-key/json';
const DirectMessageContentTopic = '/eth-dm/1/direct-message/json';
export const PublicKeyContentTopic = '/eth-dm/1/public-key/json';
export const DirectMessageContentTopic = '/eth-dm/1/direct-message/json';
declare let window: any;
@ -126,54 +127,38 @@ function App() {
}
};
const sendDummyMessage = () => {
if (!waku) return;
console.log(`Sending messages to ${publicKeys.size} peers`);
publicKeys.forEach(async (publicKey, address) => {
const msg = await encodeEncryptedWakuMessage(
'Here is a secret message',
publicKey,
address
);
await waku?.lightPush.push(msg);
});
};
const wakuReady = !!waku ? 'Waku is ready' : 'Waku is loading';
return (
<div className="App">
<header className="App-header">
{wakuReady}
<Button
variant="contained"
color="primary"
onClick={generateKeyPair}
disabled={!provider}
<div
style={{
display: 'flex',
alignItems: 'center',
flexWrap: 'wrap',
}}
>
Generate Eth-DM Key Pair
</Button>
<Button
variant="contained"
color="primary"
onClick={broadcastPublicKey}
disabled={!ethDmKeyPair || !waku}
>
Broadcast Eth-DM Public Key
</Button>
<div>
<Button
variant="contained"
color="primary"
onClick={sendDummyMessage}
disabled={!waku || publicKeys.size === 0}
onClick={generateKeyPair}
disabled={!provider}
>
Send Direct Message
Generate Eth-DM Key Pair
</Button>
<Button
variant="contained"
color="primary"
onClick={broadcastPublicKey}
disabled={!ethDmKeyPair || !waku}
>
Broadcast Eth-DM Public Key
</Button>
<Messages messages={messages} />
</div>
<SendMessage recipients={publicKeys} waku={waku} />
<Messages messages={messages} />
</header>
</div>
);
@ -208,22 +193,6 @@ function encodePublicKeyWakuMessage(ethDmMsg: PublicKeyMessage): WakuMessage {
return WakuMessage.fromBytes(payload, PublicKeyContentTopic);
}
async function encodeEncryptedWakuMessage(
message: string,
publicKey: string,
address: string
): Promise<WakuMessage> {
const encryptedMsg = await EthCrypto.encryptWithPublicKey(publicKey, message);
const directMsg: DirectMessage = {
toAddress: address,
encMessage: encryptedMsg,
};
const payload = encode(directMsg);
return WakuMessage.fromBytes(payload, DirectMessageContentTopic);
}
function handlePublicKeyMessage(
setter: Dispatch<SetStateAction<Map<string, string>>>,
msg: WakuMessage
@ -264,14 +233,3 @@ async function handleDirectMessage(
return copy;
});
}
function encode<T>(msg: T): Buffer {
const jsonStr = JSON.stringify(msg);
return Buffer.from(jsonStr, 'utf-8');
}
function decode<T>(bytes: Uint8Array): T {
const buf = Buffer.from(bytes);
const str = buf.toString('utf-8');
return JSON.parse(str);
}

View File

@ -0,0 +1,146 @@
import {
FormControl,
InputLabel,
makeStyles,
MenuItem,
Select,
TextField,
} from '@material-ui/core';
import React, { ChangeEvent, useState, KeyboardEvent } from 'react';
import { Waku, WakuMessage } from 'js-waku';
import * as EthCrypto from 'eth-crypto';
import { DirectMessage, encode } from './messages';
import { DirectMessageContentTopic } from './App';
const useStyles = makeStyles((theme) => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}));
export interface Props {
waku: Waku | undefined;
// address, public key
recipients: Map<string, string>;
}
export function SendMessage(props: Props) {
const classes = useStyles();
const [recipient, setRecipient] = useState<string>('');
const [message, setMessage] = useState<string>();
const waku = props.waku;
const handleRecipientChange = (
event: ChangeEvent<{ name?: string; value: unknown }>
) => {
setRecipient(event.target.value as string);
};
const handleMessageChange = (event: ChangeEvent<HTMLInputElement>) => {
setMessage(event.target.value);
};
const items = Array.from(props.recipients.keys()).map((recipient) => {
return <MenuItem value={recipient}>{recipient}</MenuItem>;
});
const keyDownHandler = async (event: KeyboardEvent<HTMLInputElement>) => {
if (
event.key === 'Enter' &&
!event.altKey &&
!event.ctrlKey &&
!event.shiftKey
) {
if (!waku) return;
if (!recipient) return;
if (!message) return;
const publicKey = props.recipients.get(recipient);
if (!publicKey) return;
sendMessage(waku, recipient, publicKey, message, (res) => {
if (res) {
console.log('callback called with', res);
setMessage('');
}
});
}
};
return (
<div
style={{
display: 'flex',
alignItems: 'center',
flexWrap: 'wrap',
}}
>
<FormControl className={classes.formControl}>
<InputLabel id="select-recipient-label">Recipient</InputLabel>
<Select
labelId="select-recipient"
id="select-recipient"
value={recipient}
onChange={handleRecipientChange}
>
{items}
</Select>
</FormControl>
<TextField
id="message-input"
label="Message"
variant="filled"
onChange={handleMessageChange}
onKeyDown={keyDownHandler}
value={message}
/>
</div>
);
}
async function encodeEncryptedWakuMessage(
message: string,
publicKey: string,
address: string
): Promise<WakuMessage> {
const encryptedMsg = await EthCrypto.encryptWithPublicKey(publicKey, message);
const directMsg: DirectMessage = {
toAddress: address,
encMessage: encryptedMsg,
};
const payload = encode(directMsg);
return WakuMessage.fromBytes(payload, DirectMessageContentTopic);
}
function sendMessage(
waku: Waku,
recipientAddress: string,
recipientPublicKey: string,
message: string,
callback: (res: boolean) => void
) {
encodeEncryptedWakuMessage(message, recipientPublicKey, recipientAddress)
.then((msg) => {
console.log('pushing');
waku.lightPush
.push(msg)
.then((res) => {
console.log('Message sent', res);
callback(res ? res.isSuccess : false);
})
.catch((e) => {
console.error('Failed to send message', e);
callback(false);
});
})
.catch((e) => {
console.error('Cannot encode & encrypt message', e);
callback(false);
});
}

View File

@ -16,3 +16,14 @@ export interface DirectMessage {
toAddress: string;
encMessage: EthCrypto.Encrypted;
}
export function encode<T>(msg: T): Buffer {
const jsonStr = JSON.stringify(msg);
return Buffer.from(jsonStr, 'utf-8');
}
export function decode<T>(bytes: Uint8Array): T {
const buf = Buffer.from(bytes);
const str = buf.toString('utf-8');
return JSON.parse(str);
}