Closed jimmylee closed 4 years ago
This tutorial is a step by step guide for how to build your own Filecoin application using Textile's Powergate and Slate's React Component Library. After following these steps you will learn how to:
There are some other concepts you will be exposed to as you work through this tutorial:
node
and go
to build all the necessary dependencies.node
and go
.First check if MacOS XCode Command Line Tools are installed (You may have done it already):
xcode-select -p
If there is no response, then install them using this command:
xcode-select --install
Then install both node
and go
brew install node.
brew install go.
Congrats! Once this finishes you will be able to start your project. Find a directory on your computer. Create a directory named my-powergate-project
:
mkdir my-powergate-project
Afterwards create files named package.json
and .babelrc
.
cd my-powergate-project
touch package.json
touch .babelrc
You will need to add contents to both package.json
and .babelrc
, for the sake of speed, just copy and paste the contents below:
{
"name": "my-powergate-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"@emotion/core": "^10.0.28",
"@emotion/cache": "11.0.0-next.12",
"@emotion/css": "11.0.0-next.12",
"@emotion/react": "11.0.0-next.12",
"@emotion/server": "11.0.0-next.12",
"@emotion/styled": "11.0.0-next.12",
"@emotion/babel-preset-css-prop": "^10.0.27",
"next": "latest",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"@textile/powergate-client": "0.1.0-beta.13",
"slate-react-system": "0.0.4"
}
}
{
"presets": [
[
"next/babel",
{
"transform-runtime": {
"useESModules": false
}
}
],
"@emotion/babel-preset-css-prop"
]
}
Now run npm install
. Once that command has finished, you can create a pages
directory and the necessary files for NextJS
to function properly:
mkdir pages
cd pages
touch index.js
touch _app.js
touch _document.js
For the sake of time, populate the files with the contents below:
import Document, { Head, Main, NextScript } from "next/document";
import { extractCritical } from "@emotion/server";
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
const styles = extractCritical(initialProps.html);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
<style
data-emotion-css={styles.ids.join(" ")}
dangerouslySetInnerHTML={{ __html: styles.css }}
/>
</>
),
};
}
render() {
return (
<html>
<Head />
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
import { CacheProvider } from "@emotion/react";
import { cache } from "@emotion/css";
function MyApp({ Component, pageProps }) {
return (
<CacheProvider value={cache}>
<Component {...pageProps} />
</CacheProvider>
);
}
export default MyApp;
In your index.js
import * as React from "react";
import { css } from "@emotion/react";
const STYLES_PAGE = css`
margin: 0;
padding: 0;
`;
export default class TestPage extends React.Component {
render() {
return <div css={STYLES_PAGE}>Hey everyone!</div>;
}
}
You should be ready to go! If there is any reason you got stuck or it was too difficult, you can go here and start from this example.
In your terminal client, run the following:
next
You should see the following in your terminal screen:
If you visit localhost:3000
in your browser, it should render a web page.
In /pages/index.js
you can import slate-react-system
.
import * as React from "react";
import * as System from "slate-react-system";
Once you add slate-react-system
. There are a ton of components you can use in your web application! A list of design system components live here: https://slate.host/system.
Lets start with a button:
import * as React from "react";
import * as System from "slate-react-system";
import { css } from "@emotion/react";
const STYLES_PAGE = css`
margin: 0;
padding: 0;
`;
export default class TestPage extends React.Component {
_handleClick = () => alert('You clicked me!');
render() {
return (
<div css={STYLES_PAGE}>
<System.ButtonPrimary onClick={this._handleClick}>
Hello There
</System.ButtonPrimary>
</div>
);
}
}
Some of these components will require Powergate to function.
Your web application should be running at this point. Now it is time to run Powergate on your machine. You will need to do the following:
git clone https://github.com/textileio/powergate.git
cd powergate
cd docker
make localnet
Once your localnet is running, you can add this code to /pages/index.js
.
// NOTE:
// This is how you use the createPow constructor:
// createPow({ host: "http://0.0.0.0:6002" });
import { createPow } from "@textile/powergate-client";
Now we can use slate-react-system
components that depend on Powergate running.
Here is how we add a component that can generate an authentication token.
import * as React from "react";
import * as System from "slate-react-system";
import { css } from "@emotion/react";
import { createPow } from "@textile/powergate-client";
const STYLES_PAGE = css`
margin: 0;
padding: 0;
`;
export default class TestPage extends React.Component {
PG = null;
state = { token: null };
_handleCreateToken = async () => {
this.PG = createPow({ host: "http://0.0.0.0:6002" });
const FFS = await this.PG.ffs.create();
this.setState({ token: FFS.token ? FFS.token : null });
this.PG.setToken(FFS.token);
};
render() {
return (
<div css={STYLES_PAGE}>
<System.CreateToken onClick={this._handleCreateToken}>
Hello There
</System.CreateToken>
</div>
);
}
}
Here is how we add a refresh button and get the updated Powergate state once you have a token.
import * as React from "react";
import * as System from "slate-react-system";
import { css } from "@emotion/react";
import { createPow } from "@textile/powergate-client";
const STYLES_PAGE = css`
margin: 0;
padding: 0;
`;
export default class TestPage extends React.Component {
PG = null;
state = { token : null, info: null, addrsList: [] };
_handleCreateToken = async () => {
this.PG = createPow({ host: "http://0.0.0.0:6002" });
const FFS = await this.PG.ffs.create();
this.setState({ token: FFS.token ? FFS.token : null });
this.PG.setToken(FFS.token);
};
_handleRefresh = async () => {
const { addrsList } = await this.PG.ffs.addrs();
const { info } = await this.PG.ffs.info();
this.setState({ addrsList, info });
}
render() {
return (
<div css={STYLES_PAGE}>
<System.CreateToken onClick={this._handleCreateToken}>
Hello There
</System.CreateToken>
<System.ButtonPrimary onClick={this._handleRefresh}>
Refresh
</System.ButtonPrimary>
</div>
);
}
}
Your screen should look something like this:
Let's add a component to see a list of Filecoin addresses:
import * as React from "react";
import * as System from "slate-react-system";
import { css } from "@emotion/react";
import { createPow } from "@textile/powergate-client";
const STYLES_PAGE = css`
margin: 0;
padding: 0;
`;
export default class TestPage extends React.Component {
PG = null;
state = { token : null, info: null, addrsList: [] };
_handleCreateToken = async () => {
this.PG = createPow({ host: "http://0.0.0.0:6002" });
const FFS = await this.PG.ffs.create();
this.setState({ token: FFS.token ? FFS.token : null });
this.PG.setToken(FFS.token);
};
_handleRefresh = async () => {
const { addrsList } = await this.PG.ffs.addrs();
const { info } = await this.PG.ffs.info();
this.setState({ addrsList, info });
}
render() {
const { token, info } = this.state;
return (
<div css={STYLES_PAGE}>
<System.CreateToken onClick={this._handleCreateToken}>
Hello There
</System.CreateToken>
<System.ButtonPrimary onClick={this._handleRefresh}>
Refresh
</System.ButtonPrimary>
{info ? (
<System.FilecoinBalancesList data={info.balancesList} />
) : null}
</div>
);
}
}
Create a class member function for the create address handler.
_handleCreateAddress = async ({ name, type, makeDefault }) => {
const response = await this.PG.ffs.newAddr(name, type, makeDefault);
}
Add the component to the return value of render()
<System.CreateFilecoinAddress onSubmit={this._handleCreateAddress} />
Now when you hit Refresh you should see a new address after you use the component.
Create a class member function for the send Filecoin address handler.
_handleSendFilecoin = async ({ source, target, amount }) => {
const response = await this.PG.ffs.sendFil(source, target, amount);
}
Add the component to the return value of render()
<System.SendAddressFilecoin onSubmit={this._handleSendFilecoin} />
Now when you hit Refresh you should see the balances have been updated for Filecoin you are sending between addresses.
Here is a complete snippet for your /pages/index.js
if you followed all the steps.
import * as React from "react";
import * as System from "slate-react-system";
import { css } from "@emotion/react";
import { createPow } from "@textile/powergate-client";
const STYLES_PAGE = css`
margin: 0;
padding: 0;
`;
export default class TestPage extends React.Component {
PG = null;
state = { token : null, info: null, addrsList: [] };
_handleCreateToken = async () => {
this.PG = createPow({ host: "http://0.0.0.0:6002" });
const FFS = await this.PG.ffs.create();
this.setState({ token: FFS.token ? FFS.token : null });
this.PG.setToken(FFS.token);
};
_handleCreateAddress = async ({ name, type, makeDefault }) => {
const response = await this.PG.ffs.newAddr(name, type, makeDefault);
}
_handleSendFilecoin = async ({ source, target, amount }) => {
const response = await this.PG.ffs.sendFil(source, target, amount);
}
_handleRefresh = async () => {
const { addrsList } = await this.PG.ffs.addrs();
const { info } = await this.PG.ffs.info();
this.setState({ addrsList, info });
}
render() {
const { token, info } = this.state;
return (
<div css={STYLES_PAGE}>
<System.CreateToken onClick={this._handleCreateToken}>
Hello There
</System.CreateToken>
<System.ButtonPrimary onClick={this._handleRefresh}>
Refresh
</System.ButtonPrimary>
{info ? (
<System.FilecoinBalancesList data={info.balancesList} />
) : null}
{info ? (
<System.CreateFilecoinAddress onSubmit={this._handleCreateAddress} />
) : null}
{info ? (
<System.SendAddressFilecoin onSubmit={this._handleSendFilecoin} />
) : null}
</div>
);
}
}
That should get your started!
Thank you taking the time to read this! Happy hacking :)
This is awesome @jimmylee ! I think looks good to push it up -- want to do a quick check-in on structure for the docs site?
Purpose
Deliverable