@listenaddress @radio-alice now is a good time to start putting together all the moving pieces you both have been working on within this repository.
Goals:
- Create a global redux-esque store to hold our IPFS obj, the IPFS obj's various states (connecting, connected, connectedSuccess, error) and the storage smart contract.
- Instantiate the storage smart contract and IPFS node with right provider on page load
We can call this feature: "Creating an IPFS Storage global store". At a very high level, here's how the architecture looks:
How to get this done:
Creating any global variables that are accessible to any children components can be done through the use of react context. React provides a really nice useContext
hook for this.
Context has a Provider
and a Consumer
- the Provider
is essentially serving as that "global store" and the Consumer
is pulling values from that global store. As long as the Consumer
is rendered inside the Provider
in a react render tree - the Consumer will have access to the Provider's context.
Let's use an example from the the Discussions aragon app - where the entire discussion state is held in context.
First, we need to actually create a new "store", via createContext
: https://github.com/AutarkLabs/planning-suite/blob/dev/apps/discussions/app/modules/Discussions.js#L6
Next, (ignore all the logic in the useEffect
hook for now) and just focus on the Provider
component that is returned. Notice 2 things:
- it gets passed a
value
prop - whatever gets passed to value
then becomes available to the Consumer
child components (the value is the global store!)
- It's composed as a
wrapper
component (aka, the Provider
component renders a prop called children
). A component that renders children
just makes a react component look more like a native html component:
const Wrapper = (props) => {
return <div>{props.children}<div>
}
<Wrapper>
{...} // entire react tree can go inside Wrapper component, and it will get rendered!
</Wrapper>
It's important that our Provider
Wrapper component is high enough in this aragon client render tree because only components rendered inside of it will be valid Consumer
's of the Provider Wrapper's context. I'd recommend rendering the Wrapper component as high as possible in the render tree, right around here
Third, we want to collect data and store it as one big object, which gets passed to the Provider
Wrapper through the value
prop.
So initially, our value
object might look something like this:
const storageContextStore = {
ipfsObj = null,
connectingToIpfsObj = false,
connectedToIpfsObjSuccessfully = false,
connectedToIpfsObjFailure = false,
storageContract = null,
}
This might look something like:
import React, { createContext } from 'react'
export const IPFSStorageContext = createContext({})
export const IPFSStorageProvider = ({children}) => {
const storageContextStore = {
ipfsObj = null,
connectingToIpfsObj = false,
connectedToIpfsObjSuccessfully = false,
connectedToIpfsObjFailure = false,
storageContract = null,
}
return (
<IPFSStorageContext.Provider value={{ ...storageContextStore }}>
{children}
</IPFSStorageContext.Provider>
)
}
When we pass this storageContextStore
in as a value
prop to our Provider
Wrapper, we make it available to any children rendered inside the Provider
Wrapper.
Fourth, we need to actually fetch the data, and set the variables that are getting passed in to the Provider
's value prop. This is what the logic inside useEffect
is doing within the Discussions Context: https://github.com/AutarkLabs/planning-suite/blob/dev/apps/discussions/app/modules/Discussions.js#L14
Here's how that process can go down. Upon page load:
-
Initiate the aragon-storage contract (here's an example of how we're already doing that):
a. (Find the proxy address of the storage app)
b. Instantiate the storage contract via the instantiateStorageContract
function ,passing it the proxy address of the storage-contract and the aragon wrapper instance https://github.com/AutarkLabs/aragon/blob/feat/storage-tab/src/apps/Organization/Storage.js#L113
-
Fetch the value of the storage provider from our smart contract
-
Depending on which provider is selected, get the proper credentials from the aragon-cache (@radio-alice was working on this). Only Pinata and Temporal.Cloud require credentials
- q: how should we handle the situation when we fetch the provider from the smart contract, go get the credentials from aragon cache, but they don't exist? Should we throw an error? What should our "redux-esque" store show in terms of state?
-
If all the necessary information is fetched (including credentials), instantiate the IPFS-esque object that @radio-alice made, passing in the necessary information
-
set
the state value of the variables passed in to the Provider
Wrapper. You could use the useState
hook for this, but you could also provide more flexibility by using the useReducer
hook here.
All together, this might look something like:
import React, { useState, createContext } from 'react'
export const IPFSStorageContext = createContext({})
const initialStorageContextValue = {
ipfsObj = null,
connectingToIpfsObj = false,
connectedToIpfsObjSuccessfully = false,
connectedToIpfsObjFailure = false,
storageContract = null,
}
export const IPFSStorageProvider = ({children}) => {
const [storageContextStore, setStorageContextStore] = useState(initialStorageContextValue)
useEffect(() => {
const storage = fetchAndConfigureAllTheThings() // fetch and configure all the necessary things
setStorageContextStore(fetchAndConfigureAllTheThings)
})
return (
<IPFSStorageContext.Provider value={{ ...storageContextStore }}>
{children}
</IPFSStorageContext.Provider>
)
}
And that's it for now!
Next up we'll write a custom hook to get
and set
values in our newly created context store.