mirror of https://github.com/waku-org/js-waku.git
Enable recipient selection and message input
This commit is contained in:
parent
0c825fe391
commit
66fcb2e48b
|
@ -12,13 +12,14 @@ import {
|
||||||
validatePublicKeyMessage,
|
validatePublicKeyMessage,
|
||||||
} from './crypto';
|
} from './crypto';
|
||||||
import * as EthCrypto from 'eth-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 { Message, Messages } from './Messages';
|
||||||
import 'fontsource-roboto';
|
import 'fontsource-roboto';
|
||||||
import { Button } from '@material-ui/core';
|
import { Button } from '@material-ui/core';
|
||||||
|
import { SendMessage } from './SendMessage';
|
||||||
|
|
||||||
const PublicKeyContentTopic = '/eth-dm/1/public-key/json';
|
export const PublicKeyContentTopic = '/eth-dm/1/public-key/json';
|
||||||
const DirectMessageContentTopic = '/eth-dm/1/direct-message/json';
|
export const DirectMessageContentTopic = '/eth-dm/1/direct-message/json';
|
||||||
|
|
||||||
declare let window: any;
|
declare let window: any;
|
||||||
|
|
||||||
|
@ -126,27 +127,19 @@ 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';
|
const wakuReady = !!waku ? 'Waku is ready' : 'Waku is loading';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<header className="App-header">
|
<header className="App-header">
|
||||||
{wakuReady}
|
{wakuReady}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -163,17 +156,9 @@ function App() {
|
||||||
>
|
>
|
||||||
Broadcast Eth-DM Public Key
|
Broadcast Eth-DM Public Key
|
||||||
</Button>
|
</Button>
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
onClick={sendDummyMessage}
|
|
||||||
disabled={!waku || publicKeys.size === 0}
|
|
||||||
>
|
|
||||||
Send Direct Message
|
|
||||||
</Button>
|
|
||||||
<Messages messages={messages} />
|
|
||||||
</div>
|
</div>
|
||||||
|
<SendMessage recipients={publicKeys} waku={waku} />
|
||||||
|
<Messages messages={messages} />
|
||||||
</header>
|
</header>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -208,22 +193,6 @@ function encodePublicKeyWakuMessage(ethDmMsg: PublicKeyMessage): WakuMessage {
|
||||||
return WakuMessage.fromBytes(payload, PublicKeyContentTopic);
|
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(
|
function handlePublicKeyMessage(
|
||||||
setter: Dispatch<SetStateAction<Map<string, string>>>,
|
setter: Dispatch<SetStateAction<Map<string, string>>>,
|
||||||
msg: WakuMessage
|
msg: WakuMessage
|
||||||
|
@ -264,14 +233,3 @@ async function handleDirectMessage(
|
||||||
return copy;
|
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
|
@ -16,3 +16,14 @@ export interface DirectMessage {
|
||||||
toAddress: string;
|
toAddress: string;
|
||||||
encMessage: EthCrypto.Encrypted;
|
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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue