If you've been following the Sia project (or our blog here on Medium) then you probably know about Skynet, a decentralized storage platform built on top of the Sia blockchain. You may have also seen that, beyond just sharing data, people have been using Skynet to host entire web applications called "Sky Apps", including an impressive livestreaming platform. Maybe you wanted to build a sky app yourself.
You can do it too!
In this tutorial we'll be loosely following the skynet-workshop repo, which itself is based on our video tutorial. We'll go through and create a simple application together that takes an image from the user and generates their own media page on Skynet. It will look like this:
[screenshot of application]
The goal of this tutorial is to make your introduction to web development on Skynet as smooth as possible. However, some minimal prior experience with HTML, JS, and the command line may be required. If anything is unclear, check out MDN or ask one of us! (The author can be reached directly at marcins@nebulous.tech.)
Make sure you have the following software installed on your machine:
Let's use the command line to make a new directory for our project and enter into it:
https://gist.github.com/b5a465ec67a24d80fcb89dcd47d30ee0
Initialize an empty git
repository for version control:
https://gist.github.com/fe223fdf212deffa45afbd9387dacaf9
Now initialize a brand new NPM project in the directory:
https://gist.github.com/f32bda26f6bbeba6ebf50f61a771a3f2
Running this will bring up a series of prompts allowing you to customize your project. Accept the defaults or change them at your leisure. You can always edit your project's settings in package.json
after.
When you're done with that, there's a couple changes to make in package.json
:
https://gist.github.com/82ed3b99ed88d6f01fd586a958472232
We'll be using Webpack, a standard way of packaging web applications into standalone bundles. Let's add webpack
as a dev dependency to our project:
https://gist.github.com/ba1a92c0049a4f48c98090522ec2d86a
You'll also want to install our Browser JS SDK, which is the library that allows us to make Skynet calls in our applications:
https://gist.github.com/bb4fff6c4a3aadcad609fc2f1e26e385
You'll notice that a directory called node_modules/
was generated. This contains our app's dependencies -- generally you don't want to commit this into version control, so let's ignore it. Create a file called .gitignore
with the contents
https://gist.github.com/d716e2ea059e2ece4aade8b18886e2db
First, we want to add a CSS file for styling, so that our application looks nice. Create a folder called dist
and run the following command:
https://gist.github.com/00132d5159bb0986f93e1a6338558e61
We won't be explaining this file as it is beyond the scope of this tutorial.
Now we'll be creating the structure of the app in HTML. This will provide the layout of the page; we'll add interactivity and behavior to our app later, using Javascript.
https://gist.github.com/55af88b5af6cfe2f909c56d020b5ab06
This will create a file in dist
called index.html
. A file with this filename, if it exists, is loaded by default whenever a Sky App is opened.
Let's go through everything, piece by piece.
https://gist.github.com/2c79ccccd29e1412dfd9ac53d9e9144c
This is the start of our HTML. We provide a header, which defines the title and links to the CSS file we are using.
Exercise: Replace the title of this app with a different one.
https://gist.github.com/4023d9afcbdb4eb3285595b8abbde2d8
Next we begin the body, which defines the actual visible layout of the page. Here we provide the top-level h1
header, as well as the form that allows the user to pick a media file to upload. We give this form the #mediaFile
ID. Input forms are the only way the browser can access the user's local files.
Exercise: Modify the above code to inform the user that they must pick a .jpg
file.
Exercise: Modify the above input form so that the user can only select .jpg
files.
[Picture of what we have so far]
https://gist.github.com/de5e1b3dae771a447dd193c155ae8f20
This is a span with the ID #file-selected
. Here we will display the path of the file once it's been selected. Until then, this <span>
element is not visible.
https://gist.github.com/0a74b8d1aa0d3cf80a1ebed290c6180a
The comments here are fairly self-explanatory. The onclick
event specifies what happens when the "Create Media Page" button is clicked. It will fire the createMediaPage
function with the selected media file as input; this function is defined in the Javascript file we look at later.
Question: What happens if the user selects more than one file?
https://gist.github.com/bac30d4a15c4c0690982cd40fcd7add0
This <a>
element is another empty HTML element that is used to fill in the generated skylink after the user creates his media page.
https://gist.github.com/bcd876caf6ad3c232417d1452df9de24
Here we include two scripts. The first is main.js
, the Webpack bundle that we will generate later from our Javascript code. This script provides the createMediaPage
function that is triggered when clicking "Create Media Page".
In the second script, we select the input form with the mediaFile
ID and add an event listener: for any change in the input form, we update the text content to be the path of the selected file.
Let's create another folder called src
. Run the following to download src/index.js
:
https://gist.github.com/dcac653a1e781b1f2706cd620f42df1d
Again, we'll go through and explain every part of this code.
The first thing we do is import SkynetClient
from skynet-js
and create a new client:
https://gist.github.com/600cad4b346175c8f16f0b725e48c33a
The client can be created with additional, optional parameters, such as the address of the portal to upload to. For this example, we call SkynetClient
without any parameters to let the SDK use the portal that our sky app is running from. For more about the Skynet client, see the docs.
https://gist.github.com/3d7798e4cb5633aa2ac6fe5bb0e95bd9
Here we start defining the createMediaPage
function. The function takes a file from an input form as its only parameter.
Note: The window.
part before the function name is required in order to make the function globally accessible from a Webpack bundle.
We'll be generating a new page on Skynet that displays the media file uploaded by the user. So, the first thing we do in createMediaPage
is define what we want the new page to look like. Note that it contains complete HTML and that we include an image with the source media.jpg
. This will be the name of the user-selected media file.
Exercise: Feel free to make the generated page more interesting in any way you wish!
https://gist.github.com/d9fecc0ae7b73cdc6433f8812546f665
Here we construct the directory that we upload to Skynet. It actually consists of two files: an index.html
which contains pageContent
, and media.jpg
which is the image file uploaded by the user.
Exercise: Modify the code so that media files other than .jpg
also work. The code should transform pageContent
so that it points to a file of the correct file extension.
https://gist.github.com/764d670c61a13ec010cf650bdaceec94
Here we finally call the Skynet SDK to upload the files! First though, we create a try-catch block and an async function, since Skynet is an asynchronous library that throws errors. Then, we call the uploadDirectory
method on client
, passing in the directory of two files we created and calling it "mediaFolder"
. This returns a skylink which we then display to the user.
Exercise: Now that you've seen all of the code, here's a final exercise: Modify the app to allow the user to upload multiple files at once.
Building your application is easy:
https://gist.github.com/681eb329924f3e9370d171fe4cfcf7a0
This will generate a dist/main.js
Javascript bundle which is the source script, referenced by our dist/index.html
file.
To make your application accessible across Skynet you will need to upload it to a Skynet portal. We maintain the user-friendly siasky.net webportal but you can also use the command-line interface.
Whichever method you choose, you will want to:
- Upload the
dist
directory. - Follow the returned skylink to your app.
That's it!
That's it! We hope that this tutorial was informative and helpful. Feel free to play around with the application and make further changes, or even use it as a starting point for your own ideas.