Split application into components

This commit is contained in:
Pavel Murygin
2021-07-22 00:28:58 +03:00
parent d317eb5094
commit 5a787dcf22
13 changed files with 482 additions and 375 deletions

View File

@ -1,5 +1,5 @@
{ {
"name": "ipfs-execution", "name": "@fluencelabs/ipfs-execution",
"version": "0.1.0", "version": "0.1.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
@ -1275,7 +1275,7 @@
} }
}, },
"node-fetch": { "node-fetch": {
"version": "npm:@achingbrain/node-fetch@2.6.7", "version": "npm:node-fetch@2.6.7",
"resolved": "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz", "resolved": "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-iTASGs+HTFK5E4ZqcMsHmeJ4zodyq8L38lZV33jwqcBJYoUt3HjN4+ot+O9/0b+ke8ddE7UgOtVuZN/OkV19/g==" "integrity": "sha512-iTASGs+HTFK5E4ZqcMsHmeJ4zodyq8L38lZV33jwqcBJYoUt3HjN4+ot+O9/0b+ke8ddE7UgOtVuZN/OkV19/g=="
}, },
@ -1414,7 +1414,7 @@
} }
}, },
"node-fetch": { "node-fetch": {
"version": "npm:@achingbrain/node-fetch@2.6.7", "version": "npm:node-fetch@2.6.7",
"resolved": "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz", "resolved": "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-iTASGs+HTFK5E4ZqcMsHmeJ4zodyq8L38lZV33jwqcBJYoUt3HjN4+ot+O9/0b+ke8ddE7UgOtVuZN/OkV19/g==" "integrity": "sha512-iTASGs+HTFK5E4ZqcMsHmeJ4zodyq8L38lZV33jwqcBJYoUt3HjN4+ot+O9/0b+ke8ddE7UgOtVuZN/OkV19/g=="
}, },
@ -1566,7 +1566,7 @@
} }
}, },
"node-fetch": { "node-fetch": {
"version": "npm:@achingbrain/node-fetch@2.6.7", "version": "npm:node-fetch@2.6.7",
"resolved": "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz", "resolved": "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-iTASGs+HTFK5E4ZqcMsHmeJ4zodyq8L38lZV33jwqcBJYoUt3HjN4+ot+O9/0b+ke8ddE7UgOtVuZN/OkV19/g==" "integrity": "sha512-iTASGs+HTFK5E4ZqcMsHmeJ4zodyq8L38lZV33jwqcBJYoUt3HjN4+ot+O9/0b+ke8ddE7UgOtVuZN/OkV19/g=="
}, },

View File

@ -12679,6 +12679,11 @@
"pify": "^4.0.1" "pify": "^4.0.1"
} }
}, },
"hamt_plus": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
"integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE="
},
"handle-thing": { "handle-thing": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
@ -21786,6 +21791,14 @@
"ms": "^2.1.1" "ms": "^2.1.1"
} }
}, },
"recoil": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/recoil/-/recoil-0.3.1.tgz",
"integrity": "sha512-KNA3DRqgxX4rRC8E7fc6uIw7BACmMPuraIYy+ejhE8tsw7w32CetMm8w7AMZa34wzanKKkev3vl3H7Z4s0QSiA==",
"requires": {
"hamt_plus": "1.0.2"
}
},
"recursive-readdir": { "recursive-readdir": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",

View File

@ -4,10 +4,9 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@fluencelabs/aqua-ipfs": "0.1.8", "@fluencelabs/aqua-ipfs": "0.1.8",
"@fluencelabs/ipfs-execution": "file:../aqua",
"@fluencelabs/fluence": "0.9.53", "@fluencelabs/fluence": "0.9.53",
"@fluencelabs/fluence-network-environment": "1.0.10", "@fluencelabs/fluence-network-environment": "1.0.10",
"ipfs-http-client": "^50.1.2", "@fluencelabs/ipfs-execution": "file:../aqua",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7", "@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3", "@testing-library/user-event": "^12.8.3",
@ -15,15 +14,17 @@
"@types/node": "^12.20.16", "@types/node": "^12.20.16",
"@types/react": "^17.0.14", "@types/react": "^17.0.14",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"ipfs-http-client": "^50.1.2",
"multiaddr": "^10.0.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-scripts": "4.0.3", "react-scripts": "4.0.3",
"recoil": "^0.3.1",
"typescript": "^4.3.5", "typescript": "^4.3.5",
"web-vitals": "^1.1.2", "web-vitals": "^1.1.2"
"multiaddr": "^10.0.0"
}, },
"scripts": { "scripts": {
"prestart": "(cd ../aqua; npm run build)", "_prestart": "(cd ../aqua; npm run build)",
"start": "react-scripts start", "start": "react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",

View File

@ -1,143 +1,25 @@
import React, { useState } from "react"; import React from "react";
import logo from "./logo.svg"; import logo from "./logo.svg";
import "./App.scss"; import "./App.scss";
import { createClient, FluenceClient } from "@fluencelabs/fluence"; import { gotRpcAddrState, isConnectedState, isDeployedState } from "./state";
import { get_external_api_multiaddr } from "@fluencelabs/aqua-ipfs"; import { useRecoilValue } from "recoil";
import { stage } from "@fluencelabs/fluence-network-environment"; import { ConnectedInfo } from "./Components/ConnectedInfo";
import { import { ConnectionForm } from "./Components/ConnectionForm";
deploy_service, import { IpfsForm } from "./Components/IpfsForm";
put_file_size, import { IpfsDeploymentInfo } from "./Components/IpfsDeploymentInfo";
remove_service, import { SizeCalcForm } from "./Components/SizeCalcForm";
provideFile,
} from "@fluencelabs/ipfs-execution";
import { Multiaddr, protocols } from "multiaddr";
const { create, globSource, urlSource, CID } = require("ipfs-http-client");
const relayNodes = [stage[0], stage[1], stage[2]];
const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text);
};
function fromOption<T>(opt: T | T[] | null): T | null {
if (Array.isArray(opt)) {
if (opt.length === 0) {
return null;
}
opt = opt[0];
}
if (opt === null) {
return null;
}
return opt;
}
function decapsulateP2P(rpcAddr: string): string {
return new Multiaddr(rpcAddr)
.decapsulateCode(protocols.names.p2p.code)
.toString();
}
function App() { function App() {
const [client, setClient] = useState<FluenceClient | null>(null); const isConnected = useRecoilValue(isConnectedState);
const [serviceId, setServiceId] = useState<string | null>(null); const gotRpcAddr = useRecoilValue(gotRpcAddrState);
const isDeployed = useRecoilValue(isDeployedState);
const [wasm, setWasm] = useState<string>(
"Qmf8fH2cDZXGKS9uDGBcHxv5uQ51ChrigdZKe3QxS2C1AF"
);
const [rpcAddr, setRpcAddr] = useState<string>("");
const [fileCID, setFileCID] = useState<string>("");
const [fileSize, setFileSize] = useState<string>("");
const [fileSizeCID, setFileSizeCID] = useState<string>("");
const isConnected = client !== null;
const gotRpcAddr = rpcAddr !== null;
const deployed = serviceId !== null;
const connect = async (relayPeerId: string) => {
try {
let client = await createClient(relayPeerId);
setClient(client);
await getRpcAddr(client);
} catch (err) {
console.log("Client initialization failed", err);
}
};
const getRpcAddr = async (client: FluenceClient) => {
if (client === null) {
console.log("getRpcAddr client is null");
return;
}
let result = await get_external_api_multiaddr(client, client.relayPeerId!);
console.log("getRpcAddr result", result);
let rpcAddr = result.multiaddr;
setRpcAddr(decapsulateP2P(rpcAddr));
};
const deployService = async () => {
console.log("wasm %s rpcAddr %s", wasm, rpcAddr);
if (client === null || wasm === null || rpcAddr === null) {
return;
}
var service_id = await deploy_service(
client,
client.relayPeerId!,
wasm,
rpcAddr,
(msg, value) => console.log(msg, value),
{ ttl: 10000 }
);
service_id = fromOption(service_id);
setServiceId(service_id);
};
const getFileSize = async () => {
if (client === null || serviceId === null || rpcAddr === null) {
return;
}
var putResult = await put_file_size(
client,
client.relayPeerId!,
fileCID,
rpcAddr,
serviceId,
(size) => setFileSize(size.toString()),
(label, error) => setFileSize("Error: " + label + ": " + error),
{ ttl: 10000 }
);
putResult = fromOption(putResult);
if (putResult === null) {
return;
}
if (putResult.success) {
setFileSizeCID(putResult.hash);
} else {
setFileSizeCID("Error: " + putResult.error);
}
};
const removeService = async () => {
if (client === null || serviceId === null) {
return;
}
await remove_service(client, client.relayPeerId!, serviceId, {
ttl: 10000,
});
setServiceId(null);
};
console.log( console.log(
"isConnected gotRpcAddr deployed\n", "isConnected gotRpcAddr deployed\n",
isConnected, isConnected,
gotRpcAddr, gotRpcAddr,
deployed isDeployed
); );
return ( return (
@ -147,33 +29,13 @@ function App() {
</header> </header>
<div className="content"> <div className="content">
{!isConnected && <ConnectionForm connect={connect} />} {!isConnected && <ConnectionForm />}
{isConnected && <Connected client={client!} />} {isConnected && <ConnectedInfo />}
{isConnected && gotRpcAddr && !deployed && ( {isConnected && gotRpcAddr && !isDeployed && <IpfsForm />}
<IpfsForm {isDeployed && (
rpcAddr={rpcAddr}
setRpcAddr={setRpcAddr}
wasm={wasm}
setWasm={setWasm}
deployService={deployService}
/>
)}
{deployed && (
<> <>
<IfsDeployed <IpfsDeploymentInfo />
wasm={wasm} <SizeCalcForm />
serviceId={serviceId}
removeService={removeService}
/>
<Deployed
setRpcAddr={setRpcAddr}
rpcAddr={rpcAddr}
setFileCID={setFileCID}
fileCID={fileCID}
fileSize={fileSize}
fileSizeCID={fileSizeCID}
getFileSize={getFileSize}
/>
</> </>
)} )}
</div> </div>
@ -181,207 +43,4 @@ function App() {
); );
} }
const Deployed = (props: {
setRpcAddr: React.Dispatch<React.SetStateAction<string>>;
rpcAddr: string;
setFileCID: React.Dispatch<React.SetStateAction<string>>;
fileCID: string;
fileSize: string;
fileSizeCID: string;
getFileSize: () => Promise<void>;
}) => {
return (
<>
<div>
<h2>Get file size</h2>
<p className="p">
Upload any file to IPFS node
<p>
<b>{props.rpcAddr}</b>
</p>
paste CID here and get the size of that file via ProcessFiles service
you have just deployed
</p>
<div className="row">
<label className="label bold">IPFS CID</label>
<input
className="input"
type="text"
onChange={(e) => props.setFileCID(e.target.value)}
value={props.fileCID}
/>
</div>
<div className="row">
<button className="btn btn-hello" onClick={props.getFileSize}>
get size
</button>
</div>
</div>
<div className="row">
<label className="label bold">File Size:</label>
<label className="mono">{props.fileSize}</label>
</div>
<div className="row">
<label className="label bold">
File size is uploaded to IPFS as CID:
</label>
<label className="mono">{props.fileSizeCID}</label>
</div>
</>
);
};
const IpfsForm = (props: {
rpcAddr: string;
setRpcAddr: React.Dispatch<React.SetStateAction<string>>;
wasm: string;
setWasm: React.Dispatch<React.SetStateAction<string>>;
deployService: () => Promise<void>;
}) => {
return (
<>
<h2>Ipfs</h2>
<p>
process_files.wasm will be downloaded via IPFS to the Fluence node, and
then a service will be dynamically created from it! After that, you will
be able to use that service to get sizes of IPFS files!
<br />
To do so, please specify IPFS RPC address to download process_files.wasm
from
<br />
And specify CID of WebAssembly module.
</p>
<TextInput
text={"IPFS RPC address"}
value={props.rpcAddr}
setValue={props.setRpcAddr}
/>
<TextInput
text={"process_files.wasm module CID"}
value={props.wasm}
setValue={props.setWasm}
/>
<div className="row">
<button className="btn btn-hello" onClick={props.deployService}>
deploy service
</button>
</div>
</>
);
};
const IfsDeployed = (props: {
wasm: string;
serviceId: string | null;
removeService: () => {};
}) => {
return (
<>
<h2>Deployed</h2>
<table>
<tr>
<td className="bold">process_files.wasm CID:</td>
<td className="mono">{props.wasm}</td>
<td>
<button
className="btn-clipboard"
onClick={() => copyToClipboard(props.wasm)}
>
<i className="gg-clipboard"></i>
</button>
</td>
</tr>
<tr>
<td className="bold">ProcessFiles service ID:</td>
<td className="mono">{props.serviceId}</td>
<button
className="btn-clipboard"
onClick={() => props.removeService()}
>
<i className="gg-trash"></i>
</button>
</tr>
</table>
</>
);
};
const ConnectionForm = (props: {
connect: (multiaddr: string) => Promise<void>;
}) => {
return (
<>
<h1>Pick a relay</h1>
<ul>
{relayNodes.map((x) => (
<li key={x.peerId}>
<span className="mono">{x.peerId}</span>
<button
className="btn"
onClick={async () => await props.connect(x.multiaddr)}
>
Connect
</button>
</li>
))}
</ul>
</>
);
};
const Connected = (props: { client: FluenceClient }) => {
return (
<>
<h1>Connected</h1>
<table>
<tr>
<td className="bold">Peer id:</td>
<td className="mono">{props.client.selfPeerId}</td>
<td>
<button
className="btn-clipboard"
onClick={() => copyToClipboard(props.client.selfPeerId)}
>
<i className="gg-clipboard"></i>
</button>
</td>
</tr>
<tr>
<td className="bold">Relay peer id:</td>
<td className="mono">{props.client.relayPeerId}</td>
<td>
<button
className="btn-clipboard"
onClick={() => copyToClipboard(props.client.relayPeerId!)}
>
<i className="gg-clipboard"></i>
</button>
</td>
</tr>
</table>
</>
);
};
const TextInput = (props: {
text: string;
value: string;
setValue: React.Dispatch<React.SetStateAction<string>>;
}) => {
return (
<div className="row">
<label className="label bold">{props.text}</label>
<input
className="input"
type="text"
onChange={(e) => props.setValue(e.target.value)}
value={props.value}
required={true}
/>
</div>
);
};
export default App; export default App;

View File

@ -0,0 +1,44 @@
import { useRecoilValue } from "recoil";
import { clientState } from "../state";
import { copyToClipboard } from "../util";
export const ConnectedInfo = () => {
const client = useRecoilValue(clientState);
if (client === null) {
return <></>;
}
return (
<>
<h1>Connected</h1>
<table>
<thead>
<tr>
<td className="bold">Peer id:</td>
<td className="mono">{client.selfPeerId}</td>
<td>
<button
className="btn-clipboard"
onClick={() => copyToClipboard(client.selfPeerId)}
>
<i className="gg-clipboard"></i>
</button>
</td>
</tr>
<tr>
<td className="bold">Relay peer id:</td>
<td className="mono">{client.relayPeerId}</td>
<td>
<button
className="btn-clipboard"
onClick={() => copyToClipboard(client.relayPeerId!)}
>
<i className="gg-clipboard"></i>
</button>
</td>
</tr>
</thead>
</table>
</>
);
};

View File

@ -0,0 +1,21 @@
import { relayNodes, useClientConnect } from "../state";
export const ConnectionForm = () => {
const connect = useClientConnect();
return (
<>
<h1>Pick a relay</h1>
<ul>
{relayNodes.map((x) => (
<li key={x.peerId}>
<span className="mono">{x.peerId}</span>
<button className="btn" onClick={() => connect(x.multiaddr)}>
Connect
</button>
</li>
))}
</ul>
</>
);
};

View File

@ -0,0 +1,36 @@
import { useRecoilValue } from "recoil";
import { serviceIdState, useRemoveServuce, wasmState } from "../state";
import { copyToClipboard } from "../util";
export const IpfsDeploymentInfo = () => {
const wasm = useRecoilValue(wasmState);
const serviceId = useRecoilValue(serviceIdState);
const removeService = useRemoveServuce;
return (
<>
<h2>Deployed</h2>
<table>
<tr>
<td className="bold">process_files.wasm CID:</td>
<td className="mono">{wasm}</td>
<td>
<button
className="btn-clipboard"
onClick={() => copyToClipboard(wasm)}
>
<i className="gg-clipboard"></i>
</button>
</td>
</tr>
<tr>
<td className="bold">ProcessFiles service ID:</td>
<td className="mono">{serviceId}</td>
<button className="btn-clipboard" onClick={removeService}>
<i className="gg-trash"></i>
</button>
</tr>
</table>
</>
);
};

View File

@ -0,0 +1,42 @@
import { useRecoilState, useResetRecoilState } from "recoil";
import { rpcAddrState, useDeployService, wasmState } from "../state";
import { TextInput } from "./TextInput";
export const IpfsForm = () => {
const [rpcAddr, setRpcAddr] = useRecoilState(rpcAddrState);
const [wasm, setWasm] = useRecoilState(wasmState);
const deployService = useDeployService();
return (
<>
<h2>Ipfs</h2>
<p>
process_files.wasm will be downloaded via IPFS to the Fluence node, and
then a service will be dynamically created from it! After that, you will
be able to use that service to get sizes of IPFS files!
<br />
To do so, please specify IPFS RPC address to download process_files.wasm
from
<br />
And specify CID of WebAssembly module.
</p>
<TextInput
text={"IPFS RPC address"}
value={rpcAddr}
setValue={setRpcAddr}
/>
<TextInput
text={"process_files.wasm module CID"}
value={wasm}
setValue={setWasm}
/>
<div className="row">
<button className="btn btn-hello" onClick={deployService}>
deploy service
</button>
</div>
</>
);
};

View File

@ -0,0 +1,56 @@
import { useRecoilState, useRecoilValue } from "recoil";
import {
fileCIDState,
fileSizeCIDState,
fileSizeState,
rpcAddrState,
useGetFileSize,
} from "../state";
export const SizeCalcForm = () => {
const rpcAddr = useRecoilValue(rpcAddrState);
const [fileCID, setFileCID] = useRecoilState(fileCIDState);
const fileSize = useRecoilValue(fileSizeState);
const fileSizeCID = useRecoilValue(fileSizeCIDState);
const getFileSize = useGetFileSize();
return (
<>
<div>
<h2>Get file size</h2>
<p className="p">
Upload any file to IPFS node
<p>
<b>{rpcAddr}</b>
</p>
paste CID here and get the size of that file via ProcessFiles service
you have just deployed
</p>
<div className="row">
<label className="label bold">IPFS CID</label>
<input
className="input"
type="text"
onChange={(e) => setFileCID(e.target.value)}
value={fileCID}
/>
</div>
<div className="row">
<button className="btn btn-hello" onClick={getFileSize}>
get size
</button>
</div>
</div>
<div className="row">
<label className="label bold">File Size:</label>
<label className="mono">{fileSize}</label>
</div>
<div className="row">
<label className="label bold">
File size is uploaded to IPFS as CID:
</label>
<label className="mono">{fileSizeCID}</label>
</div>
</>
);
};

View File

@ -0,0 +1,20 @@
import React from "react";
export const TextInput = (props: {
text: string;
value: string | null;
setValue: (val: string) => void;
}) => {
return (
<div className="row">
<label className="label bold">{props.text}</label>
<input
className="input"
type="text"
onChange={(e) => props.setValue(e.target.value)}
value={props.value || ""}
required={true}
/>
</div>
);
};

View File

@ -1,12 +1,14 @@
import React from 'react'; import React from "react";
import ReactDOM from 'react-dom'; import ReactDOM from "react-dom";
import './index.css'; import "./index.css";
import App from './App'; import App from "./App";
import { RecoilRoot } from "recoil";
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<RecoilRoot>
<App /> <App />
</RecoilRoot>
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById("root")
); );

View File

@ -0,0 +1,187 @@
import { get_external_api_multiaddr } from "@fluencelabs/aqua-ipfs";
import {
deploy_service,
put_file_size,
remove_service,
} from "@fluencelabs/ipfs-execution";
import { createClient, FluenceClient } from "@fluencelabs/fluence";
import { stage } from "@fluencelabs/fluence-network-environment";
import {
atom,
selector,
useRecoilState,
useRecoilValue,
useSetRecoilState,
} from "recoil";
import { decapsulateP2P, fromOption } from "./util";
export const relayNodes = [stage[0], stage[1], stage[2]];
export const clientState = atom<FluenceClient | null>({
key: "clientState",
default: null,
dangerouslyAllowMutability: true,
});
export const serviceIdState = atom<string | null>({
key: "serviceIdState",
default: null,
});
export const wasmState = atom<string>({
key: "serviceState",
default: "Qmf8fH2cDZXGKS9uDGBcHxv5uQ51ChrigdZKe3QxS2C1AF",
});
export const rpcAddrState = atom<string | null>({
key: "rpcAddrState",
default: null,
});
export const fileCIDState = atom<string>({
key: "fileCIDState",
default: "",
});
export const fileSizeState = atom<string>({
key: "fileSizeState",
default: "",
});
export const fileSizeCIDState = atom<string>({
key: "fileSizeCIDState",
default: "",
});
export const isConnectedState = selector({
key: "isConnectedState",
get: ({ get }) => {
const client = get(clientState);
return client !== null && client.isConnected;
},
dangerouslyAllowMutability: true,
});
export const gotRpcAddrState = selector({
key: "getRpcAddrState",
get: ({ get }) => {
const rpcAddr = get(rpcAddrState);
return rpcAddr !== null;
},
});
export const isDeployedState = selector({
key: "isDeployedState",
get: ({ get }) => {
const serviceId = get(serviceIdState);
return serviceId !== null;
},
});
const getRpcAddr = async (client: FluenceClient) => {
if (client === null) {
console.log("getRpcAddr client is null");
return;
}
let result = await get_external_api_multiaddr(client, client.relayPeerId!);
console.log("getRpcAddr result", result);
let rpcAddr = result.multiaddr;
return decapsulateP2P(rpcAddr);
};
export const useClientConnect = () => {
const setClient = useSetRecoilState(clientState);
const setRpcAddr = useSetRecoilState(rpcAddrState);
const connect = async (relayPeerId: string) => {
try {
const client = await createClient(relayPeerId);
const addr = await getRpcAddr(client);
setRpcAddr(addr!);
setClient(client);
} catch (err) {
console.log("Client initialization failed", err);
}
};
return connect;
};
export const useDeployService = () => {
const wasm = useRecoilValue(wasmState);
const rpcAddr = useRecoilValue(rpcAddrState);
const client = useRecoilValue(clientState);
const setServiceId = useSetRecoilState(serviceIdState);
return async () => {
console.log("wasm %s rpcAddr %s", wasm, rpcAddr);
if (client === null || wasm === null || rpcAddr === null) {
return;
}
var service_id = await deploy_service(
client,
client.relayPeerId!,
wasm,
rpcAddr,
(msg, value) => console.log(msg, value),
{ ttl: 10000 }
);
service_id = fromOption(service_id);
setServiceId(service_id);
};
};
export const useGetFileSize = () => {
const rpcAddr = useRecoilValue(rpcAddrState);
const client = useRecoilValue(clientState);
const serviceId = useRecoilValue(serviceIdState);
const setFileSize = useSetRecoilState(fileSizeState);
const [fileCID, setFileSizeCID] = useRecoilState(fileCIDState);
return async () => {
if (client === null || serviceId === null || rpcAddr === null) {
return;
}
var putResult = await put_file_size(
client,
client.relayPeerId!,
fileCID,
rpcAddr,
serviceId,
(size) => setFileSize(size.toString()),
(label, error) => setFileSize("Error: " + label + ": " + error),
{ ttl: 10000 }
);
putResult = fromOption(putResult);
if (putResult === null) {
return;
}
if (putResult.success) {
setFileSizeCID(putResult.hash);
} else {
setFileSizeCID("Error: " + putResult.error);
}
};
};
export const useRemoveServuce = () => {
const client = useRecoilValue(clientState);
const [serviceId, setServiceId] = useRecoilState(serviceIdState);
return async () => {
if (client === null || serviceId === null) {
return;
}
await remove_service(client, client.relayPeerId!, serviceId, {
ttl: 10000,
});
setServiceId(null);
};
};

View File

@ -0,0 +1,26 @@
import { Multiaddr, protocols } from "multiaddr";
export const decapsulateP2P = (rpcAddr: string) => {
return new Multiaddr(rpcAddr)
.decapsulateCode(protocols.names.p2p.code)
.toString();
};
export const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text);
};
export const fromOption = <T>(opt: T | T[] | null): T | null => {
if (Array.isArray(opt)) {
if (opt.length === 0) {
return null;
}
opt = opt[0];
}
if (opt === null) {
return null;
}
return opt;
};