Merge pull request #245 from status-im/guides

This commit is contained in:
Franck Royer 2021-08-02 13:35:32 +10:00 committed by GitHub
commit 8aadaacc54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 39385 additions and 89 deletions

View File

@ -12,7 +12,7 @@ jobs:
examples_build_and_test:
strategy:
matrix:
example: [ cli-chat, web-chat, eth-dm ]
example: [ cli-chat, web-chat, eth-dm, min-js-web-chat ]
runs-on: ubuntu-latest
steps:

1
.gitignore vendored
View File

@ -2,7 +2,6 @@
.nyc_output
build
node_modules
test
src/**.js
coverage
*.log

View File

@ -265,6 +265,19 @@ npm run doc
Release changelog can be found [here](https://github.com/status-im/js-waku/blob/main/CHANGELOG.md).
## Bugs, Questions & Features
If you encounter any bug or would like to propose new features, feel free to [open an issue](https://github.com/status-im/js-waku/issues/new/).
To get help, join #dappconnect-support on [Vac Discord](https://discord.gg/j5pGbn7MHZ) or [Telegram](https://t.me/dappconnectsupport).
For more general discussion and latest news, join #dappconnect on [Vac Discord](https://discord.gg/9DgykdmpZ6) or [Telegram](https://t.me/dappconnect).
## Examples
We have a number of code examples available,
you can find them in the [examples](https://github.com/status-im/js-waku/blob/main/examples/examples.md) directory.
## Waku Protocol Support
You can track progress on the [project board](https://github.com/status-im/js-waku/projects/1).
@ -290,88 +303,6 @@ You can track progress on the [project board](https://github.com/status-im/js-wa
|[18/WAKU2-SWAP](https://rfc.vac.dev/spec/18)||
|[19/WAKU2-LIGHTPUSH](https://rfc.vac.dev/spec/19/)|✔|
## Bugs, Questions & Features
If you encounter any bug or would like to propose new features, feel free to [open an issue](https://github.com/status-im/js-waku/issues/new/).
To get help, join #dappconnect-support on [Vac Discord](https://discord.gg/j5pGbn7MHZ) or [Telegram](https://t.me/dappconnectsupport).
For more general discussion and latest news, join #dappconnect on [Vac Discord](https://discord.gg/9DgykdmpZ6) or [Telegram](https://t.me/dappconnect).
## Examples
## Web Chat App (ReactJS)
A ReactJS chat app is provided as a showcase of the library used in the browser.
It implements [Waku v2 Toy Chat](https://rfc.vac.dev/spec/22/) protocol.
A deployed version is available at https://status-im.github.io/js-waku/.
Find the code in the [examples folder](https://github.com/status-im/js-waku/tree/main/examples/web-chat).
To run a development version locally, do:
```shell
git clone https://github.com/status-im/js-waku/ ; cd js-waku
npm install # Install dependencies for js-waku
npm run build # Build js-waku
cd examples/web-chat
npm install # Install dependencies for the web app
npm run start # Start development server to serve the web app on http://localhost:3000/js-waku
```
Use `/help` to see the available commands.
## CLI Chat App (NodeJS)
A node chat app is provided as a working example of the library.
It implements [Waku v2 Toy Chat](https://rfc.vac.dev/spec/22/) protocol.
Find the code in the [examples folder](https://github.com/status-im/js-waku/tree/main/examples/cli-chat).
To run the chat app, first ensure you have [Node.js](https://nodejs.org/en/) v14 or above:
```shell
node --version
```
Then, install and run:
```shell
git clone https://github.com/status-im/js-waku/ ; cd js-waku
npm install # Install dependencies for js-waku
npm run build # Build js-waku
cd examples/cli-chat
npm install # Install dependencies for the cli app
npm run start -- --autoDial
```
You can also specify an optional `listenAddr` parameter (.e.g `--listenAddr /ip4/0.0.0.0/tcp/7777/ws`).
This is only useful if you want a remote node to dial to your chat app,
it is not necessary in normal usage when you just connect to the fleet.
## Ethereum Direct Message
A PoC implementation of [20/ETH-DM](https://rfc.vac.dev/spec/20/).
Ethereum Direct Message, or Eth-DM, is a protocol that allows sending encrypted message to a recipient,
only knowing their Ethereum Address.
This is protocol has been created to demonstrated how encryption and signature could be added to messages
sent over the Waku v2 network.
The `main` branch's HEAD is deployed on GitHub Pages at https://status-im.github.io/js-waku/eth-dm/.
To run a development version locally, do:
```shell
git clone https://github.com/status-im/js-waku/ ; cd js-waku
npm install # Install dependencies for js-waku
npm run build # Build js-waku
cd examples/eth-dm
npm install # Install dependencies for the web app
npm run start # Start development server to serve the web app on http://localhost:3000/js-waku/eth-dm
```
## Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md).

View File

@ -1,3 +1,35 @@
# A NodeJS CLI Chat App powered by js-waku
# CLI Chat App
See js-waku [README](../../README.md#cli-chat-app-nodejs) for details.
**Demonstrates**:
- Group chat
- Node JS/TypeScript
- Waku Relay
- Waku Light Push
- Waku Store
A node chat app is provided as a working example of the library.
It implements [Waku v2 Toy Chat](https://rfc.vac.dev/spec/22/) protocol.
Find the code in the [examples folder](https://github.com/status-im/js-waku/tree/main/examples/cli-chat).
To run the chat app, first ensure you have [Node.js](https://nodejs.org/en/) v14 or above:
```shell
node --version
```
Then, install and run:
```shell
git clone https://github.com/status-im/js-waku/ ; cd js-waku
npm install # Install dependencies for js-waku
npm run build # Build js-waku
cd examples/cli-chat
npm install # Install dependencies for the cli app
npm run start -- --autoDial
```
You can also specify an optional `listenAddr` parameter (.e.g `--listenAddr /ip4/0.0.0.0/tcp/7777/ws`).
This is only useful if you want a remote node to dial to your chat app,
it is not necessary in normal usage when you just connect to the fleet.

View File

@ -1,3 +1,30 @@
# Ethereum Direct Message Web App
See js-waku [README](../../README.md#ethereum-direct-message) for details.
**Demonstrates**:
- Private Messaging
- React/TypeScript
- Waku Light Push
- Signature with Web3
- Asymmetric Encryption
A PoC implementation of [20/ETH-DM](https://rfc.vac.dev/spec/20/).
Ethereum Direct Message, or Eth-DM, is a protocol that allows sending encrypted message to a recipient,
only knowing their Ethereum Address.
This is protocol has been created to demonstrated how encryption and signature could be added to messages
sent over the Waku v2 network.
The `main` branch's HEAD is deployed on GitHub Pages at https://status-im.github.io/js-waku/eth-dm/.
To run a development version locally, do:
```shell
git clone https://github.com/status-im/js-waku/ ; cd js-waku
npm install # Install dependencies for js-waku
npm run build # Build js-waku
cd examples/eth-dm
npm install # Install dependencies for the web app
npm run start # Start development server to serve the web app on http://localhost:3000/js-waku/eth-dm
```

8
examples/examples.md Normal file
View File

@ -0,0 +1,8 @@
## Examples
Here is the list of the code examples and the features they demonstrate:
- [Web Chat App](web-chat): Group chat, React/TypeScript, Relay, Store.
- [CLI Chat App](cli-chat): Group chat, Node JS/TypeScript, Relay, Light Push, Store.
- [Ethereum Direct Message Web App](eth-dm): Private Messaging, React/TypeScript, Light Push, Signature with Web3, Asymmetric Encryption.
- [Minimal JS Web Chat App](min-js-web-chat): Group chat, React/JavaScript, Relay, Protobuf using `protons`.

23
examples/min-js-web-chat/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -0,0 +1,21 @@
# Minimal ReactJS Web Chat App
**Demonstrates**:
- Group chat
- React/JavaScript
- Waku Relay
- Protobuf using `protons`.
A barebone chat app to illustrate the [ReactJS Relay guide](/guides/reactjs-relay.md).
To run a development version locally, do:
```shell
git clone https://github.com/status-im/js-waku/ ; cd js-waku
npm install # Install dependencies for js-waku
npm run build # Build js-waku
cd examples/min-js-web-chat
npm install # Install dependencies for the web app
npm run start # Start development server to serve the web app on http://localhost:3000/
```

38344
examples/min-js-web-chat/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,41 @@
{
"name": "min-js-web-chat",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"js-waku": "../../build/main",
"protons": "^2.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts build",
"react-test": "react-scripts test --env=./test/custom-test-env.js",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@ -0,0 +1,113 @@
import './App.css';
import { getStatusFleetNodes, Waku, WakuMessage } from 'js-waku';
import * as React from 'react';
import protons from 'protons';
const ContentTopic = `/min-js-web-chat/1/chat/proto`;
const proto = protons(`
message SimpleChatMessage {
uint64 timestamp = 1;
string text = 2;
}
`);
function App() {
const [waku, setWaku] = React.useState(undefined);
const [wakuStatus, setWakuStatus] = React.useState('None');
const [sendCounter, setSendCounter] = React.useState(0);
const [messages, setMessages] = React.useState([]);
React.useEffect(() => {
if (!!waku) return;
if (wakuStatus !== 'None') return;
setWakuStatus('Starting');
Waku.create().then((waku) => {
setWaku(waku);
setWakuStatus('Connecting');
bootstrapWaku(waku).then(() => {
setWakuStatus('Ready');
});
});
}, [waku, wakuStatus]);
// Need to keep the same reference around to add and delete from relay observer
const processIncomingMessage = React.useCallback((wakuMessage) => {
if (!wakuMessage.payload) return;
const { text, timestamp } = proto.SimpleChatMessage.decode(
wakuMessage.payload
);
const time = new Date();
time.setTime(timestamp);
const message = { text, timestamp: time };
setMessages((currMessages) => {
return [message].concat(currMessages);
});
}, []);
React.useEffect(() => {
if (!waku) return;
waku.relay.addObserver(processIncomingMessage, [ContentTopic]);
return function cleanUp() {
waku.relay.deleteObserver(processIncomingMessage, [ContentTopic]);
};
}, [waku, wakuStatus, processIncomingMessage]);
const sendMessageOnClick = () => {
if (wakuStatus !== 'Ready') return;
sendMessage(`Here is message #${sendCounter}`, new Date(), waku).then(() =>
console.log('Message sent')
);
setSendCounter(sendCounter + 1);
};
return (
<div className="App">
<header className="App-header">
<p>{wakuStatus}</p>
<button onClick={sendMessageOnClick} disabled={wakuStatus !== 'Ready'}>
Send Message
</button>
<ul>
{messages.map((msg) => {
return (
<li>
<p>
{msg.timestamp.toString()}: {msg.text}
</p>
</li>
);
})}
</ul>
</header>
</div>
);
}
export default App;
async function bootstrapWaku(waku) {
const nodes = await getStatusFleetNodes();
await Promise.all(nodes.map((addr) => waku.dial(addr)));
}
async function sendMessage(message, timestamp, waku) {
const time = timestamp.getTime();
const payload = proto.SimpleChatMessage.encode({
timestamp: time,
text: message,
});
const wakuMessage = await WakuMessage.fromBytes(payload, ContentTopic);
await waku.relay.send(wakuMessage);
}

View File

@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

View File

@ -0,0 +1,12 @@
const Environment = require('jest-environment-jsdom');
module.exports = class CustomTestEnvironment extends Environment {
async setup() {
await super.setup();
if (typeof this.global.TextEncoder === 'undefined') {
const { TextEncoder, TextDecoder } = require('util');
this.global.TextEncoder = TextEncoder;
this.global.TextDecoder = TextDecoder;
}
}
};

View File

@ -1,3 +1,25 @@
# A React Web Chat App powered by js-waku
# Web Chat App
See js-waku [README](../../README.md#web-chat-app-reactjs) for details.
**Demonstrates**:
- Group chat
- React/TypeScript
- Waku Relay
- Waku Store
A ReactJS chat app is provided as a showcase of the library used in the browser.
It implements [Waku v2 Toy Chat](https://rfc.vac.dev/spec/22/) protocol.
A deployed version is available at https://status-im.github.io/js-waku/.
To run a development version locally, do:
```shell
git clone https://github.com/status-im/js-waku/ ; cd js-waku
npm install # Install dependencies for js-waku
npm run build # Build js-waku
cd examples/web-chat
npm install # Install dependencies for the web app
npm run start # Start development server to serve the web app on http://localhost:3000/js-waku
```
Use `/help` to see the available commands.

View File

@ -0,0 +1,26 @@
# How to Choose a Content Topic
A content topic is used for content based filtering.
It allows you to filter out the messages that your dApp processes,
both when receiving live messages (Relay) or retrieving historical messages (Store).
The format for content topics is as follows:
`/{dapp-name}/{version}/{content-topic-name}/{encoding}`
- `dapp-name`: The name of your dApp, it must be unique to avoid conflict with other dApps.
- `version`: We usually start at `1`, useful when introducing breaking changes in your messages.
- `content-topic-name`: The actual content topic name to use for filtering.
If your dApp uses DappConnect for several features,
you should use a content topic per feature.
- `encoding`: The encoding format of the message, we recommend using Protobuf: `proto`.
For example: Your dApp's name is SuperCrypto,
it enables users to receive notifications and send private messages.
You may want to use the following content topics:
- `/supercrypto/1/notification/proto`
- `/supercrypto/1/private-message/proto`
You can learn more about Waku topics in the [23/WAKU2-TOPICS](https://rfc.vac.dev/spec/23/) specs.

5
guides/menu.md Normal file
View File

@ -0,0 +1,5 @@
# Guides
- [Receive and Send Messages Using Waku Relay](relay-receive-send-messages.md)
- [How to Choose a Content Topic](choose-content-topic.md)
- [Receive and Send Messages Using Waku Relay With ReactJS](reactjs-relay.md)

314
guides/reactjs-relay.md Normal file
View File

@ -0,0 +1,314 @@
# Receive and Send Messages Using Waku Relay With ReactJS
It is easy to use DappConnect with ReactJS.
In this guide, we will demonstrate how your ReactJS dApp can use Waku Relay to send and receive messages.
Before starting, you need to choose a _Content Topic_ for your dApp.
Check out the [how to choose a content topic guide](choose-content-topic.md) to learn more about content topics.
For this guide, we are using a single content topic: `/min-js-web-chat/1/chat/proto`.
# Setup
Create a new react app:
```shell
npx create-react-app min-js-web-chat
cd min-js-web-chat
```
Then, install [js-waku](https://npmjs.com/package/js-waku):
```shell
npm install js-waku
```
Start the dev server and open the dApp in your browser:
```shell
npm run start
```
Note: We have noticed some issues with React bundling due to `npm` pulling an old version of babel.
If you are getting an error about the [optional chaining (?.)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining)
character not being valid, try cleaning up and re-installing your dependencies:
```shell
rm -rf node_modules package-lock.json
npm install
```
# Create Waku Instance
In order to interact with the Waku network, you first need a Waku instance.
Go to `App.js` and modify the `App` function:
```js
import { Waku } from 'js-waku';
import * as React from 'react';
function App() {
const [waku, setWaku] = React.useState(undefined);
const [wakuStatus, setWakuStatus] = React.useState('None');
// Start Waku
React.useEffect(() => {
// If Waku is already assigned, the job is done
if (!!waku) return;
// If Waku status not None, it means we are already starting Waku
if (wakuStatus !== 'None') return;
setWakuStatus('Starting');
// Create Waku
Waku.create().then((waku) => {
// Once done, put it in the state
setWaku(waku);
// And update the status
setWakuStatus('Started');
});
}, [waku, wakuStatus]);
return (
<div className="App">
<header className="App-header">
// Display the status on the web page
<p>{wakuStatus}</p>
</header>
</div>
);
}
```
# Connect to Other Peers
The Waku instance needs to connect to other peers to communicate with the network.
First, create `bootstrapWaku` to connect to the Status fleet:
```js
import { getStatusFleetNodes } from 'js-waku';
async function bootstrapWaku(waku) {
// Retrieve node addresses from https://fleets.status.im/
const nodes = await getStatusFleetNodes();
// Connect to the nodes
await Promise.all(nodes.map((addr) => waku.dial(addr)));
}
```
Then, bootstrap after Waku is created in the previous `useEffect` block:
```js
React.useEffect(() => {
if (!!waku) return;
if (wakuStatus !== 'None') return;
setWakuStatus('Starting');
Waku.create().then((waku) => {
setWaku(waku);
setWakuStatus('Connecting');
bootstrapWaku(waku).then(() => {
setWakuStatus('Ready');
});
});
}, [waku, wakuStatus]);
```
DappConnect will provide more discovery and bootstrap methods over time, or you can make your own.
# Define Message Format
To define the Protobuf message format,
use [protons](https://www.npmjs.com/package/protons)
```shell
npm install protons
```
Define `SimpleChatMessage` with two fields: `timestamp` and `text`.
```js
import protons from 'protons';
const proto = protons(`
message SimpleChatMessage {
uint64 timestamp = 1;
string text = 2;
}
`);
```
# Send Messages
Create a function that takes the Waku instance and a message to send:
```js
import { WakuMessage } from 'js-waku';
const ContentTopic = `/min-js-web-chat/1/chat/proto`;
async function sendMessage(message, timestamp, waku) {
const time = timestamp.getTime();
// Encode to protobuf
const payload = proto.SimpleChatMessage.encode({
timestamp: time,
text: message,
});
// Wrap in a Waku Message
const wakuMessage = await WakuMessage.fromBytes(payload, ContentTopic);
// Send over Waku Relay
await waku.relay.send(wakuMessage);
}
```
Then, add a button to the `App` function:
```js
function App() {
const [waku, setWaku] = React.useState(undefined);
const [wakuStatus, setWakuStatus] = React.useState('None');
// Using a counter just for the messages to be different
const [sendCounter, setSendCounter] = React.useState(0);
React.useEffect(() => {
// ... creates Waku
}, [waku, wakuStatus]);
const sendMessageOnClick = () => {
// Check Waku is started and connected first.
if (wakuStatus !== 'Ready') return;
sendMessage(`Here is message #${sendCounter}`, waku, new Date()).then(() =>
console.log('Message sent')
);
// For demonstration purposes.
setSendCounter(sendCounter + 1);
};
return (
<div className="App">
<header className="App-header">
<p>{wakuStatus}</p>
<button onClick={sendMessageOnClick} disabled={wakuStatus !== 'Ready'}> // Grey the button is Waku is not yet ready.
Send Message
</button>
</header>
</div>
);
}
```
# Receive Messages
To process incoming messages, you need to register an observer on Waku Relay.
First, you need to define the observer function.
You will need to remove the observer when the component unmount.
Hence, you need the reference to the function to remain the same.
For that, use `React.useCallback`:
```js
const processIncomingMessage = React.useCallback((wakuMessage) => {
// Empty message?
if (!wakuMessage.payload) return;
// Decode the protobuf payload
const { timestamp, text } = proto.SimpleChatMessage.decode(
wakuMessage.payload
);
const time = new Date();
time.setTime(timestamp);
// For now, just log new messages on the console
console.log(`message received at ${time.toString()}: ${text}`);
}, []);
```
Then, add this observer to Waku Relay.
Do not forget to delete the observer is the component is being unmounted:
```js
React.useEffect(() => {
if (!waku) return;
// Pass the content topic to only process messages related to your dApp
waku.relay.addObserver(processIncomingMessage, [ContentTopic]);
// `cleanUp` is called when the component is unmounted, see ReactJS doc.
return function cleanUp() {
waku.relay.deleteObserver(processIncomingMessage, [ContentTopic]);
};
}, [waku, wakuStatus, processIncomingMessage]);
```
# Display Messages
The Waku work is now done.
Your dApp is able to send and receive messages using Waku.
For the sake of completeness, let's display received messages on the page.
First, add incoming messages to the state of the `App` component:
```js
function App() {
//...
const [messages, setMessages] = React.useState([]);
const processIncomingMessage = React.useCallback((wakuMessage) => {
if (!wakuMessage.payload) return;
const { text, timestamp } = proto.SimpleChatMessage.decode(
wakuMessage.payload
);
const time = new Date();
time.setTime(timestamp);
const message = { text, timestamp: time };
setMessages((currMessages) => {
return [message].concat(currMessages);
});
}, []);
// ...
}
```
Then, render the messages:
```js
function App() {
// ...
return (
<div className="App">
<header className="App-header">
<p>{wakuStatus}</p>
<button onClick={sendMessageOnClick} disabled={wakuStatus !== 'Ready'}>
Send Message
</button>
<ul>
{messages.map((msg) => {
return (
<li>
<p>
{msg.timestamp.toString()}: {msg.text}
</p>
</li>
);
})}
</ul>
</header>
</div>
);
}
```
And Voilà! You should now be able to send and receive messages.
Try out by opening the app from different browsers.
You can see the complete code in the [Minimal JS Web Chat App](/examples/min-js-web-chat).

View File

@ -0,0 +1,212 @@
# Receive and Send Messages Using Waku Relay
Waku Relay is a gossip protocol that enables you to send and receive messages.
You can find Waku Relay's specifications on [Vac RFC](https://rfc.vac.dev/spec/11/).
Before starting, you need to choose a _Content Topic_ for your dApp.
Check out the [how to choose a content topic guide](choose-content-topic.md) to learn more about content topics.
For this guide, we are using a single content topic: `/relay-guide/1/chat/proto`.
# Installation
You can install [js-waku](https://npmjs.com/package/js-waku) using your favorite package manager:
```shell
npm install js-waku
```
# Create Waku Instance
In order to interact with the Waku network, you first need a Waku instance:
```js
import { Waku } from 'js-waku';
const wakuNode = await Waku.create();
```
# Connect to Other Peers
The Waku instance needs to connect to other peers to communicate with the network.
You are free to choose any method to bootstrap and DappConnect will ship with new methods in the future.
For now, the easiest way is to connect to Status' Waku fleet:
```js
import { getStatusFleetNodes } from 'js-waku';
const nodes = await getStatusFleetNodes();
await Promise.all(nodes.map((addr) => waku.dial(addr)));
```
# Receive messages
To watch messages for your app, you need to register an observer on relay for your app's content topic:
```js
const processIncomingMessage = (wakuMessage) => {
console.log(`Message Received: ${wakuMessage.payloadAsUtf8}`);
};
waku.relay.addObserver(processIncomingMessage, ['/relay-guide/1/chat/proto']);
```
# Send Messages
You are now ready to send messages.
Let's start by sending simple strings as messages.
To send a message, you need to wrap the message in a `WakuMessage`.
When using a basic string payload, you can use the `WakuMessage.fromUtf8String` helper:
```js
import { WakuMessage } from 'js-waku';
const wakuMessage = await WakuMessage.fromUtf8String('Here is a message', `/relay-guide/1/chat/proto`);
```
Then, use the `relay` module to send the message to our peers,
the message will then be relayed to the rest of the network thanks to Waku Relay:
```js
await waku.relay.send(wakuMessage);
```
# Use Protobuf
Sending strings as messages in unlikely to cover your dApps needs.
To include structured objects in Waku Messages,
we recommend you use [protobuf](https://developers.google.com/protocol-buffers/).
First, let's define a data structure.
For this guide, we will use a simple chat message that contains a timestamp and text:
```js
{
timestamp: Date;
text: string;
}
```
To encode and decode protobuf payloads, you can use the [protons](https://www.npmjs.com/package/protons) package.
## Install Protobuf Library
First, install protons:
```shell
npm install protons
```
## Protobuf Definition
Then define the simple chat message:
```js
import protons from 'protons';
const proto = protons(`
message SimpleChatMessage {
float timestamp = 1;
string text = 2;
}
`);
```
You can learn about protobuf message definitions here:
[Protocol Buffers Language Guide](https://developers.google.com/protocol-buffers/docs/proto).
## Encode Messages
Instead of wrapping an utf-8 string in a Waku Message,
you are going to wrap a protobuf payload.
First, encode the object:
```js
const payload = proto.SimpleChatMessage.encode({
timestamp: Date.now(),
text: 'Here is a message'
});
```
Then, wrap it in a Waku Message:
```js
const wakuMessage = await WakuMessage.fromBytes(payload, ContentTopic);
```
Now, you can send the message over Waku Relay the same way than before:
```js
await waku.relay.send(wakuMessage);
```
## Decode Messages
To decode the messages received over Waku Relay,
you need to extract the protobuf payload and decode it using `protons`.
```js
const processIncomingMessage = (wakuMessage) => {
// No need to attempt to decode a message if the payload is absent
if (!wakuMessage.payload) return;
const { timestamp, text } = proto.SimpleChatMessage.decode(
wakuMessage.payload
);
console.log(`Message Received: ${text}, sent at ${timestamp.toString()}`);
};
```
Like before, add this callback as an observer to Waku Relay:
```js
waku.relay.addObserver(processIncomingMessage, ['/relay-guide/1/chat/proto']);
```
# Conclusion
That is it! Now, you know how to send and receive messages over Waku using the Waku Relay protocol.
Feel free to check out other [guides](menu.md) or [examples](/examples/examples.md).
Here is the final code:
```js
import { getStatusFleetNodes, Waku, WakuMessage } from 'js-waku';
import protons from 'protons';
const proto = protons(`
message SimpleChatMessage {
float timestamp = 1;
string text = 2;
}
`);
const wakuNode = await Waku.create();
const nodes = await getStatusFleetNodes();
await Promise.all(nodes.map((addr) => waku.dial(addr)));
const processIncomingMessage = (wakuMessage) => {
// No need to attempt to decode a message if the payload is absent
if (!wakuMessage.payload) return;
const { timestamp, text } = proto.SimpleChatMessage.decode(
wakuMessage.payload
);
console.log(`Message Received: ${text}, sent at ${timestamp.toString()}`);
};
waku.relay.addObserver(processIncomingMessage, ['/relay-guide/1/chat/proto']);
const payload = proto.SimpleChatMessage.encode({
timestamp: Date.now(),
text: 'Here is a message'
});
const wakuMessage = await WakuMessage.fromBytes(payload, ContentTopic);
await waku.relay.send(wakuMessage);
```