Managing IPFS Image Uploads With Angular NgRx v8

Image by Stefan Keller from PixabayManaging IPFS Image Uploads With Angular NgRx v8Part three in our series demonstrating how we are building the NgRx powered DApp for the FleaMarket Smart ContractAlex YevseyevichBlockedUnblockFollowFollowingJun 8This piece is a continuation of Part II in our blog series where we demonstrated how to connect our DApp to the IPFS node running on Infura.

In this article, we will focus on coding the IPFS image upload functionality powered by NgRx.

The source code for this module is located here.

Updating to Version 8️Begin by upgrading our FleaMarketDApp project to the latest Angular version, v8.

ng update @angular/cli @angular/core @angular/materialWe want to make sure we’re using Node.

js version 12 or later and installing the tools necessary to compile the native node modules.

We also need to update the NgRx library to its latest version, v8.

To enable the dynamic imports for lazy routes we must have themodulecompile property set to the value esnextin the tsconfig.

json file:"compilerOptions": { "baseUrl": ".

/", "outDir": ".

/dist/out-tsc", "sourceMap": true, "module": "esnext", .

One cool feature included in this version is that all effects will have automatic resubscriptions on errors.

The IPFS dependenciesIn order to interact with the IPFS network, we need to install the IPFS HTTP client library:npm install ipfs-http-clientWe need to create a custom Webpack configuration file to tell Angular to include the cryptographic node modules in the compilation.

Start by installing the custom Webpack builder:npm install -save-dev @angular-builders/custom-webpack***Note: In Angular v8 we no longer need to install the @angular-builders/dev-server package ⚠️***Then, we add an extra-webpack.

config.

js file to the project with the following context:Finally, modify theangular.

js file to include the custom builders:The IPFS File Upload ServiceLet’s create a service, IpfsDaemonService, that will handle the logic of uploading and retrieving files from the IPFS Infura network:In the service constructor, inject the ipfsToken which instantiates the global objectIpfsHttpClient that is responsible for the connection to the IPFS node that Infura runs:In the first method of the service, we add an image file to IPFS.

It takes the file object and its name and upon successful execution returns the hash of the stored file.

The second method is for reading image files from IPFS.

It takes the hash param and returns the image blob object.

We encode the raw buffer stream to a base64-encoded string.

Then we wait for the image blob to be returned by using the HttpClient with the responseType option set to “blob”.

Kudos to Brian Love for this idea.

Seeding the Feature ModuleThe IPFS file-upload logic is responsible for storing and retrieving the seller’s product image.

The hash code of the image file is stored in the ipfsHash state variable of the SafeRemotePurchase smart contract.

It is natural to bundle this functionality into the feature module SellerBoothModule.

In the code above, we are using the new import()syntax to declare the lazy-loading route.

This feature was introduced in Angular v8.

Now let’s turn our attention to the component PurchaseContractComponent that is registered in the feature moduleSellerBoothModule:This is a smart component that among other things provides the user interface for:The image previewUploading the image to IPFSRetrieving the image file from IPFS based on its hash codeLet’s take a further look at each of these.

The Image PreviewThe component’s template includes the button “Select Product Image” which lets us preview the image before uploading it to IPFS.

To wire-up the preview logic we use the angular material reactive upload form.

The image file is stored in the component variable fileBlob.

Uploading the Image File to IPFSThe state interface managed by our feature store is defined as follows:export interface State { status: FileUploadStatus; ipfsHash: string | null; imageBlob?: Blob;}To initiate the upload process, the user needs to click the button labeled “Upload to IPFS” which becomes active after we choose an image file.

This triggers the store action type [IPFS/Image] Upload that carries the value of the variable fileBlob in its payload.

The action will be dispatched to the dedicated store effect uploadFile$ which is listening for this type of action only:Let’s take a closer look at the logic inside this effect.

We use the exhaustMap operator to receive the File object from the action payload.

In this case, the advantage of using this mapping operator is that it will ignore subsequential file upload requests while the current one is still ongoing.

We then pass this value to the addFile method in the IpfsDaemonService.

Upon successful execution of this method, we use the map operator to project the received hash value to the [IPFS/Image] Upload Success action.

The action will trigger the following chain of events managed by our feature store:The action carrying the IPFS image hash payload is dispatched to the corresponding reducerThe reducer will update the state propertiesipfsHash and status with new valuesThe store selectors that observe these state changes will manage to update the component template accordinglyRetrieving Images From IPFSOnce the product image is uploaded on the IPFS network, we want to make sure we can retrieve it using its hash.

We have a button in our smart template component that becomes visible when the ipfsHash$ selector emits a hash value.

Let’s see what happens when we click on it:Clicking on the button triggers the material dialog defined in the component ShowIpfsImageComponent:Inside the component, we declare the method checkStore that returns the blob observable.

The method checks the getImageBlob selector for the state property imageBlog.

If this value is not set up yet, the method will dispatch the '[IPFS/Image] Load Image' action to broadcast the request to the effect loadFile$:The current value of the hash property is pulled from the store and passed as a parameter into the service method getFile.

The method gets resolved with the image: Blob value retrieved from the IPFS node.

We then dispatch it back to the store.

The reducer updates the state property imageBlob accordingly.

The new value will be immediately picked up by the getImageBlob selector and passed down through the pipes first using the checkStoremethod and then the tap operator:this.

image$ = this.

checkStore().

pipe( tap((blob) =>this.

image.

nativeElement.

src = this.

windowRef.

URL.

createObjectURL(blob)));In the operator tap we are using the template reference variable #ipfsImage to hook-up the blob image into the template<img> native element.

The biggest benefit of this approach is that we are now in control of the image binary stream receiving from the IPFS node.

Conclusion????.Thank you!.Please stay tuned for other pieces in this blog series.

In the meantime, you can follow me on Medium, Twitter or LinkedIn.

ReferencesSmart Contract Escrow DApp — Seller View, by Jackson NgThe Curious Marriage between IPFS & Ethereum, by Jackson NgIPFS and Angular 6, by GrandSchtroumpfEthereum DApp with Ethers.

js and IPFS using Angular, Angular Material and NgRx.

Part I, by Alex YevseyevichEthereum DApp with Ethers.

js and IPFS using Angular, Angular Material and NgRx.

Part II, by Alex YevseyevichNgRx: Action Creators redesigned, by Alex OkrushkoAngular HttpClient Blob, by Brian LoveCreating a File Upload Component in Angular, by Lukas MarxManaging File Uploads With NgRx, by Wes GrimesAngular CLI under the hood — builders demystified v2, by Evgeny BarabanovEthereum + IPFS + React DApp Tutorial, by Alexander MaUnderstanding IPFS in Depth, by vasaThe set of icons I used for merchandise images are made by Eucalyp from www.

flaticon.

com and licensed by CC 3.

0 BY????.Special thanks to my son Daniel Yevseyevich for reviewing this article.

.

. More details

Leave a Reply