mirror of
https://github.com/status-im/js-waku-examples.git
synced 2025-01-11 22:44:32 +00:00
chore: remove web-chat-dev example (#217)
This commit is contained in:
parent
2cf9160aad
commit
2820bd2579
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -18,7 +18,6 @@ jobs:
|
|||||||
relay-reactjs-chat,
|
relay-reactjs-chat,
|
||||||
store-reactjs-chat,
|
store-reactjs-chat,
|
||||||
web-chat,
|
web-chat,
|
||||||
web-chat-dev,
|
|
||||||
noise-js,
|
noise-js,
|
||||||
noise-rtc,
|
noise-rtc,
|
||||||
]
|
]
|
||||||
|
1
ci/Jenkinsfile
vendored
1
ci/Jenkinsfile
vendored
@ -39,7 +39,6 @@ pipeline {
|
|||||||
stage('relay-reactjs-chat') { steps { script { buildExample() } } }
|
stage('relay-reactjs-chat') { steps { script { buildExample() } } }
|
||||||
stage('store-reactjs-chat') { steps { script { buildExample() } } }
|
stage('store-reactjs-chat') { steps { script { buildExample() } } }
|
||||||
stage('web-chat') { steps { script { buildExample() } } }
|
stage('web-chat') { steps { script { buildExample() } } }
|
||||||
stage('web-chat-dev') { steps { script { buildExample() } } }
|
|
||||||
stage('noise-js') { steps { script { buildExample() } } }
|
stage('noise-js') { steps { script { buildExample() } } }
|
||||||
stage('noise-rtc') { steps { script { buildExample() } } }
|
stage('noise-rtc') { steps { script { buildExample() } } }
|
||||||
}
|
}
|
||||||
|
@ -1,132 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2",
|
|
||||||
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/master/cspell.schema.json",
|
|
||||||
"language": "en",
|
|
||||||
"words": [
|
|
||||||
"AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM",
|
|
||||||
"asym",
|
|
||||||
"backoff",
|
|
||||||
"backoffs",
|
|
||||||
"bitjson",
|
|
||||||
"bitauth",
|
|
||||||
"bufbuild",
|
|
||||||
"chainsafe",
|
|
||||||
"cimg",
|
|
||||||
"ciphertext",
|
|
||||||
"circleci",
|
|
||||||
"codecov",
|
|
||||||
"commitlint",
|
|
||||||
"ethdenver",
|
|
||||||
"dependabot",
|
|
||||||
"dingpu",
|
|
||||||
"Dlazy",
|
|
||||||
"dnsaddr",
|
|
||||||
"Dout",
|
|
||||||
"Dscore",
|
|
||||||
"ecies",
|
|
||||||
"editorconfig",
|
|
||||||
"enr",
|
|
||||||
"enrs",
|
|
||||||
"enrtree",
|
|
||||||
"ephem",
|
|
||||||
"esnext",
|
|
||||||
"ethersproject",
|
|
||||||
"execa",
|
|
||||||
"exponentiate",
|
|
||||||
"fanout",
|
|
||||||
"floodsub",
|
|
||||||
"fontsource",
|
|
||||||
"globby",
|
|
||||||
"gossipsub",
|
|
||||||
"huilong",
|
|
||||||
"iasked",
|
|
||||||
"ihave",
|
|
||||||
"ihaves",
|
|
||||||
"ineed",
|
|
||||||
"ipfs",
|
|
||||||
"iwant",
|
|
||||||
"jdev",
|
|
||||||
"jswaku",
|
|
||||||
"keccak",
|
|
||||||
"lastpub",
|
|
||||||
"libauth",
|
|
||||||
"libp",
|
|
||||||
"lightpush",
|
|
||||||
"livechat",
|
|
||||||
"mkdir",
|
|
||||||
"multiaddr",
|
|
||||||
"multiaddresses",
|
|
||||||
"multiaddrs",
|
|
||||||
"multicodec",
|
|
||||||
"multicodecs",
|
|
||||||
"multiformats",
|
|
||||||
"mplex",
|
|
||||||
"multihashes",
|
|
||||||
"muxed",
|
|
||||||
"muxer",
|
|
||||||
"muxers",
|
|
||||||
"mvps",
|
|
||||||
"nodekey",
|
|
||||||
"nwaku",
|
|
||||||
"opendns",
|
|
||||||
"peerhave",
|
|
||||||
"portfinder",
|
|
||||||
"prettierignore",
|
|
||||||
"proto",
|
|
||||||
"protobuf",
|
|
||||||
"protoc",
|
|
||||||
"reactjs",
|
|
||||||
"recid",
|
|
||||||
"rlnrelay",
|
|
||||||
"roadmap",
|
|
||||||
"sandboxed",
|
|
||||||
"scanf",
|
|
||||||
"secio",
|
|
||||||
"seckey",
|
|
||||||
"secp",
|
|
||||||
"sscanf",
|
|
||||||
"staticnode",
|
|
||||||
"statusim",
|
|
||||||
"submodule",
|
|
||||||
"submodules",
|
|
||||||
"supercrypto",
|
|
||||||
"transpiled",
|
|
||||||
"typedoc",
|
|
||||||
"unencrypted",
|
|
||||||
"unmarshal",
|
|
||||||
"unmount",
|
|
||||||
"unmounts",
|
|
||||||
"untracked",
|
|
||||||
"upgrader",
|
|
||||||
"vacp",
|
|
||||||
"varint",
|
|
||||||
"waku",
|
|
||||||
"wakuconnect",
|
|
||||||
"wakuv",
|
|
||||||
"wakunode",
|
|
||||||
"webfonts",
|
|
||||||
"websockets",
|
|
||||||
"wifi",
|
|
||||||
"xsalsa20",
|
|
||||||
"Alives"
|
|
||||||
],
|
|
||||||
"flagWords": [],
|
|
||||||
"ignorePaths": [
|
|
||||||
"package.json",
|
|
||||||
"package-lock.json",
|
|
||||||
"yarn.lock",
|
|
||||||
"tsconfig.json",
|
|
||||||
"node_modules/**",
|
|
||||||
"build",
|
|
||||||
"gen",
|
|
||||||
"proto",
|
|
||||||
"*.spec.ts"
|
|
||||||
],
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "import",
|
|
||||||
"pattern": "/import .*/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ignoreRegExpList": ["import"]
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
# Remove ReactJS warning about webpack
|
|
||||||
# because this is not a monorepo, ReactJS projects are examples
|
|
||||||
SKIP_PREFLIGHT_CHECK=true
|
|
23
examples/web-chat-dev/.gitignore
vendored
23
examples/web-chat-dev/.gitignore
vendored
@ -1,23 +0,0 @@
|
|||||||
# 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*
|
|
@ -1,3 +0,0 @@
|
|||||||
# package.json is formatted by package managers, so we ignore it here
|
|
||||||
package.json
|
|
||||||
gen
|
|
@ -1,25 +0,0 @@
|
|||||||
# Web Chat App
|
|
||||||
|
|
||||||
**Demonstrates**:
|
|
||||||
|
|
||||||
- Group chat
|
|
||||||
- React/TypeScript
|
|
||||||
- Waku Filter
|
|
||||||
- Waku Light Push
|
|
||||||
- Waku Store
|
|
||||||
- Protobuf using `protons`
|
|
||||||
|
|
||||||
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://examples.waku.org/web-chat/.
|
|
||||||
|
|
||||||
To run a development version locally, do:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
git clone https://github.com/waku-org/js-waku-examples
|
|
||||||
cd web-chat
|
|
||||||
npm install
|
|
||||||
npm run start
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `/help` to see the available commands.
|
|
@ -1,6 +0,0 @@
|
|||||||
version: v1beta1
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
- name: ts_proto
|
|
||||||
out: ./src/proto
|
|
||||||
opt: grpc_js,esModuleInterop=true
|
|
@ -1,5 +0,0 @@
|
|||||||
version: v1beta1
|
|
||||||
|
|
||||||
build:
|
|
||||||
roots:
|
|
||||||
- ./src/proto
|
|
22525
examples/web-chat-dev/package-lock.json
generated
22525
examples/web-chat-dev/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,74 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "web-chat-dev",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"homepage": "/web-chat-dev",
|
|
||||||
"dependencies": {
|
|
||||||
"@libp2p/bootstrap": "^5.0.0",
|
|
||||||
"@livechat/ui-kit": "^0.5.0-24",
|
|
||||||
"@multiformats/multiaddr": "11.0.7",
|
|
||||||
"@waku/react": "0.0.1-a",
|
|
||||||
"@waku/byte-utils": "^0.0.2",
|
|
||||||
"@waku/core": "^0.0.10",
|
|
||||||
"@waku/create": "^0.0.4",
|
|
||||||
"@waku/dns-discovery": "0.0.5",
|
|
||||||
"@waku/interfaces": "^0.0.7",
|
|
||||||
"@waku/peer-exchange": "^0.0.3",
|
|
||||||
"process": "^0.11.10",
|
|
||||||
"protons-runtime": "^3.1.0",
|
|
||||||
"react": "^17.0.2",
|
|
||||||
"react-dom": "^17.0.2",
|
|
||||||
"server-name-generator": "^1.0.5",
|
|
||||||
"uint8arraylist": "^2.3.3"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/jest": "^27.5.2",
|
|
||||||
"@types/node": "^17.0.45",
|
|
||||||
"@types/react": "^18.0.25",
|
|
||||||
"@types/react-dom": "^18.0.9",
|
|
||||||
"cspell": "^6.14.3",
|
|
||||||
"gh-pages": "^4.0.0",
|
|
||||||
"npm-run-all": "^4.1.5",
|
|
||||||
"prettier": "^2.7.1",
|
|
||||||
"protons": "^5.1.0",
|
|
||||||
"react-scripts": "5.0.1",
|
|
||||||
"typescript": "^4.9.3",
|
|
||||||
"url": "^0.11.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"start": "GENERATE_SOURCEMAP=false PORT=3003 react-scripts start",
|
|
||||||
"build": "react-scripts build",
|
|
||||||
"test:unit": "exit 0",
|
|
||||||
"fix": "run-s fix:*",
|
|
||||||
"test": "run-s build test:*",
|
|
||||||
"test:lint": "eslint src --ext .ts --ext .tsx",
|
|
||||||
"test:prettier": "prettier \"src/**/*.{ts,tsx}\" \"./*.json\" --list-different",
|
|
||||||
"test:spelling": "cspell \"{README.md,.github/*.md,src/**/*.{ts,tsx},public/**/*.html}\" -c ./.cspell.json",
|
|
||||||
"fix:prettier": "prettier \"src/**/*.{ts,tsx}\" \"./*.json\" --write",
|
|
||||||
"fix:lint": "eslint src --ext .ts --ext .tsx --fix",
|
|
||||||
"proto": "protons src/proto/*.proto",
|
|
||||||
"js-waku:build": "cd ../; npm run build",
|
|
||||||
"predeploy": "run-s js-waku:build build",
|
|
||||||
"deploy": "gh-pages -d build"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"extends": [
|
|
||||||
"react-app",
|
|
||||||
"react-app/jest"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"browserslist": {
|
|
||||||
"production": [
|
|
||||||
">0.2%",
|
|
||||||
"not ie <= 99",
|
|
||||||
"not android <= 4.4.4",
|
|
||||||
"not dead",
|
|
||||||
"not op_mini all"
|
|
||||||
],
|
|
||||||
"development": [
|
|
||||||
"last 1 chrome version",
|
|
||||||
"last 1 firefox version",
|
|
||||||
"last 1 safari version"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.1 KiB |
@ -1,44 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
|
|
||||||
/>
|
|
||||||
<meta name="description" content="Chat app powered by js-waku" />
|
|
||||||
<!--
|
|
||||||
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" />
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/favicon.png" />
|
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
||||||
<!--
|
|
||||||
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>Waku v2 chat 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>
|
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "Waku v2 Chat",
|
|
||||||
"name": "Chat app powered by js-waku",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "favicon.ico",
|
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "favicon.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
# https://www.robotstxt.org/robotstxt.html
|
|
||||||
User-agent: *
|
|
||||||
Disallow:
|
|
@ -1,38 +0,0 @@
|
|||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
import "./App.css";
|
|
||||||
import Room from "./Room";
|
|
||||||
import { PageDirection, LightNode } from "@waku/interfaces";
|
|
||||||
|
|
||||||
import { useWaku, useContentPair } from "@waku/react";
|
|
||||||
|
|
||||||
import { useMessages, usePersistentNick } from "./hooks";
|
|
||||||
|
|
||||||
const startTime = new Date();
|
|
||||||
// Only retrieve a week of history
|
|
||||||
startTime.setTime(Date.now() - 1000 * 60 * 60 * 24 * 7);
|
|
||||||
const endTime = new Date();
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
const { node } = useWaku<LightNode>();
|
|
||||||
const { decoder } = useContentPair();
|
|
||||||
const [messages] = useMessages({
|
|
||||||
node,
|
|
||||||
decoder,
|
|
||||||
options: {
|
|
||||||
pageSize: 5,
|
|
||||||
pageDirection: PageDirection.FORWARD,
|
|
||||||
timeFilter: {
|
|
||||||
startTime,
|
|
||||||
endTime,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const [nick] = usePersistentNick();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="chat-app"
|
|
||||||
style={{ height: "100vh", width: "100vw", overflow: "hidden" }}
|
|
||||||
>
|
|
||||||
<Room nick={nick} messages={messages} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
import { useEffect, useRef } from "react";
|
|
||||||
import {
|
|
||||||
Message as LiveMessage,
|
|
||||||
MessageText,
|
|
||||||
MessageList,
|
|
||||||
} from "@livechat/ui-kit";
|
|
||||||
import { Message } from "./Message";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
messages: Message[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ChatList(props: Props) {
|
|
||||||
const renderedMessages = props.messages.map((message) => (
|
|
||||||
<LiveMessage
|
|
||||||
key={
|
|
||||||
message.nick +
|
|
||||||
message.payloadAsUtf8 +
|
|
||||||
message.timestamp.valueOf() +
|
|
||||||
message.sentTimestamp?.valueOf()
|
|
||||||
}
|
|
||||||
authorName={message.nick}
|
|
||||||
date={formatDisplayDate(message)}
|
|
||||||
>
|
|
||||||
<MessageText>{message.payloadAsUtf8}</MessageText>
|
|
||||||
</LiveMessage>
|
|
||||||
));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MessageList active containScrollInSubtree>
|
|
||||||
{renderedMessages}
|
|
||||||
<AlwaysScrollToBottom messages={props.messages} />
|
|
||||||
</MessageList>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDisplayDate(message: Message): string {
|
|
||||||
return message.timestamp.toLocaleString([], {
|
|
||||||
month: "short",
|
|
||||||
day: "numeric",
|
|
||||||
hour: "numeric",
|
|
||||||
minute: "2-digit",
|
|
||||||
hour12: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const AlwaysScrollToBottom = (props: { messages: Message[] }) => {
|
|
||||||
const elementRef = useRef<HTMLDivElement>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// @ts-ignore
|
|
||||||
elementRef.current.scrollIntoView();
|
|
||||||
}, [props.messages]);
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
return <div ref={elementRef} />;
|
|
||||||
};
|
|
@ -1,44 +0,0 @@
|
|||||||
import { IDecodedMessage } from "@waku/interfaces";
|
|
||||||
import { ChatMessage } from "./chat_message";
|
|
||||||
|
|
||||||
export class Message {
|
|
||||||
public chatMessage: ChatMessage;
|
|
||||||
// WakuMessage timestamp
|
|
||||||
public sentTimestamp: Date | undefined;
|
|
||||||
|
|
||||||
constructor(chatMessage: ChatMessage, sentTimestamp: Date | undefined) {
|
|
||||||
this.chatMessage = chatMessage;
|
|
||||||
this.sentTimestamp = sentTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromWakuMessage(wakuMsg: IDecodedMessage): Message | undefined {
|
|
||||||
if (wakuMsg.payload) {
|
|
||||||
try {
|
|
||||||
const chatMsg = ChatMessage.decode(wakuMsg.payload);
|
|
||||||
if (chatMsg) {
|
|
||||||
return new Message(chatMsg, wakuMsg.timestamp);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Failed to decode chat message", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromUtf8String(nick: string, text: string): Message {
|
|
||||||
const now = new Date();
|
|
||||||
return new Message(ChatMessage.fromUtf8String(now, nick, text), now);
|
|
||||||
}
|
|
||||||
|
|
||||||
get nick() {
|
|
||||||
return this.chatMessage.nick;
|
|
||||||
}
|
|
||||||
|
|
||||||
get timestamp() {
|
|
||||||
return this.chatMessage.timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
get payloadAsUtf8() {
|
|
||||||
return this.chatMessage.payloadAsUtf8;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
import { ChangeEvent, KeyboardEvent, useEffect, useState } from "react";
|
|
||||||
import { useWaku } from "@waku/react";
|
|
||||||
import { LightNode } from "@waku/interfaces";
|
|
||||||
import {
|
|
||||||
TextInput,
|
|
||||||
TextComposer,
|
|
||||||
Row,
|
|
||||||
Fill,
|
|
||||||
Fit,
|
|
||||||
SendButton,
|
|
||||||
} from "@livechat/ui-kit";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
hasLightPushPeers: boolean;
|
|
||||||
sendMessage: ((msg: string) => Promise<void>) | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function MessageInput(props: Props) {
|
|
||||||
const { hasLightPushPeers } = props;
|
|
||||||
const { node } = useWaku<LightNode>();
|
|
||||||
|
|
||||||
const [inputText, setInputText] = useState<string>("");
|
|
||||||
const [isActive, setActiveButton] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const sendMessage = async () => {
|
|
||||||
if (props.sendMessage) {
|
|
||||||
await props.sendMessage(inputText);
|
|
||||||
setInputText("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setInputText(event.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onKeyDown = async (event: KeyboardEvent<HTMLInputElement>) => {
|
|
||||||
if (
|
|
||||||
event.key === "Enter" &&
|
|
||||||
!event.altKey &&
|
|
||||||
!event.ctrlKey &&
|
|
||||||
!event.shiftKey
|
|
||||||
) {
|
|
||||||
await sendMessage();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Enable the button if there are peers available or the user is sending a command
|
|
||||||
useEffect(() => {
|
|
||||||
if (inputText.startsWith("/") || hasLightPushPeers) {
|
|
||||||
setActiveButton(true);
|
|
||||||
} else if (node) {
|
|
||||||
setActiveButton(false);
|
|
||||||
}
|
|
||||||
}, [node, inputText, hasLightPushPeers]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TextComposer
|
|
||||||
onKeyDown={onKeyDown}
|
|
||||||
onChange={onChange}
|
|
||||||
active={isActive}
|
|
||||||
onButtonClick={sendMessage}
|
|
||||||
>
|
|
||||||
<Row align="center">
|
|
||||||
<Fill>
|
|
||||||
<TextInput value={inputText} />
|
|
||||||
</Fill>
|
|
||||||
<Fit>
|
|
||||||
<SendButton />
|
|
||||||
</Fit>
|
|
||||||
</Row>
|
|
||||||
</TextComposer>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
import type { LightNode } from "@waku/interfaces";
|
|
||||||
import ChatList from "./ChatList";
|
|
||||||
import MessageInput from "./MessageInput";
|
|
||||||
import { useWaku, useContentPair, useLightPush, usePeers } from "@waku/react";
|
|
||||||
import { TitleBar } from "@livechat/ui-kit";
|
|
||||||
import { Message } from "./Message";
|
|
||||||
import { ChatMessage } from "./chat_message";
|
|
||||||
import { useNodePeers } from "./hooks";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
messages: Message[];
|
|
||||||
nick: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Room(props: Props) {
|
|
||||||
const { node } = useWaku<LightNode>();
|
|
||||||
|
|
||||||
const { storePeers, filterPeers, lightPushPeers } = usePeers({ node });
|
|
||||||
const { bootstrapPeers, peerExchangePeers } = useNodePeers(node);
|
|
||||||
|
|
||||||
const { encoder } = useContentPair();
|
|
||||||
const { push: _sendMessage } = useLightPush({ node, encoder });
|
|
||||||
|
|
||||||
const sendMessage = async (text: string) => {
|
|
||||||
if (!_sendMessage) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const timestamp = new Date();
|
|
||||||
const chatMessage = ChatMessage.fromUtf8String(timestamp, props.nick, text);
|
|
||||||
const payload = chatMessage.encode();
|
|
||||||
|
|
||||||
await _sendMessage({ payload, timestamp });
|
|
||||||
};
|
|
||||||
|
|
||||||
const lightPushPeersLength = orZero(lightPushPeers?.length);
|
|
||||||
const filterPeersLength = orZero(filterPeers?.length);
|
|
||||||
const storePeersLength = orZero(storePeers?.length);
|
|
||||||
|
|
||||||
const peersMessage = `Peers: ${lightPushPeersLength} light push, ${filterPeersLength} filter, ${storePeersLength} store.`;
|
|
||||||
const bootstrapPeersMessage = `Bootstrap (DNS Discovery): ${bootstrapPeers.size}, Peer exchange: ${peerExchangePeers.size}. `;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="chat-container"
|
|
||||||
style={{ height: "98vh", display: "flex", flexDirection: "column" }}
|
|
||||||
>
|
|
||||||
<TitleBar
|
|
||||||
leftIcons={[peersMessage]}
|
|
||||||
rightIcons={[bootstrapPeersMessage, "View console for more details."]}
|
|
||||||
title="Waku v2 chat app"
|
|
||||||
/>
|
|
||||||
<ChatList messages={props.messages} />
|
|
||||||
<MessageInput
|
|
||||||
hasLightPushPeers={!!lightPushPeers}
|
|
||||||
sendMessage={sendMessage}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function orZero(value: undefined | number): number {
|
|
||||||
return value || 0;
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
import { utf8ToBytes, bytesToUtf8 } from "@waku/byte-utils";
|
|
||||||
import * as proto from "./proto/chat_message";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ChatMessage is used by the various show case waku apps that demonstrates
|
|
||||||
* waku used as the network layer for chat group applications.
|
|
||||||
*
|
|
||||||
* This is included to help building PoC and MVPs. Apps that aim to be
|
|
||||||
* production ready should use a more appropriate data structure.
|
|
||||||
*/
|
|
||||||
export class ChatMessage {
|
|
||||||
public constructor(public proto: proto.ChatMessage) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create Chat Message with a utf-8 string as payload.
|
|
||||||
*/
|
|
||||||
static fromUtf8String(
|
|
||||||
timestamp: Date,
|
|
||||||
nick: string,
|
|
||||||
text: string
|
|
||||||
): ChatMessage {
|
|
||||||
const timestampNumber = BigInt(Math.floor(timestamp.valueOf() / 1000));
|
|
||||||
const payload = utf8ToBytes(text);
|
|
||||||
|
|
||||||
return new ChatMessage({
|
|
||||||
timestamp: timestampNumber,
|
|
||||||
nick,
|
|
||||||
payload,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a protobuf payload to a ChatMessage.
|
|
||||||
* @param bytes The payload to decode.
|
|
||||||
*/
|
|
||||||
static decode(bytes: Uint8Array): ChatMessage {
|
|
||||||
const protoMsg = proto.ChatMessage.decode(bytes);
|
|
||||||
return new ChatMessage(protoMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode this ChatMessage to a byte array, to be used as a protobuf payload.
|
|
||||||
* @returns The encoded payload.
|
|
||||||
*/
|
|
||||||
encode(): Uint8Array {
|
|
||||||
return proto.ChatMessage.encode(this.proto);
|
|
||||||
}
|
|
||||||
|
|
||||||
get timestamp(): Date {
|
|
||||||
return new Date(Number(BigInt(this.proto.timestamp) * BigInt(1000)));
|
|
||||||
}
|
|
||||||
|
|
||||||
get nick(): string {
|
|
||||||
return this.proto.nick;
|
|
||||||
}
|
|
||||||
|
|
||||||
get payloadAsUtf8(): string {
|
|
||||||
if (!this.proto.payload) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytesToUtf8(this.proto.payload);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,140 +0,0 @@
|
|||||||
import { multiaddr } from "@multiformats/multiaddr";
|
|
||||||
import type { LightNode } from "@waku/interfaces";
|
|
||||||
|
|
||||||
function help(): string[] {
|
|
||||||
return [
|
|
||||||
"/nick <nickname>: set a new nickname",
|
|
||||||
"/info: some information about the node",
|
|
||||||
"/connect <Multiaddr>: connect to the given peer",
|
|
||||||
"/help: Display this help",
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function nick(
|
|
||||||
nick: string | undefined,
|
|
||||||
setNick: (nick: string) => void
|
|
||||||
): string[] {
|
|
||||||
if (!nick) {
|
|
||||||
return ["No nick provided"];
|
|
||||||
}
|
|
||||||
setNick(nick);
|
|
||||||
return [`New nick: ${nick}`];
|
|
||||||
}
|
|
||||||
|
|
||||||
function info(waku: LightNode | undefined): string[] {
|
|
||||||
if (!waku) {
|
|
||||||
return ["Waku node is starting"];
|
|
||||||
}
|
|
||||||
return [`PeerId: ${waku.libp2p.peerId.toString()}`];
|
|
||||||
}
|
|
||||||
|
|
||||||
function connect(
|
|
||||||
peer: string | undefined,
|
|
||||||
waku: LightNode | undefined
|
|
||||||
): string[] {
|
|
||||||
if (!waku) {
|
|
||||||
return ["Waku node is starting"];
|
|
||||||
}
|
|
||||||
if (!peer) {
|
|
||||||
return ["No peer provided"];
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const peerMultiaddr = multiaddr(peer);
|
|
||||||
const peerId = peerMultiaddr.getPeerId();
|
|
||||||
if (!peerId) {
|
|
||||||
return ["Peer Id needed to dial"];
|
|
||||||
}
|
|
||||||
waku
|
|
||||||
.dial(peerMultiaddr)
|
|
||||||
.catch((e) => console.error(`Failed to dial ${peerMultiaddr}`, e));
|
|
||||||
return [
|
|
||||||
`${peerId}: ${peerMultiaddr.toString()} added to address book, autodial in progress`,
|
|
||||||
];
|
|
||||||
} catch (e) {
|
|
||||||
return ["Invalid multiaddr: " + e];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function peers(waku: LightNode | undefined): Promise<string[]> {
|
|
||||||
if (!waku) {
|
|
||||||
return ["Waku node is starting"];
|
|
||||||
}
|
|
||||||
let response: string[] = [];
|
|
||||||
const peers = await waku.libp2p.peerStore.all();
|
|
||||||
|
|
||||||
Array.from(peers).forEach((peer) => {
|
|
||||||
response.push(peer.id.toString() + ":");
|
|
||||||
let addresses = " addresses: [";
|
|
||||||
peer.addresses.forEach(({ multiaddr }) => {
|
|
||||||
addresses += " " + multiaddr.toString() + ",";
|
|
||||||
});
|
|
||||||
addresses = addresses.replace(/,$/, "");
|
|
||||||
addresses += "]";
|
|
||||||
response.push(addresses);
|
|
||||||
let protocols = " protocols: [";
|
|
||||||
protocols += peer.protocols;
|
|
||||||
protocols += "]";
|
|
||||||
response.push(protocols);
|
|
||||||
});
|
|
||||||
if (response.length === 0) {
|
|
||||||
response.push("Not connected to any peer.");
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
function connections(waku: LightNode | undefined): string[] {
|
|
||||||
if (!waku) {
|
|
||||||
return ["Waku node is starting"];
|
|
||||||
}
|
|
||||||
let response: string[] = [];
|
|
||||||
let strConnections = " connections: \n";
|
|
||||||
waku.libp2p.connectionManager.getConnections().forEach((connection) => {
|
|
||||||
strConnections += connection.remotePeer.toString() + ", ";
|
|
||||||
strConnections += JSON.stringify(connection.stat);
|
|
||||||
strConnections += "; " + JSON.stringify(connection.streams);
|
|
||||||
strConnections += "\n";
|
|
||||||
});
|
|
||||||
response.push(strConnections);
|
|
||||||
if (response.length === 0) {
|
|
||||||
response.push("Not connected to any peer.");
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function handleCommand(
|
|
||||||
input: string,
|
|
||||||
waku: LightNode | undefined,
|
|
||||||
setNick: (nick: string) => void
|
|
||||||
): Promise<{ command: string; response: string[] }> {
|
|
||||||
let response: string[] = [];
|
|
||||||
const args = parseInput(input);
|
|
||||||
const command = args.shift()!;
|
|
||||||
switch (command) {
|
|
||||||
case "/help":
|
|
||||||
help().map((str) => response.push(str));
|
|
||||||
break;
|
|
||||||
case "/nick":
|
|
||||||
nick(args.shift(), setNick).map((str) => response.push(str));
|
|
||||||
break;
|
|
||||||
case "/info":
|
|
||||||
info(waku).map((str) => response.push(str));
|
|
||||||
break;
|
|
||||||
case "/connect":
|
|
||||||
connect(args.shift(), waku).map((str) => response.push(str));
|
|
||||||
break;
|
|
||||||
case "/peers":
|
|
||||||
(await peers(waku)).map((str) => response.push(str));
|
|
||||||
break;
|
|
||||||
case "/connections":
|
|
||||||
connections(waku).map((str) => response.push(str));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
response.push(`Unknown Command '${command}'`);
|
|
||||||
}
|
|
||||||
return { command, response };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseInput(input: string): string[] {
|
|
||||||
const clean = input.trim().replaceAll(/\s\s+/g, " ");
|
|
||||||
return clean.split(" ");
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
import { Protocols } from "@waku/interfaces";
|
|
||||||
|
|
||||||
export const CONTENT_TOPIC = "/toy-chat/2/ethdenver/proto";
|
|
||||||
|
|
||||||
const PUBLIC_KEY = "AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM";
|
|
||||||
const FQDN = "test.waku.nodes.status.im";
|
|
||||||
export const ENR_TREE = `enrtree://${PUBLIC_KEY}@${FQDN}`;
|
|
||||||
|
|
||||||
export const PROTOCOLS = [
|
|
||||||
Protocols.Filter,
|
|
||||||
Protocols.Store,
|
|
||||||
Protocols.LightPush,
|
|
||||||
];
|
|
@ -1,46 +0,0 @@
|
|||||||
import { wakuDnsDiscovery } from "@waku/dns-discovery";
|
|
||||||
import { wakuPeerExchangeDiscovery } from "@waku/peer-exchange";
|
|
||||||
|
|
||||||
import { ENR_TREE } from "./config";
|
|
||||||
|
|
||||||
export const NODE_OPTIONS = {
|
|
||||||
libp2p: {
|
|
||||||
peerDiscovery: [
|
|
||||||
wakuDnsDiscovery(ENR_TREE, {
|
|
||||||
store: 1,
|
|
||||||
filter: 2,
|
|
||||||
lightpush: 2,
|
|
||||||
}),
|
|
||||||
wakuPeerExchangeDiscovery(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const THEMES = {
|
|
||||||
AuthorName: {
|
|
||||||
css: {
|
|
||||||
fontSize: "1.1em",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Message: {
|
|
||||||
css: {
|
|
||||||
margin: "0em",
|
|
||||||
padding: "0em",
|
|
||||||
fontSize: "0.83em",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MessageText: {
|
|
||||||
css: {
|
|
||||||
margin: "0em",
|
|
||||||
padding: "0.1em",
|
|
||||||
paddingLeft: "1em",
|
|
||||||
fontSize: "1.1em",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MessageGroup: {
|
|
||||||
css: {
|
|
||||||
margin: "0em",
|
|
||||||
padding: "0.2em",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,107 +0,0 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import { generate } from "server-name-generator";
|
|
||||||
import { Message } from "./Message";
|
|
||||||
import { Decoder } from "@waku/core/lib/message/version_0";
|
|
||||||
import { LightNode, StoreQueryOptions } from "@waku/interfaces";
|
|
||||||
|
|
||||||
import { useFilterMessages, useStoreMessages } from "@waku/react";
|
|
||||||
|
|
||||||
export const usePersistentNick = (): [
|
|
||||||
string,
|
|
||||||
React.Dispatch<React.SetStateAction<string>>
|
|
||||||
] => {
|
|
||||||
const [nick, setNick] = useState<string>(() => {
|
|
||||||
const persistedNick = window.localStorage.getItem("nick");
|
|
||||||
return persistedNick !== null ? persistedNick : generate();
|
|
||||||
});
|
|
||||||
useEffect(() => {
|
|
||||||
localStorage.setItem("nick", nick);
|
|
||||||
}, [nick]);
|
|
||||||
|
|
||||||
return [nick, setNick];
|
|
||||||
};
|
|
||||||
|
|
||||||
type UseMessagesParams = {
|
|
||||||
node: undefined | LightNode;
|
|
||||||
decoder: undefined | Decoder;
|
|
||||||
options: StoreQueryOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
type UseMessagesResult = [Message[], (v: Message[]) => void];
|
|
||||||
|
|
||||||
export const useMessages = (params: UseMessagesParams): UseMessagesResult => {
|
|
||||||
const { messages: newMessages } = useFilterMessages(params);
|
|
||||||
const { messages: storedMessages } = useStoreMessages(params);
|
|
||||||
const [localMessages, setLocalMessages] = useState<Message[]>([]);
|
|
||||||
|
|
||||||
const pushMessages = (msgs: Message[]) => {
|
|
||||||
if (!msgs || !msgs.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setLocalMessages((prev) => [...prev, ...msgs]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const allMessages = React.useMemo((): Message[] => {
|
|
||||||
return [...storedMessages, ...newMessages]
|
|
||||||
.map(Message.fromWakuMessage)
|
|
||||||
.concat(localMessages)
|
|
||||||
.filter((v): v is Message => !!v)
|
|
||||||
.sort(
|
|
||||||
(left, right) => left.timestamp.getTime() - right.timestamp.getTime()
|
|
||||||
);
|
|
||||||
}, [storedMessages, newMessages, localMessages]);
|
|
||||||
|
|
||||||
return [allMessages, pushMessages];
|
|
||||||
};
|
|
||||||
|
|
||||||
// can be safely ignored
|
|
||||||
// this is for experiments on waku side around new discovery options
|
|
||||||
export const useNodePeers = (node: undefined | LightNode) => {
|
|
||||||
const [bootstrapPeers, setBootstrapPeers] = useState(new Set<string>());
|
|
||||||
const [peerExchangePeers, setPeerExchangePeers] = useState(new Set<string>());
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!node) return;
|
|
||||||
|
|
||||||
node.libp2p.peerStore.all().then(async (peers) => {
|
|
||||||
for (const peer of peers) {
|
|
||||||
const tags = (await node.libp2p.peerStore.getTags(peer.id)).map(
|
|
||||||
(t) => t.name
|
|
||||||
);
|
|
||||||
if (tags.includes("peer-exchange")) {
|
|
||||||
setPeerExchangePeers((peers) =>
|
|
||||||
new Set(peers).add(peer.id.toString())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
setBootstrapPeers((peers) => new Set(peers).add(peer.id.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update store peer when new peer connected & identified
|
|
||||||
node.libp2p.peerStore.addEventListener("change:protocols", async (evt) => {
|
|
||||||
const { peerId } = evt.detail;
|
|
||||||
const tags = (await node.libp2p.peerStore.getTags(peerId)).map(
|
|
||||||
(t) => t.name
|
|
||||||
);
|
|
||||||
if (tags.includes("peer-exchange")) {
|
|
||||||
setPeerExchangePeers((peers) => new Set(peers).add(peerId.toString()));
|
|
||||||
} else {
|
|
||||||
setBootstrapPeers((peers) => new Set(peers).add(peerId.toString()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [node]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log("Bootstrap Peers:");
|
|
||||||
console.table(bootstrapPeers);
|
|
||||||
|
|
||||||
console.log("Peer Exchange Peers:");
|
|
||||||
console.table(peerExchangePeers);
|
|
||||||
}, [bootstrapPeers, peerExchangePeers]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
bootstrapPeers,
|
|
||||||
peerExchangePeers,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,30 +0,0 @@
|
|||||||
@import-normalize; /* bring in normalize.css styles */
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.room-row {
|
|
||||||
text-align: left;
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.room-row:after {
|
|
||||||
clear: both;
|
|
||||||
content: "";
|
|
||||||
display: table;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-room {
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import ReactDOM from "react-dom";
|
|
||||||
import { ThemeProvider } from "@livechat/ui-kit";
|
|
||||||
import { LightNodeProvider, ContentPairProvider } from "@waku/react";
|
|
||||||
|
|
||||||
import "./index.css";
|
|
||||||
import App from "./App";
|
|
||||||
import { CONTENT_TOPIC, PROTOCOLS } from "./config";
|
|
||||||
import { NODE_OPTIONS, THEMES } from "./constants";
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<ThemeProvider theme={THEMES}>
|
|
||||||
<LightNodeProvider options={NODE_OPTIONS} protocols={PROTOCOLS}>
|
|
||||||
<ContentPairProvider contentTopic={CONTENT_TOPIC}>
|
|
||||||
<App />
|
|
||||||
</ContentPairProvider>
|
|
||||||
</LightNodeProvider>
|
|
||||||
</ThemeProvider>
|
|
||||||
</React.StrictMode>,
|
|
||||||
document.getElementById("root")
|
|
||||||
);
|
|
@ -1,7 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
message ChatMessage {
|
|
||||||
uint64 timestamp = 1;
|
|
||||||
string nick = 2;
|
|
||||||
bytes payload = 3;
|
|
||||||
}
|
|
@ -1,117 +0,0 @@
|
|||||||
/* eslint-disable import/export */
|
|
||||||
/* eslint-disable @typescript-eslint/no-namespace */
|
|
||||||
|
|
||||||
import { encodeMessage, decodeMessage, message } from "protons-runtime";
|
|
||||||
import type { Uint8ArrayList } from "uint8arraylist";
|
|
||||||
import type { Codec } from "protons-runtime";
|
|
||||||
|
|
||||||
export interface ChatMessage {
|
|
||||||
timestamp: bigint;
|
|
||||||
nick: string;
|
|
||||||
payload: Uint8Array;
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace ChatMessage {
|
|
||||||
let _codec: Codec<ChatMessage>;
|
|
||||||
|
|
||||||
export const codec = (): Codec<ChatMessage> => {
|
|
||||||
if (_codec == null) {
|
|
||||||
_codec = message<ChatMessage>(
|
|
||||||
(obj, writer, opts = {}) => {
|
|
||||||
if (opts.lengthDelimited !== false) {
|
|
||||||
writer.fork();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.timestamp != null) {
|
|
||||||
writer.uint32(8);
|
|
||||||
writer.uint64(obj.timestamp);
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
'Protocol error: required field "timestamp" was not found in object'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.nick != null) {
|
|
||||||
writer.uint32(18);
|
|
||||||
writer.string(obj.nick);
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
'Protocol error: required field "nick" was not found in object'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.payload != null) {
|
|
||||||
writer.uint32(26);
|
|
||||||
writer.bytes(obj.payload);
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
'Protocol error: required field "payload" was not found in object'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.lengthDelimited !== false) {
|
|
||||||
writer.ldelim();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(reader, length) => {
|
|
||||||
const obj: any = {
|
|
||||||
timestamp: 0n,
|
|
||||||
nick: "",
|
|
||||||
payload: new Uint8Array(0),
|
|
||||||
};
|
|
||||||
|
|
||||||
const end = length == null ? reader.len : reader.pos + length;
|
|
||||||
|
|
||||||
while (reader.pos < end) {
|
|
||||||
const tag = reader.uint32();
|
|
||||||
|
|
||||||
switch (tag >>> 3) {
|
|
||||||
case 1:
|
|
||||||
obj.timestamp = reader.uint64();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
obj.nick = reader.string();
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
obj.payload = reader.bytes();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
reader.skipType(tag & 7);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.timestamp == null) {
|
|
||||||
throw new Error(
|
|
||||||
'Protocol error: value for required field "timestamp" was not found in protobuf'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.nick == null) {
|
|
||||||
throw new Error(
|
|
||||||
'Protocol error: value for required field "nick" was not found in protobuf'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.payload == null) {
|
|
||||||
throw new Error(
|
|
||||||
'Protocol error: value for required field "payload" was not found in protobuf'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _codec;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const encode = (obj: ChatMessage): Uint8Array => {
|
|
||||||
return encodeMessage(obj, ChatMessage.codec());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const decode = (buf: Uint8Array | Uint8ArrayList): ChatMessage => {
|
|
||||||
return decodeMessage(buf, ChatMessage.codec());
|
|
||||||
};
|
|
||||||
}
|
|
1
examples/web-chat-dev/src/react-app-env.d.ts
vendored
1
examples/web-chat-dev/src/react-app-env.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
/// <reference types="react-scripts" />
|
|
@ -1,5 +0,0 @@
|
|||||||
// 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";
|
|
2
examples/web-chat-dev/src/types/types.d.ts
vendored
2
examples/web-chat-dev/src/types/types.d.ts
vendored
@ -1,2 +0,0 @@
|
|||||||
declare module "@livechat/ui-kit";
|
|
||||||
declare module "@waku/dns-discovery";
|
|
@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"incremental": true,
|
|
||||||
"target": "es2020",
|
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
|
||||||
"allowJs": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"typeRoots": ["node_modules/@types", "src/types"]
|
|
||||||
},
|
|
||||||
"include": ["src"]
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user