mirror of
https://github.com/status-im/js-waku-examples.git
synced 2025-02-26 21:50:46 +00:00
add Waku and Form abstractions as well as scheme
This commit is contained in:
parent
c6e560af0b
commit
b923d852a4
15
examples/free-forms/scheme.txt
Normal file
15
examples/free-forms/scheme.txt
Normal file
@ -0,0 +1,15 @@
|
||||
App root content topic
|
||||
/free-form/0.0.1/{content-topic-for-particular-use}/proto
|
||||
|
||||
|
||||
1. User logs in with his wallet
|
||||
1.1 User's preferences are fetched from following content topic
|
||||
id = waller-or-uuid-or-pubkey
|
||||
/fee-form/{version}/user:{id}/proto
|
||||
2. User creates new form
|
||||
2.1 Form topic is set up
|
||||
id = uuid
|
||||
/free-form/0/form:{id}/proto
|
||||
2.2 User sets form scheme, version, signature
|
||||
2.3 Scheme is sent
|
||||
3.
|
BIN
examples/free-forms/tmp/favicon.ico
Normal file
BIN
examples/free-forms/tmp/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
examples/free-forms/tmp/favicon.png
Normal file
BIN
examples/free-forms/tmp/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
149
examples/free-forms/tmp/index.html
Normal file
149
examples/free-forms/tmp/index.html
Normal file
@ -0,0 +1,149 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
||||
<title>Free Forms</title>
|
||||
<link rel="apple-touch-icon" href="./favicon.png" />
|
||||
<link rel="manifest" href="./manifest.json" />
|
||||
<link rel="icon" href="./favicon.ico" />
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
word-wrap: break-word;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
min-width: 300px;
|
||||
max-width: 800px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: space-between;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
h3:last-of-type {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h2 span,
|
||||
h3 span {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.progress {
|
||||
color: #9ea13b;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #3ba183;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #c84740;
|
||||
}
|
||||
|
||||
button.progress {
|
||||
color: white;
|
||||
background-color: #9ea13b;
|
||||
}
|
||||
|
||||
button.success {
|
||||
color: white;
|
||||
background-color: #3ba183;
|
||||
}
|
||||
|
||||
button.error {
|
||||
color: white;
|
||||
background-color: #c84740;
|
||||
}
|
||||
|
||||
.pairingInfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pairingInfo input {
|
||||
display: block;
|
||||
min-width: 250px;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.5rem;
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pairingInfo button {
|
||||
flex-grow: 1;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.pairingInfo button + button {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.chatArea {
|
||||
}
|
||||
|
||||
.chatArea ul {
|
||||
margin-bottom: 30px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.chatArea ul li + li {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.chatArea div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.chatArea div > * {
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.5rem;
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script src="./index.js"></script>
|
||||
</body>
|
||||
</html>
|
25
examples/free-forms/tmp/index.js
Normal file
25
examples/free-forms/tmp/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { createDecoder, createEncoder } from "@waku/sdk";
|
||||
import * as utils from "@waku/utils/bytes";
|
||||
import protobuf from "protobufjs";
|
||||
import { Waku } from "./waku";
|
||||
|
||||
run()
|
||||
.then(() => {
|
||||
console.log("App is running...");
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error("Failed to run app: ", e);
|
||||
});
|
||||
|
||||
async function run() {
|
||||
const waku = await Waku.create();
|
||||
window.waku = waku;
|
||||
|
||||
window.createForm = (id) => {
|
||||
return waku.createForm({ id, scheme: "" });
|
||||
};
|
||||
|
||||
window.fetchForm = (id) => {
|
||||
return waku.fetchForm(id);
|
||||
};
|
||||
}
|
19
examples/free-forms/tmp/manifest.json
Normal file
19
examples/free-forms/tmp/manifest.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Waku Noise",
|
||||
"description": "Example showing Waku noise capabilities.",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "favicon.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
}
|
||||
],
|
||||
"display": "standalone",
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff"
|
||||
}
|
9907
examples/free-forms/tmp/package-lock.json
generated
Normal file
9907
examples/free-forms/tmp/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
examples/free-forms/tmp/package.json
Normal file
23
examples/free-forms/tmp/package.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "free-forms",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"start": "webpack-dev-server"
|
||||
},
|
||||
"dependencies": {
|
||||
"@waku/dns-discovery": "^0.0.17",
|
||||
"@waku/sdk": "0.0.19",
|
||||
"@waku/utils": "0.0.11",
|
||||
"protobufjs": "^7.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-dev-server": "^4.11.1"
|
||||
}
|
||||
}
|
140
examples/free-forms/tmp/waku.js
Normal file
140
examples/free-forms/tmp/waku.js
Normal file
@ -0,0 +1,140 @@
|
||||
import {
|
||||
createDecoder,
|
||||
createEncoder,
|
||||
createLightNode,
|
||||
waitForRemotePeer,
|
||||
} from "@waku/sdk";
|
||||
import { enrTree, wakuDnsDiscovery } from "@waku/dns-discovery";
|
||||
import * as utils from "@waku/utils/bytes";
|
||||
|
||||
const VERSION = `0.0.00001`;
|
||||
|
||||
export class Waku {
|
||||
constructor(node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
static async create() {
|
||||
const node = await createLightNode({
|
||||
defaultBootstrap: true,
|
||||
libp2p: {
|
||||
peerDiscovery: [
|
||||
wakuDnsDiscovery([enrTree["PROD"]], {
|
||||
lightPush: 1,
|
||||
store: 1,
|
||||
filter: 1,
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await waitForRemotePeer(node);
|
||||
|
||||
return new Waku(node);
|
||||
}
|
||||
|
||||
fetchForm(id) {
|
||||
return Form.fetch(this.node, id);
|
||||
}
|
||||
|
||||
createForm({ id, scheme }) {
|
||||
return Form.create(this.node, { id, scheme });
|
||||
}
|
||||
}
|
||||
|
||||
class Form {
|
||||
constructor(id, waku) {
|
||||
this.waku = waku;
|
||||
this.history = [];
|
||||
this.contentTopic = `/free-form/${VERSION}/definition:${id}/proto`;
|
||||
this.decoder = createDecoder(this.contentTopic);
|
||||
this.encoder = createEncoder({ contentTopic: this.contentTopic });
|
||||
}
|
||||
|
||||
// Initiates new form
|
||||
static async create(waku, { id, scheme }) {
|
||||
const form = new Form(id, waku);
|
||||
await form.createNew({ scheme });
|
||||
return form;
|
||||
}
|
||||
|
||||
// Fetches history of an existing form
|
||||
static async fetch(waku, id) {
|
||||
// TODO: throw on attempt to fetch non existing form
|
||||
const form = new Form(id, waku);
|
||||
await form.fetchState(id);
|
||||
return form;
|
||||
}
|
||||
|
||||
async createNew({ scheme }) {
|
||||
// TODO: throw on attempt to create existing form
|
||||
const command = {
|
||||
type: "CREATE",
|
||||
nonce: 0,
|
||||
signature: "",
|
||||
scheme,
|
||||
};
|
||||
const payload = this.toPayload(command);
|
||||
await this.waku.lightPush.send(this.encoder, { payload });
|
||||
this.history.push(command);
|
||||
console.log("DEBUG", this.history);
|
||||
}
|
||||
|
||||
async fetchState() {
|
||||
let historyPromises = [];
|
||||
|
||||
for await (const promises of this.waku.store.queryGenerator([
|
||||
this.decoder,
|
||||
])) {
|
||||
historyPromises = [...historyPromises, ...promises];
|
||||
}
|
||||
|
||||
if (!historyPromises.length) {
|
||||
console.error("DEBUG", "No form info fetched from store");
|
||||
}
|
||||
|
||||
const commandPackets = await Promise.all(historyPromises);
|
||||
const commands = commandPackets
|
||||
.map(({ payload }) => {
|
||||
try {
|
||||
// validate scheme of command record
|
||||
// check signature
|
||||
const command = this.toCommand(payload);
|
||||
return command;
|
||||
} catch (e) {
|
||||
console.error("DEBUG", "failed to parse update command.");
|
||||
return;
|
||||
}
|
||||
})
|
||||
.filter((c) => !!c);
|
||||
|
||||
this.history = [...this.history, ...commands];
|
||||
console.log("DEBUG", this.history);
|
||||
}
|
||||
|
||||
async patchState({ scheme }) {
|
||||
if (!this.history.length) {
|
||||
throw Error("no history");
|
||||
}
|
||||
|
||||
const command = {
|
||||
type: "PATCH",
|
||||
nonce: this.history[this.history.length - 1].nonce + 1,
|
||||
signature: "",
|
||||
scheme,
|
||||
};
|
||||
const payload = this.toPayload(command);
|
||||
|
||||
await this.waku.lightPush.send(this.encoder, { payload });
|
||||
this.history.push(command);
|
||||
console.log("DEBUG patch", this.history);
|
||||
}
|
||||
|
||||
toPayload(command) {
|
||||
return utils.utf8ToBytes(JSON.stringify(command));
|
||||
}
|
||||
|
||||
toCommand(payload) {
|
||||
return JSON.parse(utils.bytesToUtf8(payload));
|
||||
}
|
||||
}
|
19
examples/free-forms/tmp/webpack.config.js
Normal file
19
examples/free-forms/tmp/webpack.config.js
Normal file
@ -0,0 +1,19 @@
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
entry: "./index.js",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "build"),
|
||||
filename: "index.js",
|
||||
},
|
||||
experiments: {
|
||||
asyncWebAssembly: true,
|
||||
},
|
||||
mode: "development",
|
||||
plugins: [
|
||||
new CopyWebpackPlugin({
|
||||
patterns: ["index.html", "favicon.ico", "favicon.png", "manifest.json"],
|
||||
}),
|
||||
],
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user