Build social media platform with the Steem Blockchain! #1 — Vue.js, Storybook.js and Steem.js

in #utopian-io6 years ago (edited)

Build social media platform with the Steem Blockchain #1 — Vue.js, Storybook.js and Steem.js



Image: Fintech News

Welcome to a new series, where we will build a full blogging platform with Vue.js and Storybook, based on the Steem blockchain! This will be similar to web clients like steemit.com and busy.org, but with some different features. This was originally posted on my Medium account here.

Learn how to use Storybook.js for Vue, and how to build decoupled applications using the component/container pattern. The source for this part is available here..

This series will showcase all the best practices, patterns and tools I learned building large applciations using Vue.js, TDD and the other front tools over the last two years.

In this introduction, we will:

  1. Set up the Vue app
  2. Configure Storybook.js for Vue
  3. Fetch data from the Steem blockchain using steem.js
  4. Display data using the container/component pattern
  5. Design a <Profile> component in storybook

Why storybook? What is the container/component pattern?

Storybook helps further decouple presentational components (ones focused on just displaying things) and container components, which handle API calls, manipulate and store local state, and so on.

Storybook allows designers and developers to work together more effectively, by not getting in each other’s way. Developers spend most of their time working on business logic, API calls, and in Vuex stores, and designers tend to focus on CSS, layouts and how things looks and flow.

This also makes your applcation more testable — presentational component tests normally assert whether the props are correct, and the markup is accurate. Container components are more about calling functions, interacting with external services and manipulating data to be passed to a presentational component. Dan Abramov explains it well here.

Getting Started

We will bootstrap the app using vue-cli. If you haven’t installed that, run yarn global add vue-cli. Now, create the application with vue init webpack vue-steem. I will be building a full app with routing, unit tests [jest] and e2e tests. If you want to follow along, say yes to all the prompts when running vue init.

(Optional) Install vue-component-scaffold

I wrote a small tool npm module to generate Vue component templates and the accompanying test called vue-component-scaffold. You can read more it here if you are interested install with npm install -g vue-component-scaffold. I will be using during this series.

Install steem.js

Simply run yarn install steem  -- save. Steem is a blockchain based social media platform. We will use the API to serve profiles, posts, do upvotes, and claim rewards. The most common interface to access content is steemit.com.

Create the initial Profile route and components

Let’s create some directories for the initial /profile route, and components. We will see what these are used for soon.
mkdir src/views
mkdir src/views/profile
vc src/views/profile/Index -t
vc src/views/profile/Profile -t
vc is the command for vue-component-scaffold. If you don’t want to use it, just create empty .vue files with the same name and follow along.

Inside of views/profile/Index.vue, add:

Now edit src/router/index.js and use your newly created route.

Add the component to vue-router

Run yarn dev to start the dev server. Assuming you typed everything correctly and I didn’t make any mistakes, localhost:8080/#/profile should show you a white page with Profile!!! like this:




Profile/Index.vue, added to routing

Looking good.

We installed steem.js earlier. Let’s use that to grab some data, and then we will add storybook, and start to see container/component at work.

First, inside of /views/profile/ProfileContainer.vue, created a mounted method, in which we will make the API call and get some data about the user.. Also we will need to import the steem module.

The <script> section of ProfileContainer.vue, using steem.js to get some data for the user ‘xenetics’ (me).If you refresh the page, you should see some data printed n the console with a ton of fields. We will be working with this data.

The field we are interested in for now is json_metadata, which contains the account name, location, bio, and profile picture. Let’s display it! But first, take a step back and and consider, what is the best way to do so?

We now have two components to work with: Profile.vue and ProfileContainer.vue. The latter currently makes an API call. Another way to look at it ProfileContainer.vue is that it is a smart component, or what is often called a container component. It fetches and manipulates data. Profile.vue will be a what is often known as a presentational component — it will show some data, but should not have knowledge of where the data comes from, or it’s contents.

We will use storybook to work on our application UI — this will make the distinction between containers and components more clear.

Install and Configure Storybook.js

Install storybook by running yarn add @storybook/vue —-save. Next, inside of package.json, add:

{
  “scripts”: {
    “storybook”: “start-storybook -p 9001 -c .storybook”
  }
}

This is the storybook startup script. Now we need to create a .storybook directory, on the same level as src. Inside, add config.js with the following:

Next, inside of .storybook again, create webpack.config.js. We need to extend the default webpack config, by just a little (this setup can take a bit of work, but it’s worth it)!

Extended config to load .vue files.In the above config.js (not webpack.config.js, the one above), we did require ..src/stories which doesn’t exist. Go ahead and make a src/stories directory, and inside create index.js and profile.js. index.js will simply import the story for Profile.vue which we are about to create!

index.js simply contains:

import profile from ‘./profile’

profile.js contains the story:

If you type/copypasted all that correctly, run yarn storybook -c and visit localhost:9001, and you should see:


Our newly created storybook with an empty story for Profile.vue.

Now we can get to work. Notice in profile.js in the template property, we just render <Profile />. The Profile.vue component will not be able to make API calls, or dispatch actions, or anything fancy. Image storybook in an isolated environment, without an internet connection. The UI should have nothing to do with external services, APIs, or business logic. Just render data from props. This way, we can use storybook to see if the UI is correct, and unit tests to make sure the data and props are correct.

We want to be able to develop our presentation components, like Profile.vue completely decoupled from the application logic. Let’s start there. Inside of Profile.vue, add the following:

Now Profile.vue knows it will be receiving a profileImage, by the props, which will be a link to wherever the user’s image is stored. Inside of stories/profile.js, update the story to include an image like so:

Updated story to render some an imageI know I said storybook should operate without relying on external services! You probably are thinking “hey, Vue.js is an external service…!”, and of course you are correct. Later on, we will set up some static images saved locally. This is fine for now, to make sure storybook and our first story are set up and working correctly.
Assuming you still have the storybook server running, visit localhost:9001. You should be greeted with:

Great! Fantastic work. You just wrote your first story for a completely decoupled UI components.

Let’s go ahead and write some logic in ProfileContainer.vue, so we can use the image returned from the Steem API.

First we will update ProfileContainer.vue, data and mounted.

We added result, to save the response, and loaded. We don’t want to render <Profile> until the API call is complete. We write the result of the API call to result and set loaded to be true when the API call (hopefully) succeeds.
Next, the markup:

Nothing too exciting — just pass the profile_image property of result.profile. Now. visiting localhost:8080/#/profile

Great job! We knew this would work since we saw it in stories/profile.js, but it’s good to see it live as well.

Let’s add in the rest of the profile data. This time, I’m just going to pass the entire result.profile as a prop. In the future, I’d like to extract the avatar into a separate component, which I might use in other places on the website, such as a list of followers or next my posts, so I want to key it separate from the other data.

We only need to update the <template> section of ProfileContainer.vue:

And then add the new prop and markup in Profile.vue:



localhost:8080/#/profile should be looking like this:



Don’t forget to update stories/profile.js.



Beautiful!

Now we are set up for a large app — our data layer in ProfileContainer.vue and the presentation layer in Profile are decoupled. We also set up storybook.

The app looks pretty ugly now — but that’s fine. The developers can go ahead and add more functionality, and we can hand the repository over to the designers, who can work on the UI without even needing to make an API calls — armed with storybook, all the designers need to do is work in Profile.vue, while the developers focus on build business logic in the form of containers.

Better yet, the designers could even go ahead and make components using storybook, assuming they know what kind of data they can expect to get. Perhaps a designer could start building a Post.vue component, which formats a blog post nicely. The props are obvious enough, and when the developers can, they can hook it up to a PostContainer.vue, and pass the props to the Post.vue presentational component. I'm the only dev on this project, so I'll do all of the design and coding in upcoming articles.

The last thing to do is write some unit tests. We want to make sure the API call is made and the result assigned correctly. At that time, loaded should be set to true and reveal the <Profile> component. I won’t do that, because in the next part of this series we will move the API call to a service, which is in turn called by dispatching a Vuex action.

Next article we will improve the UI a bit, and show the followers, as well as start setting up authentication.

Thanks for reading. Any comments or questions are welcome, and again, the source for this part is available here.



Posted on Utopian.io - Rewarding Open Source Contributors

Sort:  

Nice tutorial! A lot of it went over my head of course :P But it was cool to see something like this set up -- looking forward to the rest of the series. And Storybook sounds pretty cool, will check it out!

Thanks. This is a bit more intermediate level.

If you didn't understand something, just ask and I can point you to resources. These tools and patterns are pretty common when you get to large scale web dev, so it's worth knowing about them earlier, even if you don't use them until later on in your studies.

I'd like to eventually build something similar to steemit.com but with more stats and bots - not that hard, but will take a bit of time! Hopefully it'll serve as a guide for someone moving from basics to something more larger.

Awesome, thank you! I think I might actually try playing around with the steem API today to put together a simple tool.

Great. Lmk if you need any help.

Great to see another in-depth tutorial from you @xenetics!
Sad to see you are not yet getting the amount of votes and comments you deserve, but I hope utopian-io gives it the recognition it deserves.

I hope to have time next week to follow the tutorial instructions. I'm glad you stepped away from React, which in my opinion is the most over-hyped JS platform in existence today (I have yet to see a website build on React with a for me acceptable user experience - I used to be a UI architect), and am curious to see how vue and storybook fare.

I'm glad you stepped away from React, which in my opinion is the most over-hyped JS platform in existence today (I have yet to see a website build on React with a for me acceptable user experience - I used to be a UI architect), and am curious to see how vue and storybook fare.

Well, Storybook is written ​in React. Netflix and Instagram are other applications which have been coded with React.

UX has nothing to do with the lib you are using. One can write apps with excellent UX with Vue.js and React.

I have seen platforms come and go, each with their pros and cons. I judge a platform by its ease of use for an average developer. A stellar developer can write something excellent no matter what tool they need to use. Unfortunately that doesn't apply to the majority of developers out there.
This too will pass.

Hey! Thanks for the kind words. I like React too, but I prefer Vue. After working with it fulltime for almost two years, my experience is it is easier to learn and more approachable for non developers (designers, UX people).

I'm still learning Storybook but I think it's a great tool and I want to continue this series and see how it goes.

Wonderful! I hope to dig into your tutorial example coming week to see what my experience of Vue is.

Great tutorial, xenetics. I'm currently working on a similar tutorial, but using ReactJS for it.

Very detailed; one can see that you have put considerable​ effort into it. This is the kind of content Steemit needs.

Your contribution cannot be approved because it does not follow the Utopian Rules.


Hi. Your post is good and is written with a good format, Unfortunately it does not fit the "Blog post" category, your post fits more to the "Tutorials" category.

Consider to write your post in the Tutorials category. Blog posts can't be changed to other categories yet, then, I suggest you to write a new entry in the "Tutorials" category.

If you write a new entry, you should edit the current one with "deleted" in title and body so that there are no duplicates visible in the feed.


You can contact us on Discord.
[utopian-moderator]

Please lets talk, you can send me a message via discord (link is in my profile)

Coin Marketplace

STEEM 0.28
TRX 0.12
JST 0.032
BTC 66266.06
ETH 3031.03
USDT 1.00
SBD 3.67