From 118bc57fc4683d17aa583c9f445f9398642966de Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Mon, 9 Mar 2020 15:15:07 -0400 Subject: [PATCH] Update docs to reflect latest subspace changes --- packages/core/README.md | 2 +- packages/docs/api.md | 6 +- packages/docs/getting-started.md | 16 +-- packages/docs/integrations.md | 0 packages/docs/react.md | 170 +++++++++++++++--------------- packages/docs/reactive-graphql.md | 6 +- packages/docs/redux.md | 2 +- packages/docs/vue.md | 2 +- packages/react/README.md | 92 +++++++++++++++- 9 files changed, 193 insertions(+), 103 deletions(-) delete mode 100644 packages/docs/integrations.md diff --git a/packages/core/README.md b/packages/core/README.md index 72f6bef..1e1456d 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -37,7 +37,7 @@ await subspace.init(); In addition to the web3 object, `Subspace` also accepts an `options` object with settings that can change its behavior: - `dbFilename` - Name of the database where the information will be stored (default `subspace.db`) -- `callInterval` - Interval of time in milliseconds to query a contract/address to determine changes in state or balance (default: `undefined`. Obtain data every block). +- `callInterval` - Interval of time in milliseconds to query a contract/address to determine changes in state or balance. It's only used with HttpProviders (default: `undefined`. Obtains data every block using the average block time as an interval). - `refreshLastNBlocks` - Ignores last N blocks (from current block), stored in the local db and refresh them via a web3 subscription. Useful for possible reorgs (default: `12`), - `disableSubscriptions` - Subspace by default will attempt to use websocket subscriptions if the current provider supports them, otherwise it will use polling because it asumes the provider is an HttpProvider. This functionality can be disabled by passing true to this option. (default: `undefined`) diff --git a/packages/docs/api.md b/packages/docs/api.md index ff6c620..8fce198 100644 --- a/packages/docs/api.md +++ b/packages/docs/api.md @@ -2,14 +2,14 @@ ## General -### `new Subspace(web3Provider [, options])` +### `new Subspace(web3 [, options])` Constructor. **Parameters** -1. `web3Provider` - `Object`: a valid web3 provider. +1. `web3` - `Object`: a `web3.js` object. 2. `options` - `Object` (optional): Options used to initialize Subspace - `dbFilename` - `String` (optional): Name of the database where the information will be stored (default `'subspace.db'`) - - `callInterval` - `Number` (optional): - Interval of time in milliseconds to poll a contract/address to determine changes in state or balance (default: `undefined`. Obtains data every block. If using a HttpProvider, the default is: `1000`) +- `callInterval` - Interval of time in milliseconds to query a contract/address to determine changes in state or balance. It's only used with HttpProviders (default: `undefined`. Obtains data every block using the average block time as an interval). - `refreshLastNBlocks` - Ignores last N blocks (from current block), stored in the local db and refresh them via a web3 subscription. Useful for possible reorgs (default: 12), - `disableSubscriptions` - Subspace by default will attempt to use websocket subscriptions if the current provider supports them, otherwise it will use polling because it asumes the provider is an HttpProvider. This functionality can be disabled by passing true to this option. (default: undefined) diff --git a/packages/docs/getting-started.md b/packages/docs/getting-started.md index 3aaf2e1..01e2673 100644 --- a/packages/docs/getting-started.md +++ b/packages/docs/getting-started.md @@ -22,18 +22,18 @@ const Subspace = require('@embarklabs/subspace'); ## Connecting to a web3 provider -To interact with the EVM, **Subspace** requires a valid Web3 provider. +To interact with the EVM, **Subspace** requires a valid Web3 object, connected to a provider ```js -const subspace = new Subspace(web3.currentProvider); +const subspace = new Subspace(web3); await subspace.init(); ``` In addition to the provider, `Subspace` also accepts an `options` object with settings that can change its behavior: - `dbFilename` - Name of the database where the information will be stored (default `'subspace.db'`) -- `callInterval` - Interval of time in milliseconds to query a contract/address to determine changes in state or balance (default: `undefined`. Obtains data every block). +- `callInterval` - Interval of time in milliseconds to query a contract/address to determine changes in state or balance. It's only used with HttpProviders (default: `undefined`. Obtains data every block using the average block time as an interval). - `refreshLastNBlocks` - Ignores last N blocks (from current block), stored in the local db and refresh them via a web3 subscription. Useful for possible reorgs (default: 12), -- `disableSubscriptions` - Subspace by default will attempt to use websocket subscriptions if the current provider supports them, otherwise it will use polling because it asumes the provider is an HttpProvider. This functionality can be disabled by passing true to this option. (default: undefined) +- `disableSubscriptions` - Subspace by default will attempt to use websocket subscriptions if the current provider supports them, otherwise it will use polling because it asumes the provider is an HttpProvider. This functionality can be disabled by passing true to this option. (default: `undefined`) ## Enhancing your contract objects @@ -88,13 +88,13 @@ The subscription will be triggered whenever the title changes ## Tracking events You can track events and react to their returned values. ```js -const eventObservable$ = Contract.event.eventName().track(); +const eventObservable$ = Contract.event.eventName.track(); ``` Example: ```js -const rating$ = Product.events.Rating().track().map("rating")).pipe(map(x => parseInt(x))); +const rating$ = Product.events.Rating.track().map("rating")).pipe(map(x => parseInt(x))); rating$.subscribe((rating) => console.log("rating received: " + rating)); @@ -112,7 +112,7 @@ For e.g: if you needed to get the average rating of the last 5 events: ```js import { $average, $latest } from "@embarklabs/subspace"; -const rating$ = Product.events.Rating().track().map("rating")).pipe(map(x => parseInt(x))); +const rating$ = Product.events.Rating.track().map("rating")).pipe(map(x => parseInt(x))); rating$.pipe($latest(5), $average()).subscribe((rating) => { console.log("average rating of the last 5 events is " + rating) @@ -212,6 +212,6 @@ If **Subspace** is not needed anymore, you need can invoke `close()` to dispose subspace.close(); ``` ::: warning What about subscriptions created with our observables? -Any subscription created via the tracking methods must be unsubscribed manually (in the current version). +`close()` will dispose any web3 subscription created when using a Subspace tracking method, however any subscription to an observable must still be unsubscribed manually. The npm package `subsink` can be used to clear all the observables' subscriptions at once. ::: diff --git a/packages/docs/integrations.md b/packages/docs/integrations.md deleted file mode 100644 index e69de29..0000000 diff --git a/packages/docs/react.md b/packages/docs/react.md index ea2265a..efb7729 100644 --- a/packages/docs/react.md +++ b/packages/docs/react.md @@ -1,109 +1,111 @@ # React -We provide a higher-order component to connect to enhance presentational components to react to any observable (not limited to those generated by **Subspace**). +Subspace also provides a set of components that simplifies its usage within React projects through the `@embarklabs/subspace-react` package. + +### Install +You can install it through npm or yarn: +``` +npm install --save @embarklabs/subspace-react web3 rxjs # RxJS and Web3.js are needed peer-dependencies +``` ### Usage + +#### SubspaceProvider +To use most of the `subspace-react` components, you need to wrap your app with the `` component. This will make Subspace available to any nested components that accesses it via the `useSubspace` hook or has been wrapped in the `withSubspace` higher order component. Any React component might use Subspace so it makes sense to add the provider near the top level of your dApp. The `SubspaceProvider` requires a web3 object + ```js -import { observe } from '@embarklabs/subspace/react'; +// index.js +import React from 'react' +import ReactDOM from 'react-dom' +import MyApp from './MyApp' +import { SubspaceProvider } from '@embarklabs/subspace-react'; + +const web3 = new Web3("ws://localhost:8545"); + +const rootElement = document.getElementById('root') +ReactDOM.render( + + + , + rootElement +); +``` + + +#### useSubspace +Rather than relying on global variables or passing Subspace through props, The easiest way to access Subspace features is via the `useSubspace` hook. Be sure that your entire dApp is wrapped with a `` to have it available througout the component tree. +```js +// index.js +import React from 'react' +import { useSubspace } from '@embarklabs/subspace-react'; + +const MyComponent = () => { + const subspace = useSubspace(); + + // do something.... + // subspace.trackBalance(web3.eth.defaultAccount); + + return ...; +} + +export default MyComponent +``` + + +#### withSubspace +This higher order component is provided as an alternative to the `useSubspace` hook. This injects the `subspace` property with an already initialized Subspace instance. Just like with the hook, your entire dApp needs to be wrapped with a ``. + +```js +// index.js +import React from 'react' +import { withSubspace } from '@embarklabs/subspace-react'; + +const MyComponent = (props) => { + // do something.... + // props.subspace.trackBalance(web3.eth.defaultAccount); + + return ...; +} + +export default withSubspace(MyComponent); +``` + + +#### observe + +Useful to make your component subscribe to any observable props it receives when the component is mounted and automatically unsubscribes when the component is unmounted. It can be used with any kind of observables. + + +```js +import { observe } from '@embarklabs/subspace-react'; const ObserverComponent = observe(WrappedComponent); ``` -This enhanced component will subscribe to any observable property it receives when the component is mounted and automatically unsubscribe when the component is unmounted. - -### Example - -::: tip -This example is available in [Github](https://github.com/embark-framework/subspace/tree/master/examples/react-example1) -::: - - -#### MyComponentObserver.js +##### Example usage: ```js -import React from "react"; -import ReactDOM from 'react-dom'; -import {observe} from "@embarklabs/subspace/react"; - -const MyComponent = ({eventData}) => { +const MyComponent = ({eventData}) => { // Handle initial state when no data is available if (!eventData) { return

No data

; } - return

{eventData.someReturnedValue}

+ return

Value: {eventData.someReturnValue}

}; -// MyComponent will now observe any observable prop it receives -// and update its state whenever the observable emits an event -export default observe(MyComponent); -``` -#### App.js -```js -import React, {Component} from 'react'; -import ReactDOM from 'react-dom'; -import Subspace from '@embarklabs/subspace'; +const MyEnhancedComponent = observe(MyComponent); -import MyComponentObserver from './MyComponentObserver'; -class App extends Component { - state = { - myEventObservable$: null - } - - async componentDidMount() { - const MyContractInstance = ...; // TODO: obtain a web3.eth.contract instance - - const subspace = new Subspace("wss://localhost:8545"); // Use a valid provider (geth, parity, infura...) - await subspace.init() - - const myEventObservable$ = subspace.trackEvent(MyContractInstance, "MyEvent", {filter: {}, fromBlock: 1 }); - this.setState({ myEventObservable$ }); - } - - render() { - return ; - } +const SomeOtherComponent = () => { + const myObservable$ = MyContractInstance.events.MyEvent.track({fromBlock: 1}); + return ; } - -export default App; ``` ::: warning Handling Contract Objects -The variable `MyContractInstance` is a `web3.eth.Contract` object pointing to a deployed contract address. You can use a DApp framework like [Embark](https://embark.status.im/docs/contracts_javascript.html) to easily import that contract instance: `import { MyContract } from './embarkArtifacts/contracts';`, or use web3.js directly (just like in the example [source code](https://github.com/embarklabs/subspace/blob/master/examples/react/src/MyContract.js#L36-L42)) +The variable `MyContractInstance` is a `web3.eth.Contract` object pointing to a deployed contract address that has been enhanced with `subspace.contract()`. You can use a DApp framework like [Embark](https://embark.status.im/docs/contracts_javascript.html) to easily import that contract instance: `import { MyContract } from './embarkArtifacts/contracts';`. ::: -#### index.js -```js -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -ReactDOM.render(, document.getElementById('root')); -``` - - -```js -import { observe } from "@embarklabs/subspace/react"; - -const ProductComponent = ({ maxRating, minRating, averageRating }) => { - return
    -
  • minimum rating: {minRating}
  • -
  • maximum rating: {maxRating}
  • -
  • average rating: {averageRating}
  • -
; -}; - -const ReactiveProductComponent = observe(ProductComponent); - -const Product = subspace.contract({abi, address}); -const rating$ = Product.events.Rating.track().map("rating").pipe(map(x => parseInt(x))); - -ReactDOM.render( - , - document.getElementById('hello-example') -); -``` +::: tip +To learn more about how to use `subspace-react`, there are full working examples available in [Github](https://github.com/embark-framework/subspace/tree/master/examples/) +::: diff --git a/packages/docs/reactive-graphql.md b/packages/docs/reactive-graphql.md index a1ecd53..5306ad2 100644 --- a/packages/docs/reactive-graphql.md +++ b/packages/docs/reactive-graphql.md @@ -14,7 +14,7 @@ const gql = require("graphql-tag"); const { graphql } = require("reactive-graphql"); const run = async () => { - const subspace = new Subspace(web3.currentProvider); // Use a valid provider (geth, parity, infura...) + const subspace = new Subspace(web3); await subspace.init(); const MyContractInstance = ...; // TODO: obtain a web3.eth.contract instance @@ -31,9 +31,7 @@ const run = async () => { const resolvers = { Query: { - myEvents: () => { - return subspace.trackEvent(MyContractInstance, 'MyEvent', { filter: {}, fromBlock: 1 }) - } + myEvents: () => subspace.trackEvent(MyContractInstance, 'MyEvent', { filter: {}, fromBlock: 1 }) } }; diff --git a/packages/docs/redux.md b/packages/docs/redux.md index b7f6a3d..b80bf11 100644 --- a/packages/docs/redux.md +++ b/packages/docs/redux.md @@ -19,7 +19,7 @@ import { myAction } from './actions'; const run = async () => { const MyContractInstance = ...; // TODO: obtain a web3.eth.contract instance - const subspace = new Subspace("ws://localhost:8545"); // Use a valid provider (geth, parity, infura...) + const subspace = new Subspace(web3); await subspace.init(); subspace.trackEvent(MyContractInstance, "MyEvent", {filter: {}, fromBlock: 1 }) diff --git a/packages/docs/vue.md b/packages/docs/vue.md index bed3572..e8e3ba8 100644 --- a/packages/docs/vue.md +++ b/packages/docs/vue.md @@ -56,7 +56,7 @@ export default { created: async function(){ this.MyContractInstance = ...; // TODO: obtain a web3.eth.contract instance - const subspace = new Subspace("wss://localhost:8545"); // Use a valid provider (geth, parity, infura...) + const subspace = new Subspace(web3); await subspace.init(); this.myEventObservable$ = subspace.trackEvent(this.MyContractInstance, "MyEvent", {filter: {}, fromBlock: 1 }); diff --git a/packages/react/README.md b/packages/react/README.md index fe3bb39..6e4781d 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -15,8 +15,98 @@ npm install --save @embarklabs/subspace-react web3 rxjs # RxJS and Web3.js are n ### Usage -TODO +#### SubspaceProvider +To use most of the `subspace-react` components, you need to wrap your app with the `` component. This will make Subspace available to any nested components that accesses it via the `useSubspace` hook or has been wrapped in the `withSubspace` higher order component. Any React component might use Subspace so it makes sense to add the provider near the top level of your dApp. The `SubspaceProvider` requires a web3 object +```js +// index.js +import React from 'react' +import ReactDOM from 'react-dom' +import MyApp from './MyApp' +import { SubspaceProvider } from '@embarklabs/subspace-react'; + +const web3 = new Web3("ws://localhost:8545"); + +const rootElement = document.getElementById('root') +ReactDOM.render( + + + , + rootElement +); +``` + + +#### useSubspace +Rather than relying on global variables or passing Subspace through props, The easiest way to access Subspace features is via the `useSubspace` hook. Be sure that your entire dApp is wrapped with a `` to have it available througout the component tree. +```js +// index.js +import React from 'react' +import { useSubspace } from '@embarklabs/subspace-react'; + +const MyComponent = () => { + const subspace = useSubspace(); + + // do something.... + // subspace.trackBalance(web3.eth.defaultAccount); + + return ...; +} + +export default MyComponent +``` + + +#### withSubspace +This higher order component is provided as an alternative to the `useSubspace` hook. This injects the `subspace` property with an already initialized Subspace instance. Just like with the hook, your entire dApp needs to be wrapped with a ``. + +```js +// index.js +import React from 'react' +import { withSubspace } from '@embarklabs/subspace-react'; + +const MyComponent = (props) => { + // do something.... + // props.subspace.trackBalance(web3.eth.defaultAccount); + + return ...; +} + +export default withSubspace(MyComponent); +``` + + +#### observe + +Useful to make your component subscribe to any observable props it receives when the component is mounted and automatically unsubscribes when the component is unmounted. It can be used with any kind of observables. + + +```js +import { observe } from '@embarklabs/subspace-react'; + +const ObserverComponent = observe(WrappedComponent); +``` + +##### Example usage: +```js +const MyComponent = ({eventData}) => { + // Handle initial state when no data is available + if (!eventData) { + return

No data

; + } + + return

Value: {eventData.someReturnValue}

+}; + + +const MyEnhancedComponent = observe(MyComponent); + + +const SomeOtherComponent = () => { + const myObservable$ = MyContractInstance.events.MyEvent.track({fromBlock: 1}); + return ; +} +``` ## Contribution Thank you for considering to help out with the source code! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes!