@ -0,0 +1 @@ |
|||
12 |
@ -0,0 +1 @@ |
|||
12 |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 123 KiB |
After Width: | Height: | Size: 139 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 41 KiB |
@ -0,0 +1,773 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Android SDK Tutorial (Pre-release) |
|||
{:.no_toc} |
|||
|
|||
This tutorial is written for readers who are new to either or both Blockstack |
|||
and Android to create a decentralized application. It contains the following |
|||
content: |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
This tutorial was extensively tested using Android Studio 3.1 on a MacBook Air |
|||
running High Sierra 10.13.4. If your environment is different, you may encounter |
|||
slight or even major discrepancies when performing the procedures in this |
|||
tutorial. Please [join the Blockstack community |
|||
Slack](https://slofile.com/slack/blockstack) and post questions or comments to |
|||
the `#support` channel. |
|||
|
|||
Finally, this tutorial is written for all levels from the beginner to the most |
|||
experienced. For best results, beginners should follow the guide as written. It |
|||
is expected that the fast or furiously brilliant will skip ahead and improvise |
|||
on this material at will. Fair journey one and all. |
|||
|
|||
If you prefer, you can skip working through the tutorial all together. Instead, |
|||
you can [download the final project code](images/helloandroid.zip) and import it |
|||
into Android Studio to review it. |
|||
|
|||
## Understand the sample application flow |
|||
|
|||
When complete, the sample application is a simple `hello-world` display. It is |
|||
intended for user on an Android phone. |
|||
|
|||
![](images/final-app.png) |
|||
|
|||
Only users with an existing `blockstack.id` can run your |
|||
final sample application. When complete, users interact with the sample |
|||
application by doing the following: |
|||
|
|||
![](images/app-flow.png) |
|||
|
|||
## Set up your environment |
|||
|
|||
This sample application has two code bases, a Blockstack `hello-blockstack` |
|||
application and a `hello-andriod` Android application. Before you start |
|||
developing the sample, there are a few elements you need in your environment. |
|||
|
|||
### Install Android Studio |
|||
|
|||
If you are an experienced Android developer and already have an Android |
|||
development environment on your workstation, you can use that and skip this |
|||
step. However, you will need to adjust the remaining instructions for your |
|||
environment. |
|||
|
|||
Follow the installation instructions to download and [Android Studio |
|||
3.1](https://developer.android.com/studio/install) for your operating system. |
|||
Depending on your network connection, this can take between 15-30 minutes. |
|||
|
|||
![](images/studio-download.png) |
|||
|
|||
### Do you have npm? |
|||
|
|||
The Blockstack code in this tutorial relies on the `npm` dependency manager. |
|||
Before you begin, verify you have installed `npm` using the `which` command to |
|||
verify. |
|||
|
|||
```bash |
|||
$ which npm |
|||
/usr/local/bin/npm |
|||
``` |
|||
|
|||
If you don't find `npm` in your system, [install |
|||
it](https://www.npmjs.com/get-npm). |
|||
|
|||
### Install the Blockstack test rig |
|||
|
|||
Users interact with Blockstack-enabled applications through a web browser. You |
|||
can Blockstack in test mode, on `localhost` or you can interact with completed |
|||
apps through the Blockstack webapp which is available at |
|||
[https://browser.blockstack.org/]. |
|||
|
|||
If you have already installed Blockstack for testing locally and have an |
|||
existing Blockstack ID, skip this section. Otherwise, continue onto install |
|||
Blockstack. |
|||
|
|||
1. Go to [Blockstack](https://blockstack.org/install) |
|||
|
|||
![](images/blockstack-install.png) |
|||
|
|||
2. Install the version appropriate for your operating system. |
|||
|
|||
|
|||
### Use npm to install Yeoman and the Blockstack App Generator |
|||
|
|||
You use `npm` to install Yeoman. Yeoman is a generic scaffolding system that |
|||
helps users rapidly start new projects and streamline the maintenance of |
|||
existing projects. |
|||
|
|||
|
|||
1. Install Yeoman. |
|||
|
|||
```bash |
|||
npm install -g yo |
|||
``` |
|||
2. Install the Blockstack application generator. |
|||
|
|||
```bash |
|||
npm install -g generator-blockstack |
|||
``` |
|||
|
|||
|
|||
## Build the Blockstack hello-world |
|||
|
|||
In this section, you build a Blockstack `hello-world` application. Then, you |
|||
modify the `hello-world` to interact with the Android app via a redirect. |
|||
|
|||
### Generate and launch your hello-blockstack application |
|||
|
|||
In this section, you build an initial React.js application called |
|||
`hello-blockstack`. |
|||
|
|||
1. Create a `hello-blockstack` directory. |
|||
|
|||
```bash |
|||
mkdir hello-blockstack |
|||
``` |
|||
|
|||
2. Change into your new directory. |
|||
|
|||
```bash |
|||
cd hello-blockstack |
|||
``` |
|||
|
|||
3. Use Yeoman and the Blockstack application generator to create your initial `hello-blockstack` application. |
|||
|
|||
```bash |
|||
yo blockstack:react |
|||
``` |
|||
|
|||
You should see several interactive prompts. |
|||
|
|||
```bash |
|||
$ yo blockstack:react |
|||
========================================================================== |
|||
We are constantly looking for ways to make yo better! |
|||
May we anonymously report usage statistics to improve the tool over time? |
|||
More info: https://github.com/yeoman/insight & http://yeoman.io |
|||
========================================================================== No |
|||
|
|||
_-----_ ╭──────────────────────────╮ |
|||
| | │ Welcome to the │ |
|||
|--(o)--| │ Blockstack app │ |
|||
--------- │ generator! │ |
|||
( _U_ ) ╰──────────────────────────╯ |
|||
/___A___\ / |
|||
| ~ | |
|||
__'.___.'__ |
|||
|° Y |
|||
|
|||
? Are you ready to build a Blockstack app in React? (Y/n) |
|||
``` |
|||
|
|||
4. Respond to the prompts to populate the initial app. |
|||
|
|||
After the process completes successfully, you see a prompt similar to the following: |
|||
|
|||
```bash |
|||
[fsevents] Success: |
|||
"/Users/theuser/repos/hello-blockstack/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node" |
|||
is installed via remote npm notice created a lockfile as package-lock.json. |
|||
You should commit this file. added 1060 packages in 26.901s |
|||
``` |
|||
|
|||
5. Run the initial application. |
|||
|
|||
```bash |
|||
$ npm start |
|||
|
|||
> hello-blockstack@0.0.0 start /Users/moxiegirl/repos/hello-blockstack |
|||
> webpack-dev-server |
|||
|
|||
Project is running at http://localhost:8080/ |
|||
webpack output is served from / |
|||
404s will fallback to /index.html |
|||
Hash: 4d2312ba236a4b95dc3a |
|||
Version: webpack 2.7.0 |
|||
Time: 2969ms |
|||
Asset Size Chunks Chunk Names |
|||
.... |
|||
Child html-webpack-plugin for "index.html": |
|||
chunk {0} index.html 541 kB [entry] [rendered] |
|||
[0] ./~/lodash/lodash.js 540 kB {0} [built] |
|||
[1] ./~/html-webpack-plugin/lib/loader.js!./src/index.html 533 bytes {0} [built] |
|||
[2] (webpack)/buildin/global.js 509 bytes {0} [built] |
|||
[3] (webpack)/buildin/module.js 517 bytes {0} [built] |
|||
webpack: Compiled successfully. |
|||
``` |
|||
|
|||
The system opens a browser displaying your running application. |
|||
|
|||
![](images/blockstack-signin.png) |
|||
|
|||
At this point, the browser is running a Blockstack server on your local host. |
|||
This is for testing your applications only. |
|||
|
|||
6. Choose **Sign in with Blockstack** |
|||
|
|||
The system displays a prompt allowing you to create a new Blockstack ID or restore an existing one. |
|||
|
|||
![](images/create-restore.png) |
|||
|
|||
7. Follow the prompts appropriate to your situation. |
|||
|
|||
If you are restoring an existing ID, you may see a prompt about your user |
|||
being nameless, ignore it. At this point you have only a single application |
|||
on your test server. So, you should see this single application, with your |
|||
own `blockstack.id` display name, once you are signed in: |
|||
|
|||
![](images/running-app.png) |
|||
|
|||
|
|||
### Add a redirect end point to your application |
|||
|
|||
When a user opens the webapp from the Blockstack browser on an Android phone, |
|||
you want the web app to redirect the user to your Android application. The work |
|||
you do here will allow it. |
|||
|
|||
1. From the terminal command line, change directory to the root of your sample |
|||
application directory. |
|||
|
|||
2. Use the `touch` command to add a redirect endpoint to your application. |
|||
|
|||
This endpoint on the web version of your app will redirect Android users back |
|||
to your mobile app. |
|||
|
|||
```bash |
|||
$ touch public/redirect.html |
|||
``` |
|||
|
|||
3. Open `redirect.html` and add code to the endpoint. |
|||
|
|||
```html |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Hello, Blockstack!</title> |
|||
<script> |
|||
function getParameterByName(name) { |
|||
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search); |
|||
return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); |
|||
} |
|||
|
|||
var authResponse = getParameterByName('authResponse') |
|||
window.location="myblockstackapp:" + authResponse |
|||
</script> |
|||
<body> |
|||
</body> |
|||
</html> |
|||
``` |
|||
|
|||
Blockstack apps are identified by their domain names. The endpoint will |
|||
receive a get request with the query parameter `authResponse=XXXX` and |
|||
should redirect the browser to `myblockstackapp:XXXX`. |
|||
|
|||
`myblockstackapp:` is custom protocol handler. The handler should be unique |
|||
to your application. Your app's web-based authentication uses this handler |
|||
to redirect the user back to your Android app. Later, you'll add a reference |
|||
to this handler in your Android application. |
|||
|
|||
5. Close and save the `redirect.html` file. |
|||
6. Ensure your Blockstack compiles successfully. |
|||
|
|||
## Create the hello-android project |
|||
|
|||
In this section, you'll create an Android application in Android Studio. You'll |
|||
run the application in the emulator to test it. |
|||
|
|||
### Create a simple project |
|||
|
|||
In this section, you create an inital project. You'll validate the application's |
|||
iniatial state by creating an emulator to run it in. Open Android Studio and do the following: |
|||
|
|||
|
|||
1. Open Android Studio and choose **Start a new Andriod Studio project**. |
|||
|
|||
If studio is already started, choose **File > New > New Project**. |
|||
|
|||
2. Enter these fields in the **Create Android Project** page. |
|||
|
|||
<table> |
|||
<tr> |
|||
<th>Application Name</th> |
|||
<td><code>hello-android</code></td> |
|||
</tr> |
|||
<tr> |
|||
<th>Company domain</th> |
|||
<td><code><i>USERNAME</i>.example.com</code></td> |
|||
</tr> |
|||
<tr> |
|||
<th>Project location</th> |
|||
<td><code>/Users/<i>USERNAME</i>/AndroidStudioProjects/helloandroid</code></td> |
|||
</tr> |
|||
<tr> |
|||
<th>Include Kotlin support</th> |
|||
<td>Set (checked)</td> |
|||
</tr> |
|||
</table> |
|||
|
|||
3. Press **Next** to display **Target Android Devices**. |
|||
4. Check **Phone and Tablet**. |
|||
5. Choose API 27: Andriod 8.1 (Oreo) for the target version. |
|||
6. Press **Next**. |
|||
7. Choose **Empty Activity** and press **Next**. |
|||
8. Leave the **Configure Activity** dialog with its defaults. |
|||
|
|||
![](images/configure-activity.png) |
|||
|
|||
9. Press **Finish**. |
|||
|
|||
Android studio builds your initial project. This can take a bit the first time you do it. |
|||
|
|||
![](images/initial-build.png) |
|||
|
|||
### Run the app in an emulator |
|||
|
|||
In this section, you run the appliation and create an emulator when prompted. |
|||
|
|||
1. Once the project is imported into studio, click the `app` module in the **Project** window. |
|||
|
|||
2. Then, select **Run > Run** (or click the green arrow in the toolbar). |
|||
|
|||
Studio prompts you to **Select Deployment Target**. |
|||
|
|||
3. Choose **Create New Virtual Device** and press **OK**. |
|||
|
|||
Studio prompts you to **Select Hardware**. |
|||
|
|||
4. Choose a Phone running Pixel XL. |
|||
|
|||
![](images/select-hdw.png) |
|||
|
|||
Studio prompts you for a system image. |
|||
|
|||
5. Choose **Oreo** which is API level 27 and press **Next**. |
|||
|
|||
![](images/oreo-api.png) |
|||
|
|||
Studio asks you to verify your new emulator configuration. |
|||
|
|||
6. Press **Finish**. |
|||
|
|||
The emulation takes a moment to build. Then, studio launches the emulation and opens your application. |
|||
|
|||
![](images/hello-andriod-1.png) |
|||
|
|||
|
|||
### Configure your application with the Blockstack SDK |
|||
|
|||
Now that you have created your initial project and verified it running in an emulator, you are ready to begin configuring the application for use with Blockstack. |
|||
|
|||
1. In studio, open the `AndroidManifest.xml` file. |
|||
2. Add the a `launchMode` to the `.MainActivity` definition. |
|||
|
|||
```XML |
|||
<activity android:name=".MainActivity" android:launchMode="singleTask"> |
|||
``` |
|||
|
|||
Blockstack requires that the activity that starts the sign-in process also |
|||
handles the auth reponse token. This means that the activity needs to run in |
|||
`singleTask` launch mode. |
|||
|
|||
2. Add an `<intent-filter>` with the custom handler for Blockstack. |
|||
|
|||
```XML |
|||
<intent-filter> |
|||
<action android:name="android.intent.action.VIEW" /> |
|||
<category android:name="android.intent.category.DEFAULT" /> |
|||
<category android:name="android.intent.category.BROWSABLE" /> |
|||
<data android:scheme="myblockstackapp" /> |
|||
</intent-filter> |
|||
``` |
|||
|
|||
2. Open the Project's `build.gradle` file. |
|||
3. Add the Jitpack repository `maven { url 'https://jitpack.io' }` to the `repositories` section. |
|||
|
|||
When you finish, that section looks like this: |
|||
|
|||
```JS |
|||
allprojects { |
|||
repositories { |
|||
google() |
|||
jcenter() |
|||
maven { url 'https://jitpack.io' } |
|||
} |
|||
} |
|||
``` |
|||
|
|||
4. Open the Module `build.gradle` file. |
|||
5. Set the `defaultConfig minSdkVersion` to `19`. |
|||
|
|||
When you are done, you should see (within your own username not `moxiegirl`): |
|||
|
|||
```JS |
|||
android { |
|||
compileSdkVersion 27 |
|||
defaultConfig { |
|||
applicationId "com.example.moxiegirl.hello_android" |
|||
minSdkVersion 19 |
|||
targetSdkVersion 27 |
|||
versionCode 1 |
|||
versionName "1.0" |
|||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" |
|||
} |
|||
... |
|||
} |
|||
``` |
|||
|
|||
7. Below this, add the Blockstack Android SDK dependency to your project's `dependencies` list: |
|||
|
|||
When you are done you should see: |
|||
|
|||
```JS |
|||
dependencies { |
|||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" |
|||
implementation 'com.android.support:appcompat-v7:27.1.0' |
|||
implementation 'com.android.support.constraint:constraint-layout:1.1.2' |
|||
testImplementation 'junit:junit:4.12' |
|||
androidTestImplementation 'com.android.support.test:runner:1.0.2' |
|||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' |
|||
implementation 'com.github.blockstack:blockstack-android:0.3.0' |
|||
} |
|||
|
|||
``` |
|||
|
|||
**NOTE**: Ignore the warning on the appcompat` dependencies. |
|||
|
|||
8. Sync your project. |
|||
|
|||
![](images/sync-project.png) |
|||
|
|||
Be sure to check the sync completed successfully. |
|||
|
|||
![](images/sync-success.png) |
|||
|
|||
10. Run your app in the emulator. |
|||
|
|||
You've made a lot of changes, make sure the emulator is still running |
|||
correctly. |
|||
|
|||
### Add a simple interface |
|||
|
|||
1. Open the `app/res/layout/activity_main.xml` file. |
|||
|
|||
The `activity_main.xml` file defines the graphical elements. Some elements are required before you can functionality to your `MainActivity.kt` code. |
|||
|
|||
3. Replace the entire content of the file with the following code: |
|||
|
|||
The new interface includes a `BlockstackSignInButton` which is provided by |
|||
the SDK. This SDK includes a themed "Sign in with Blockstack" button |
|||
(`BlockstackSignInButton`). You use this button in your here with the |
|||
`org.blockstack.android.sdk.ui.BlockstackSignInButton` class. |
|||
|
|||
```XML |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
tools:context=".MainActivity"> |
|||
|
|||
<org.blockstack.android.sdk.ui.BlockstackSignInButton |
|||
android:id="@+id/signInButton" |
|||
android:layout_width="307dp" |
|||
android:layout_height="45dp" |
|||
android:layout_margin="4dp" |
|||
android:layout_marginEnd="185dp" |
|||
android:layout_marginStart="39dp" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
tools:layout_editor_absoluteY="16dp" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/userDataTextView" |
|||
android:layout_width="370dp" |
|||
android:layout_height="27dp" |
|||
tools:layout_editor_absoluteX="6dp" |
|||
tools:layout_editor_absoluteY="70dp" /> |
|||
|
|||
</android |
|||
``` |
|||
|
|||
This codes adds a button and some text to your application. |
|||
|
|||
4. Choose **Run > Apply changes**. |
|||
|
|||
5. Choose **Run > Run app** in the emulator. |
|||
|
|||
The emulator now contains a new interface with a button: |
|||
|
|||
![](images/new-interface.png) |
|||
|
|||
### Add session & authentication code |
|||
|
|||
1. Open the `MainActivity.kt` file. |
|||
2. Add some additional imports to the top below the `android.os.Bundle` import. |
|||
|
|||
When you are done, your imports should appear as follows: |
|||
|
|||
```kotlin |
|||
|
|||
import android.support.v7.app.AppCompatActivity |
|||
import android.os.Bundle |
|||
|
|||
import android.support.v7.app.AppCompatActivity |
|||
import android.view.View |
|||
import kotlinx.android.synthetic.main.activity_main.* |
|||
import org.blockstack.android.sdk.BlockstackSession |
|||
import org.blockstack.android.sdk.Scope |
|||
import org.blockstack.android.sdk.UserData |
|||
import java.net.URI |
|||
``` |
|||
3. Add a variable for the Blockstack session before `onCreate`. |
|||
|
|||
```kotlin |
|||
class MainActivity : AppCompatActivity() { |
|||
|
|||
private var _blockstackSession: BlockstackSession? = null |
|||
|
|||
|
|||
override fun onCreate(savedInstanceState: Bundle?) { |
|||
super.onCreate(savedInstanceState) |
|||
setContentView(R.layout.activity_main) |
|||
} |
|||
} |
|||
``` |
|||
|
|||
4. Replace the existing the `onCreate` function with the following: |
|||
|
|||
```kotlin |
|||
override fun onCreate(savedInstanceState: Bundle?) { |
|||
super.onCreate(savedInstanceState) |
|||
setContentView(R.layout.activity_main) |
|||
|
|||
signInButton.isEnabled = false |
|||
|
|||
val appDomain = URI("https://flamboyant-darwin-d11c17.netlify.com") |
|||
val redirectURI = URI("${appDomain}/redirect") |
|||
val manifestURI = URI("${appDomain}/manifest.json") |
|||
val scopes = arrayOf(Scope.StoreWrite) |
|||
|
|||
val config = java.net.URI("https://flamboyant-darwin-d11c17.netlify.com").run { |
|||
org.blockstack.android.sdk.BlockstackConfig( |
|||
this, |
|||
redirectURI, |
|||
manifestURI, |
|||
scopes |
|||
} |
|||
|
|||
_blockstackSession = BlockstackSession(this, config, |
|||
onLoadedCallback = { |
|||
signInButton.isEnabled = true |
|||
}) |
|||
|
|||
|
|||
signInButton.setOnClickListener { view: View -> |
|||
blockstackSession().redirectUserToSignIn { userData -> |
|||
if (userData.hasValue) { |
|||
runOnUiThread { |
|||
onSignIn(userData.value!!) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
if (intent?.action == Intent.ACTION_VIEW) { |
|||
// handle the redirect from sign in |
|||
handleAuthResponse(intent) |
|||
} |
|||
} |
|||
``` |
|||
|
|||
This new `onCreate` does several things: |
|||
|
|||
* Define the initial state for the `signInButton`. |
|||
* Supply authentication information for connecting to your Blockstack app: `appDomain`, `redirectURI`, `manifestURI` and `scopes` |
|||
* Add a listener for the button click. |
|||
|
|||
Notice that the application in this example is a URI you have not set up. |
|||
Registering and application name takes time, so in time's interest you'll |
|||
use an existing app that is identical to the `hello-world` you created |
|||
earlier. For a produciton verison, you'll need to replace `appDomain`, |
|||
`redirectURI`, `manifestURI` and `scopes` with values appropriate for your |
|||
app. |
|||
|
|||
5. Add a private function to reflect when a user successfully signs in. |
|||
|
|||
```kotlin |
|||
private fun onSignIn(userData: UserData) { |
|||
userDataTextView.text = "Signed in as ${userData.decentralizedID}" |
|||
|
|||
signInButton.isEnabled = false |
|||
|
|||
} |
|||
``` |
|||
|
|||
6. Handle sign in requests with an `onNewIntent` function if the activity was already opened when signing in |
|||
|
|||
Retrieve the authentication token from the custom protocol handler call and |
|||
send it to the Blockstack session. |
|||
|
|||
```kotlin |
|||
override fun onNewIntent(intent: Intent?) { |
|||
super.onNewIntent(intent) |
|||
|
|||
if (intent?.action == Intent.ACTION_VIEW) { |
|||
handleAuthResponse(intent) |
|||
} |
|||
} |
|||
``` |
|||
|
|||
7. Create a handler for the authentication response. |
|||
|
|||
```kotlin |
|||
private fun handleAuthResponse(intent: Intent) { |
|||
val response = intent.dataString |
|||
if (response != null) { |
|||
val authResponseTokens = response.split(':') |
|||
|
|||
if (authResponseTokens.size > 1) { |
|||
val authResponse = authResponseTokens[1] |
|||
|
|||
blockstackSession().handlePendingSignIn(authResponse, { userData -> |
|||
if (userData.hasValue) { |
|||
// The user is now signed in! |
|||
runOnUiThread { |
|||
onSignIn(userData.value!!) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
8. Add the convenience method to access the blockstack session. |
|||
|
|||
```kotlin |
|||
fun blockstackSession() : BlockstackSession { |
|||
val session = _blockstackSession |
|||
if(session != null) { |
|||
return session |
|||
} else { |
|||
throw IllegalStateException("No session.") |
|||
} |
|||
} |
|||
``` |
|||
|
|||
9. Verify your final `MainActivity.kt` code looks like this: |
|||
|
|||
```kotlin |
|||
class MainActivity : AppCompatActivity() { |
|||
|
|||
private var _blockstackSession: BlockstackSession? = null |
|||
|
|||
|
|||
override fun onCreate(savedInstanceState: Bundle?) { |
|||
super.onCreate(savedInstanceState) |
|||
setContentView(R.layout.activity_main) |
|||
|
|||
signInButton.isEnabled = false |
|||
|
|||
val appDomain = URI("https://flamboyant-darwin-d11c17.netlify.com") |
|||
val redirectURI = URI("${appDomain}/redirect") |
|||
val manifestURI = URI("${appDomain}/manifest.json") |
|||
val scopes = arrayOf(Scope.StoreWrite) |
|||
|
|||
_blockstackSession = BlockstackSession(this, appDomain, redirectURI, manifestURI, scopes, |
|||
onLoadedCallback = {signInButton.isEnabled = true |
|||
}) |
|||
|
|||
|
|||
signInButton.setOnClickListener { view: View -> |
|||
blockstackSession().redirectUserToSignIn { userData -> |
|||
if (userData.hasValue) { |
|||
runOnUiThread { |
|||
onSignIn(userData) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
if (intent?.action == Intent.ACTION_VIEW) { |
|||
handleAuthResponse(intent) |
|||
} |
|||
} |
|||
|
|||
private fun onSignIn(userData: UserData) { |
|||
userDataTextView.text = "Signed in as ${userData.decentralizedID}" |
|||
|
|||
signInButton.isEnabled = false |
|||
|
|||
} |
|||
|
|||
override fun onNewIntent(intent: Intent?) { |
|||
super.onNewIntent(intent) |
|||
|
|||
if (intent?.action == Intent.ACTION_VIEW) { |
|||
handleAuthResponse(intent) |
|||
} |
|||
} |
|||
|
|||
private fun handleAuthResponse(intent: Intent) { |
|||
val response = intent.dataString |
|||
if (response != null) { |
|||
val authResponseTokens = response.split(':') |
|||
|
|||
if (authResponseTokens.size > 1) { |
|||
val authResponse = authResponseTokens[1] |
|||
|
|||
blockstackSession().handlePendingSignIn(authResponse, { userData -> |
|||
if (userData.hasValue) { |
|||
// The user is now signed in! |
|||
runOnUiThread { |
|||
onSignIn(userData.value!!) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
|
|||
fun blockstackSession() : BlockstackSession { |
|||
val session = _blockstackSession |
|||
if(session != null) { |
|||
return session |
|||
} else { |
|||
throw IllegalStateException("No session.") |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
``` |
|||
|
|||
### Run the final app in the emulator |
|||
|
|||
1. Choose **Run > Apply changes**. |
|||
2. Choose **Run > Run app** in the emulator. |
|||
3. When you see the application open, choose **Sign in with Blockstack**. |
|||
|
|||
The system prompts you how to open. |
|||
|
|||
![](images/chrome-prompt.png) |
|||
|
|||
4. Choose **Chrome** and click **ALWAYS**. |
|||
5. Move through the rest of the Chrome prompts. |
|||
|
|||
The system presents you with your final application. |
|||
|
|||
![](images/final-app.png) |
|||
|
|||
6. Work through the Blockstack prompts to login. |
|||
|
|||
|
|||
## Where to go next |
|||
|
|||
Congratulations, you've completed your Android app using the new, pre-release |
|||
Blockstack Android SDK. Thank you for trying this pre-release. Please let us |
|||
know about your experience by tweeting to |
|||
[@blockstack](https://twitter.com/blockstack). |
|||
|
|||
Learn more about Blockstack by [trying another tutorial](https://blockstack.org/tutorials). |
After Width: | Height: | Size: 179 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 179 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 123 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 139 KiB |
@ -0,0 +1,732 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Android SDK Tutorial (Pre-release) |
|||
{:.no_toc} |
|||
|
|||
This tutorial is written for readers who are new to either or both Blockstack |
|||
and Android to create a decentralized application. It contains the following |
|||
content: |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
This tutorial was extensively tested using Android Studio 3.1 on a MacBook Air |
|||
running High Sierra 10.13.4. If your environment is different, you may encounter |
|||
slight or even major discrepancies when performing the procedures in this |
|||
tutorial. Please [join the Blockstack community |
|||
Slack](https://slofile.com/slack/blockstack) and post questions or comments to |
|||
the `#support` channel. |
|||
|
|||
Finally, this tutorial is written for all levels from the beginner to the most |
|||
experienced. For best results, beginners should follow the guide as written. It |
|||
is expected that the fast or furiously brilliant will skip ahead and improvise |
|||
on this material at will. Fair journey one and all. |
|||
|
|||
If you prefer, you can skip working through the tutorial all together. Instead, |
|||
you can [download the final project code](images/helloandroid.zip) and import it |
|||
into Android Studio to review it. |
|||
|
|||
## Understand the sample application flow |
|||
|
|||
When complete, the sample application is a simple `hello-world` display. It is |
|||
intended for user on an Android phone. |
|||
|
|||
![](images/final-app.png) |
|||
|
|||
Only users with an existing `blockstack.id` can run your |
|||
final sample application. When complete, users interact with the sample |
|||
application by doing the following: |
|||
|
|||
![](images/app-flow.png) |
|||
|
|||
## Set up your environment |
|||
|
|||
This sample application has two code bases, a Blockstack `hello-blockstack` |
|||
application and a `hello-andriod` Android application. Before you start |
|||
developing the sample, there are a few elements you need in your environment. |
|||
|
|||
### Install Android Studio |
|||
|
|||
If you are an experienced Android developer and already have an Android |
|||
development environment on your workstation, you can use that and skip this |
|||
step. However, you will need to adjust the remaining instructions for your |
|||
environment. |
|||
|
|||
Follow the installation instructions to download and [Android Studio |
|||
3.1](https://developer.android.com/studio/install) for your operating system. |
|||
Depending on your network connection, this can take between 15-30 minutes. |
|||
|
|||
![](images/studio-download.png) |
|||
|
|||
### Do you have npm? |
|||
|
|||
The Blockstack code in this tutorial relies on the `npm` dependency manager. |
|||
Before you begin, verify you have installed `npm` using the `which` command to |
|||
verify. |
|||
|
|||
```bash |
|||
$ which npm |
|||
/usr/local/bin/npm |
|||
``` |
|||
|
|||
If you don't find `npm` in your system, [install |
|||
it](https://www.npmjs.com/get-npm). |
|||
|
|||
### Install the Blockstack test rig |
|||
|
|||
Users interact with Blockstack-enabled applications through a web browser. You |
|||
can Blockstack in test mode, on `localhost` or you can interact with completed |
|||
apps through the Blockstack webapp which is available at |
|||
[https://browser.blockstack.org/]. |
|||
|
|||
If you have already installed Blockstack for testing locally and have an |
|||
existing Blockstack ID, skip this section. Otherwise, continue onto install |
|||
Blockstack. |
|||
|
|||
1. Go to [Blockstack](https://blockstack.org/install) |
|||
|
|||
![](images/blockstack-install.png) |
|||
|
|||
2. Install the version appropriate for your operating system. |
|||
|
|||
|
|||
### Use npm to install Yeoman and the Blockstack App Generator |
|||
|
|||
You use `npm` to install Yeoman. Yeoman is a generic scaffolding system that |
|||
helps users rapidly start new projects and streamline the maintenance of |
|||
existing projects. |
|||
|
|||
|
|||
1. Install Yeoman. |
|||
|
|||
```bash |
|||
npm install -g yo |
|||
``` |
|||
2. Install the Blockstack application generator. |
|||
|
|||
```bash |
|||
npm install -g generator-blockstack |
|||
``` |
|||
|
|||
|
|||
## Build the Blockstack hello-world |
|||
|
|||
In this section, you build a Blockstack `hello-world` application. Then, you |
|||
modify the `hello-world` to interact with the Android app via a redirect. |
|||
|
|||
### Generate and launch your hello-blockstack application |
|||
|
|||
In this section, you build an initial React.js application called |
|||
`hello-blockstack`. |
|||
|
|||
1. Create a `hello-blockstack` directory. |
|||
|
|||
```bash |
|||
mkdir hello-blockstack |
|||
``` |
|||
|
|||
2. Change into your new directory. |
|||
|
|||
```bash |
|||
cd hello-blockstack |
|||
``` |
|||
|
|||
3. Use Yeoman and the Blockstack application generator to create your initial `hello-blockstack` application. |
|||
|
|||
```bash |
|||
yo blockstack:react |
|||
``` |
|||
|
|||
You should see several interactive prompts. |
|||
|
|||
```bash |
|||
$ yo blockstack:react |
|||
========================================================================== |
|||
We are constantly looking for ways to make yo better! |
|||
May we anonymously report usage statistics to improve the tool over time? |
|||
More info: https://github.com/yeoman/insight & http://yeoman.io |
|||
========================================================================== No |
|||
|
|||
_-----_ ╭──────────────────────────╮ |
|||
| | │ Welcome to the │ |
|||
|--(o)--| │ Blockstack app │ |
|||
--------- │ generator! │ |
|||
( _U_ ) ╰──────────────────────────╯ |
|||
/___A___\ / |
|||
| ~ | |
|||
__'.___.'__ |
|||
|° Y |
|||
|
|||
? Are you ready to build a Blockstack app in React? (Y/n) |
|||
``` |
|||
|
|||
4. Respond to the prompts to populate the initial app. |
|||
|
|||
After the process completes successfully, you see a prompt similar to the following: |
|||
|
|||
```bash |
|||
[fsevents] Success: |
|||
"/Users/theuser/repos/hello-blockstack/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node" |
|||
is installed via remote npm notice created a lockfile as package-lock.json. |
|||
You should commit this file. added 1060 packages in 26.901s |
|||
``` |
|||
|
|||
5. Run the initial application. |
|||
|
|||
```bash |
|||
$ npm start |
|||
|
|||
> hello-blockstack@0.0.0 start /Users/moxiegirl/repos/hello-blockstack |
|||
> webpack-dev-server |
|||
|
|||
Project is running at http://localhost:8080/ |
|||
webpack output is served from / |
|||
404s will fallback to /index.html |
|||
Hash: 4d2312ba236a4b95dc3a |
|||
Version: webpack 2.7.0 |
|||
Time: 2969ms |
|||
Asset Size Chunks Chunk Names |
|||
.... |
|||
Child html-webpack-plugin for "index.html": |
|||
chunk {0} index.html 541 kB [entry] [rendered] |
|||
[0] ./~/lodash/lodash.js 540 kB {0} [built] |
|||
[1] ./~/html-webpack-plugin/lib/loader.js!./src/index.html 533 bytes {0} [built] |
|||
[2] (webpack)/buildin/global.js 509 bytes {0} [built] |
|||
[3] (webpack)/buildin/module.js 517 bytes {0} [built] |
|||
webpack: Compiled successfully. |
|||
``` |
|||
|
|||
The system opens a browser displaying your running application. |
|||
|
|||
![](images/blockstack-signin.png) |
|||
|
|||
At this point, the browser is running a Blockstack server on your local host. |
|||
This is for testing your applications only. |
|||
|
|||
6. Choose **Sign in with Blockstack** |
|||
|
|||
The system displays a prompt allowing you to create a new Blockstack ID or restore an existing one. |
|||
|
|||
![](images/create-restore.png) |
|||
|
|||
7. Follow the prompts appropriate to your situation. |
|||
|
|||
If you are restoring an existing ID, you may see a prompt about your user |
|||
being nameless, ignore it. At this point you have only a single application |
|||
on your test server. So, you should see this single application, with your |
|||
own `blockstack.id` display name, once you are signed in: |
|||
|
|||
![](images/running-app.png) |
|||
|
|||
|
|||
### Add a redirect end point to your application |
|||
|
|||
When a user opens the webapp from the Blockstack browser on an Android phone, |
|||
you want the web app to redirect the user to your Android application. The work |
|||
you do here will allow it. |
|||
|
|||
1. From the terminal command line, change directory to the root of your sample |
|||
application directory. |
|||
|
|||
2. Use the `touch` command to add a redirect endpoint to your application. |
|||
|
|||
This endpoint on the web version of your app will redirect Android users back |
|||
to your mobile app. |
|||
|
|||
```bash |
|||
$ touch public/redirect.html |
|||
``` |
|||
|
|||
3. Open `redirect.html` and add code to the endpoint. |
|||
|
|||
```html |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Hello, Blockstack!</title> |
|||
<script> |
|||
function getParameterByName(name) { |
|||
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search); |
|||
return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); |
|||
} |
|||
|
|||
var authResponse = getParameterByName('authResponse') |
|||
window.location="myblockstackapp:" + authResponse |
|||
</script> |
|||
<body> |
|||
</body> |
|||
</html> |
|||
``` |
|||
|
|||
Blockstack apps are identified by their domain names. The endpoint will |
|||
receive a get request with the query parameter `authResponse=XXXX` and |
|||
should redirect the browser to `myblockstackapp:XXXX`. |
|||
|
|||
`myblockstackapp:` is custom protocol handler. The handler should be unique |
|||
to your application. Your app's web-based authentication uses this handler |
|||
to redirect the user back to your Android app. Later, you'll add a reference |
|||
to this handler in your Android application. |
|||
|
|||
5. Close and save the `redirect.html` file. |
|||
6. Ensure your Blockstack compiles successfully. |
|||
|
|||
## Create the hello-android project |
|||
|
|||
In this section, you'll create an Android application in Android Studio. You'll |
|||
run the application in the emulator to test it. |
|||
|
|||
### Create a simple project |
|||
|
|||
In this section, you create an inital project. You'll validate the application's |
|||
iniatial state by creating an emulator to run it in. Open Android Studio and do the following: |
|||
|
|||
|
|||
1. Open Android Studio and choose **Start a new Andriod Studio project**. |
|||
|
|||
If studio is already started, choose **File > New > New Project**. |
|||
|
|||
2. Enter these fields in the **Create Android Project** page. |
|||
|
|||
<table class="uk-table"> |
|||
<tr> |
|||
<th>Application Name</th> |
|||
<td><code>hello-android</code></td> |
|||
</tr> |
|||
<tr> |
|||
<th>Company domain</th> |
|||
<td><code><i>USERNAME</i>.example.com</code></td> |
|||
</tr> |
|||
<tr> |
|||
<th>Project location</th> |
|||
<td><code>/Users/<i>USERNAME</i>/AndroidStudioProjects/helloandroid</code></td> |
|||
</tr> |
|||
<tr> |
|||
<th>Include Kotlin support</th> |
|||
<td>Set (checked)</td> |
|||
</tr> |
|||
</table> |
|||
|
|||
3. Press **Next** to display **Target Android Devices**. |
|||
4. Check **Phone and Tablet**. |
|||
5. Choose API 27: Andriod 8.1 (Oreo) for the target version. |
|||
6. Press **Next**. |
|||
7. Choose **Empty Activity** and press **Next**. |
|||
8. Leave the **Configure Activity** dialog with its defaults. |
|||
|
|||
![](images/configure-activity.png) |
|||
|
|||
9. Press **Finish**. |
|||
|
|||
Android studio builds your initial project. This can take a bit the first time you do it. |
|||
|
|||
![](images/initial-build.png) |
|||
|
|||
### Run the app in an emulator |
|||
|
|||
In this section, you run the appliation and create an emulator when prompted. |
|||
|
|||
1. Once the project is imported into studio, click the `app` module in the **Project** window. |
|||
|
|||
2. Then, select **Run > Run** (or click the green arrow in the toolbar). |
|||
|
|||
Studio prompts you to **Select Deployment Target**. |
|||
|
|||
3. Choose **Create New Virtual Device** and press **OK**. |
|||
|
|||
Studio prompts you to **Select Hardware**. |
|||
|
|||
4. Choose a Phone running Pixel XL. |
|||
|
|||
![](images/select-hdw.png) |
|||
|
|||
Studio prompts you for a system image. |
|||
|
|||
5. Choose **Oreo** which is API level 27 and press **Next**. |
|||
|
|||
![](images/oreo-api.png) |
|||
|
|||
Studio asks you to verify your new emulator configuration. |
|||
|
|||
6. Press **Finish**. |
|||
|
|||
The emulation takes a moment to build. Then, studio launches the emulation and opens your application. |
|||
|
|||
![](images/hello-andriod-1.png) |
|||
|
|||
|
|||
### Configure your application with the Blockstack SDK |
|||
|
|||
Now that you have created your initial project and verified it running in an emulator, you are ready to begin configuring the application for use with Blockstack. |
|||
|
|||
1. In studio, open the `AndroidManifest.xml` file. |
|||
|
|||
2. Add an `<intent-filter>` with the custom handler for Blockstack. |
|||
|
|||
```XML |
|||
<intent-filter> |
|||
<action android:name="android.intent.action.VIEW" /> |
|||
<category android:name="android.intent.category.DEFAULT" /> |
|||
<category android:name="android.intent.category.BROWSABLE" /> |
|||
<data android:scheme="myblockstackapp" /> |
|||
</intent-filter> |
|||
``` |
|||
|
|||
2. Open the Project's `build.gradle` file. |
|||
3. Add the Jitpack repository `maven { url 'https://jitpack.io' }` to the `repositories` section. |
|||
|
|||
When you finish, that section looks like this: |
|||
|
|||
```JS |
|||
allprojects { |
|||
repositories { |
|||
google() |
|||
jcenter() |
|||
maven { url 'https://jitpack.io' } |
|||
} |
|||
} |
|||
``` |
|||
|
|||
4. Open the Module `build.gradle` file. |
|||
5. Set the `defaultConfig minSdkVersion` to `19`. |
|||
|
|||
When you are done, you should see (within your own username not `moxiegirl`): |
|||
|
|||
```JS |
|||
android { |
|||
compileSdkVersion 27 |
|||
defaultConfig { |
|||
applicationId "com.example.moxiegirl.hello_android" |
|||
minSdkVersion 19 |
|||
targetSdkVersion 27 |
|||
versionCode 1 |
|||
versionName "1.0" |
|||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" |
|||
} |
|||
... |
|||
} |
|||
``` |
|||
|
|||
7. Below this, add the Blockstack Android SDK dependency to your project's `dependencies` list: |
|||
|
|||
When you are done you should see: |
|||
|
|||
```JS |
|||
dependencies { |
|||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" |
|||
implementation 'com.android.support:appcompat-v7:27.1.1' |
|||
implementation 'com.android.support.constraint:constraint-layout:1.1.3' |
|||
implementation 'com.android.support:design:27.1.1' |
|||
testImplementation 'junit:junit:4.12' |
|||
androidTestImplementation 'com.android.support.test:runner:1.0.2' |
|||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' |
|||
implementation 'com.github.blockstack:blockstack-android:0.4.0' |
|||
} |
|||
|
|||
``` |
|||
|
|||
**NOTE**: Ignore the warning on the appcompat` dependencies. |
|||
|
|||
8. Sync your project. |
|||
|
|||
![](images/sync-project.png) |
|||
|
|||
Be sure to check the sync completed successfully. |
|||
|
|||
![](images/sync-success.png) |
|||
|
|||
10. Run your app in the emulator. |
|||
|
|||
You've made a lot of changes, make sure the emulator is still running |
|||
correctly. |
|||
|
|||
### Add a simple interface |
|||
|
|||
1. Open the `app/res/layout/activity_main.xml` file. |
|||
|
|||
The `activity_main.xml` file defines the graphical elements. Some elements are required before you can functionality to your `MainActivity.kt` code. |
|||
|
|||
3. Replace the entire content of the file with the following code: |
|||
|
|||
The new interface includes a `BlockstackSignInButton` which is provided by |
|||
the SDK. This SDK includes a themed "Sign in with Blockstack" button |
|||
(`BlockstackSignInButton`). You use this button here with the |
|||
`org.blockstack.android.sdk.ui.BlockstackSignInButton` class. |
|||
|
|||
```XML |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
tools:context=".MainActivity"> |
|||
|
|||
<org.blockstack.android.sdk.ui.BlockstackSignInButton |
|||
android:id="@+id/signInButton" |
|||
android:layout_width="307dp" |
|||
android:layout_height="45dp" |
|||
android:layout_margin="4dp" |
|||
android:layout_marginEnd="185dp" |
|||
android:layout_marginStart="39dp" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
tools:layout_editor_absoluteY="16dp" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/userDataTextView" |
|||
android:layout_width="370dp" |
|||
android:layout_height="27dp" |
|||
tools:layout_editor_absoluteX="6dp" |
|||
tools:layout_editor_absoluteY="70dp" /> |
|||
|
|||
</android> |
|||
``` |
|||
|
|||
This codes adds a button and some text to your application. |
|||
|
|||
4. Choose **Run > Apply changes**. |
|||
|
|||
5. Choose **Run > Run app** in the emulator. |
|||
|
|||
The emulator now contains a new interface with a button: |
|||
|
|||
![](images/new-interface.png) |
|||
|
|||
### Add session & authentication code |
|||
|
|||
1. Open the `MainActivity.kt` file. |
|||
2. Add some additional imports to the top below the `android.os.Bundle` import. |
|||
|
|||
When you are done, your imports should appear as follows: |
|||
|
|||
```kotlin |
|||
import android.content.Intent |
|||
import android.os.Bundle |
|||
import android.support.v7.app.AppCompatActivity |
|||
import android.view.View |
|||
import kotlinx.android.synthetic.main.activity_main.* |
|||
import org.blockstack.android.sdk.BlockstackSession |
|||
import org.blockstack.android.sdk.Scope |
|||
import org.blockstack.android.sdk.UserData |
|||
import org.blockstack.android.sdk.toBlockstackConfig |
|||
``` |
|||
3. Add a variable for the Blockstack session before `onCreate`. |
|||
|
|||
```kotlin |
|||
class MainActivity : AppCompatActivity() { |
|||
|
|||
private var _blockstackSession: BlockstackSession? = null |
|||
|
|||
override fun onCreate(savedInstanceState: Bundle?) { |
|||
super.onCreate(savedInstanceState) |
|||
setContentView(R.layout.activity_main) |
|||
} |
|||
} |
|||
``` |
|||
|
|||
4. Replace the existing the `onCreate` function with the following: |
|||
|
|||
```kotlin |
|||
override fun onCreate(savedInstanceState: Bundle?) { |
|||
super.onCreate(savedInstanceState) |
|||
setContentView(R.layout.activity_main) |
|||
|
|||
val scopes = arrayOf(Scope.StoreWrite) |
|||
val config = "https://flamboyant-darwin-d11c17.netlify.com" |
|||
.toBlockstackConfig(scopes) |
|||
|
|||
_blockstackSession = BlockstackSession(this, config) |
|||
|
|||
signInButton.isEnabled = true |
|||
|
|||
signInButton.setOnClickListener { view: View -> |
|||
blockstackSession().redirectUserToSignIn { |
|||
// only called on error |
|||
} |
|||
} |
|||
if (intent?.action == Intent.ACTION_VIEW) { |
|||
// handle the redirect from sign in |
|||
handleAuthResponse(intent) |
|||
} |
|||
} |
|||
``` |
|||
|
|||
This new `onCreate` does several things: |
|||
|
|||
* Define the initial state for the `signInButton`. |
|||
* Supply authentication information for connecting to your Blockstack app: `appDomain` and `scopes` (for `redirectURI`, `manifestURI` the default values are used) |
|||
* Add a listener for the button click. |
|||
|
|||
Notice that the application in this example is a URI you have not set up. |
|||
Registering and application name takes time, so in time's interest you'll |
|||
use an existing app that is identical to the `hello-world` you created |
|||
earlier. For a production version, you'll need to replace `appDomain`, |
|||
`redirectURI`, `manifestURI` and `scopes` with values appropriate for your |
|||
app. |
|||
|
|||
5. Add a private function to reflect when a user successfully signs in. |
|||
|
|||
```kotlin |
|||
private fun onSignIn(userData: UserData) { |
|||
userDataTextView.text = "Signed in as ${userData.decentralizedID}" |
|||
signInButton.isEnabled = false |
|||
} |
|||
``` |
|||
|
|||
6. Handle sign in requests with an `onNewIntent` function if the activity was already opened when signing in |
|||
|
|||
Retrieve the authentication token from the custom protocol handler call and |
|||
send it to the Blockstack session. |
|||
|
|||
```kotlin |
|||
override fun onNewIntent(intent: Intent?) { |
|||
super.onNewIntent(intent) |
|||
|
|||
if (intent?.action == Intent.ACTION_VIEW) { |
|||
handleAuthResponse(intent) |
|||
} |
|||
} |
|||
``` |
|||
|
|||
7. Create a handler for the authentication response. |
|||
|
|||
```kotlin |
|||
private fun handleAuthResponse(intent: Intent) { |
|||
val response = intent.dataString |
|||
if (response != null) { |
|||
val authResponseTokens = response.split(':') |
|||
|
|||
if (authResponseTokens.size > 1) { |
|||
val authResponse = authResponseTokens[1] |
|||
|
|||
blockstackSession().handlePendingSignIn(authResponse, { userData -> |
|||
if (userData.hasValue) { |
|||
// The user is now signed in! |
|||
runOnUiThread { |
|||
onSignIn(userData.value!!) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
8. Add the convenience method to access the blockstack session. |
|||
|
|||
```kotlin |
|||
fun blockstackSession() : BlockstackSession { |
|||
val session = _blockstackSession |
|||
if(session != null) { |
|||
return session |
|||
} else { |
|||
throw IllegalStateException("No session.") |
|||
} |
|||
} |
|||
``` |
|||
|
|||
9. Verify your final `MainActivity.kt` code looks like this: |
|||
|
|||
```kotlin |
|||
class MainActivity : AppCompatActivity() { |
|||
|
|||
private var _blockstackSession: BlockstackSession? = null |
|||
|
|||
override fun onCreate(savedInstanceState: Bundle?) { |
|||
super.onCreate(savedInstanceState) |
|||
setContentView(R.layout.activity_main) |
|||
|
|||
signInButton.isEnabled = false |
|||
|
|||
val scopes = arrayOf(Scope.StoreWrite) |
|||
val config = "https://flamboyant-darwin-d11c17.netlify.com" |
|||
.toBlockstackConfig(scopes) |
|||
|
|||
_blockstackSession = BlockstackSession(this, config) |
|||
signInButton.isEnabled = true |
|||
|
|||
signInButton.setOnClickListener { view: View -> |
|||
blockstackSession().redirectUserToSignIn { |
|||
// only called on error |
|||
} |
|||
} |
|||
if (intent?.action == Intent.ACTION_VIEW) { |
|||
handleAuthResponse(intent) |
|||
} |
|||
} |
|||
|
|||
private fun onSignIn(userData: UserData) { |
|||
userDataTextView.text = "Signed in as ${userData.decentralizedID}" |
|||
|
|||
signInButton.isEnabled = false |
|||
} |
|||
|
|||
override fun onNewIntent(intent: Intent?) { |
|||
super.onNewIntent(intent) |
|||
|
|||
if (intent?.action == Intent.ACTION_VIEW) { |
|||
handleAuthResponse(intent) |
|||
} |
|||
} |
|||
|
|||
private fun handleAuthResponse(intent: Intent) { |
|||
val response = intent.dataString |
|||
if (response != null) { |
|||
val authResponseTokens = response.split(':') |
|||
|
|||
if (authResponseTokens.size > 1) { |
|||
val authResponse = authResponseTokens[1] |
|||
|
|||
blockstackSession().handlePendingSignIn(authResponse, { userData -> |
|||
if (userData.hasValue) { |
|||
// The user is now signed in! |
|||
runOnUiThread { |
|||
onSignIn(userData.value!!) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
|
|||
fun blockstackSession() : BlockstackSession { |
|||
val session = _blockstackSession |
|||
if(session != null) { |
|||
return session |
|||
} else { |
|||
throw IllegalStateException("No session.") |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Run the final app in the emulator |
|||
|
|||
1. Choose **Run > Apply changes**. |
|||
2. Choose **Run > Run app** in the emulator. |
|||
3. When you see the application open, choose **Sign in with Blockstack**. |
|||
|
|||
The system prompts you how to open. |
|||
|
|||
![](images/chrome-prompt.png) |
|||
|
|||
4. Choose **Chrome** and click **ALWAYS**. |
|||
5. Move through the rest of the Chrome prompts. |
|||
|
|||
The system presents you with your final application. |
|||
|
|||
![](images/final-app.png) |
|||
|
|||
6. Work through the Blockstack prompts to login. |
|||
|
|||
|
|||
## Where to go next |
|||
|
|||
Congratulations, you've completed your Android app using the new, pre-release |
|||
Blockstack Android SDK. Thank you for trying this pre-release. Please let us |
|||
know about your experience by tweeting to |
|||
[@blockstack](https://twitter.com/blockstack). |
|||
|
|||
Learn more about Blockstack by [trying another tutorial](https://blockstack.org/tutorials). |
@ -0,0 +1 @@ |
|||
12 |
@ -0,0 +1 @@ |
|||
12 |
After Width: | Height: | Size: 37 KiB |
@ -0,0 +1,330 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Hello, Blockstack Tutorial |
|||
|
|||
In this tutorial, you generate a simple application on Blockstack. The application |
|||
is a single-page application (SPA) that runs completely client-side. The |
|||
application has no backend API to talk to, other than the identity and storage |
|||
API that the user provides. In this sense, the application is a completely |
|||
decentralized, server-less application. You work through the following sections: |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
## About this tutorial and the prerequisites you need |
|||
|
|||
For this tutorial, we will use the following tools: |
|||
|
|||
- `npm` to manage dependencies and scripts |
|||
- `browserify` to compile node code into browser-ready code |
|||
- `blockstack.js` to authenticate the user and work with the user's identity/profile information |
|||
|
|||
At minimum, Blockstack requires macOS High Sierra. This tutorial was written for |
|||
a user running macOS High Sierra 10.13.4. The application you build is a |
|||
React.js application that is completely decentralized and server-less. While |
|||
not strictly required to follow along, basic familiarity with React.js is |
|||
helpful. |
|||
|
|||
When complete, the app is capable of the following: |
|||
|
|||
- authenticating users using Blockstack |
|||
- posting new statuses |
|||
- displaying statuses in the user profile |
|||
- looking up the profiles and statuses of other users |
|||
|
|||
The basic identity and storage services are provided by `blockstack.js`. To test |
|||
the application, you need to have already registered a Blockstack ID. |
|||
|
|||
The tutorial relies on the `npm` dependency manager. Before you begin, verify |
|||
you have installed `npm` using the `which` command to verify. |
|||
|
|||
```bash |
|||
$ which npm |
|||
/usr/local/bin/npm |
|||
``` |
|||
|
|||
If you don't find `npm` in your system, [install |
|||
it](https://www.npmjs.com/get-npm). |
|||
|
|||
Finally, make sure you have [created at least one Blockstack ID]({{ site.baseurl }}/browser/ids-introduction.html#create-an-initial-blockstack-id). You'll use this ID to interat with the application. |
|||
|
|||
## Use npm to install Yeoman and the Blockstack App Generator |
|||
|
|||
You use `npm` to install Yeoman. Yeoman is a generic scaffolding system that |
|||
helps users rapidly start new projects and streamline the maintenance of |
|||
existing projects. |
|||
|
|||
|
|||
1. Install Yeoman. |
|||
|
|||
```bash |
|||
npm install -g yo |
|||
``` |
|||
2. Install the Blockstack application generator. |
|||
|
|||
```bash |
|||
npm install -g generator-blockstack |
|||
``` |
|||
|
|||
## Generate an initial Blockstack application |
|||
|
|||
In this section, you build an initial React.js application called `hello-world-tutorial`. |
|||
|
|||
1. Create the `hello-world-tutorial` directory. |
|||
|
|||
```bash |
|||
mkdir hello-world-tutorial |
|||
``` |
|||
|
|||
2. Change into your new directory. |
|||
|
|||
```bash |
|||
cd hello-world-tutorial |
|||
``` |
|||
|
|||
3. Use Yeoman and the Blockstack application generator to create your initial `hello-world-tutorial` application. |
|||
|
|||
```bash |
|||
yo blockstack |
|||
``` |
|||
|
|||
You should see several interactive prompts. |
|||
|
|||
```bash |
|||
$ yo blockstack |
|||
|
|||
_-----_ ╭──────────────────────────╮ |
|||
| | │ Welcome to the │ |
|||
|--(o)--| │ Blockstack app │ |
|||
`---------´ │ generator! │ |
|||
( _´U`_ ) ╰──────────────────────────╯ |
|||
/___A___\ / |
|||
| ~ | |
|||
__'.___.'__ |
|||
´ ` |° ´ Y ` |
|||
|
|||
? Are you ready to build a Blockstack app in React? (Y/n) |
|||
``` |
|||
|
|||
4. Respond to the prompts to populate the initial app. |
|||
|
|||
After the process completes successfully, you see a prompt similar to the following: |
|||
|
|||
```bash |
|||
... |
|||
create public/icon-192x192.png |
|||
create public/index.html |
|||
create public/robots.txt |
|||
create public/manifest.json |
|||
|
|||
|
|||
I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself. |
|||
``` |
|||
|
|||
Depending on your environment you may have some problems with the `npm` packages. Go ahead and fix these before continuing to the next section. |
|||
|
|||
## Review the basic application structure |
|||
|
|||
The initial application you create is a generic Javascript application you run |
|||
with a local express node. Before you continue, take a moment to examine the |
|||
structure of this generic application structure: |
|||
|
|||
| File | Description | |
|||
|------------------|-----------------------------------| |
|||
| .editorconfig | Sets universal values for editor. | |
|||
| .gitignore | Git configuration file. | |
|||
| firebase.json | Configuragion for mobile application.| |
|||
| package.json | Specifies required packages. | |
|||
| requires.js | A Javascript module loader. | |
|||
| server.js | Simple static server configuration.| |
|||
|
|||
In the `public` folder you find these files: |
|||
|
|||
| File | Description | |
|||
|------------------|-----------------------------------| |
|||
| app.css | Contains application styles. | |
|||
| app.js | Main application file. | |
|||
| boostrap.min.css | Minifield css for production. | |
|||
| icon-192x192.png | Application icon | |
|||
| index.html | Single page. | |
|||
| manifest.json | Tells the browser about the application and how it should behave.| |
|||
| robots.txt | Configures crawling and indexing. | |
|||
|
|||
The simple static file server in the `server.js`file serves all of the files in |
|||
the `/public` directory, including `index.html`, `app.js`, `bootstrap.min.css` |
|||
and `app.css`. The main file of the application is in the `app.js`. It contains |
|||
the majority of the application logic. |
|||
|
|||
## Start the server and view the application |
|||
|
|||
When you start the server, it will create a Node.js server, start it locally, |
|||
and open your browser 'http://localhost:5000'. From the root of your new application directory: |
|||
|
|||
1. Start the application server. |
|||
|
|||
```bash |
|||
npm start |
|||
``` |
|||
|
|||
The first time you run it, your system prompts you to accept incoming connections. |
|||
|
|||
![Network Connection](images/network-connections.gif) |
|||
|
|||
2. Choose **Allow**. |
|||
|
|||
3. Open your browser to `http://localhost:8080`. |
|||
|
|||
You should see a simple application: |
|||
|
|||
![](images/initial-app.gif) |
|||
|
|||
4. Choose **Sign In with Blockstack**. |
|||
|
|||
The application detects whether the user has the Blockstack client edition installed or |
|||
not. This is done automatically by the Blockstack API, more about this later. |
|||
What the browser displays depends on the users' current state. |
|||
|
|||
| Using web app | Has client edition installed | |
|||
|------------------|-----------------------------------| |
|||
| ![](images/login-choice.png) | ![](images/login.gif) | |
|||
|
|||
If the user logged into the Blockstack Browser but not reset it, the user can |
|||
simply use the exiting identity. |
|||
|
|||
![](images/login-no-auth.png) |
|||
|
|||
If the user chooses **Deny**, the Blockstack Browser displays its |
|||
**Home** page but the user is not logged into the sample application. |
|||
|
|||
5. Leave your new application running and move onto the next section. |
|||
|
|||
## Understand the generated application code |
|||
|
|||
In this section, you look at the basic application generated with the `yo |
|||
blockstack` command. The generated code contains simple authentication methods |
|||
that allow a user to log into the browser. The main application code is located |
|||
in the `public/app.css` file. Open this file now. |
|||
|
|||
All of the code in the file is wrapped in an event |
|||
listener. |
|||
|
|||
```js |
|||
document.addEventListener("DOMContentLoaded", function(event) { |
|||
}) |
|||
``` |
|||
|
|||
This listener that waits until the DOM content is loaded. Then, it creates an auth request and redirects the user to sign in: |
|||
|
|||
```js |
|||
document.getElementById('signin-button').addEventListener('click', function() { |
|||
blockstack.redirectUserToSignIn() |
|||
}) |
|||
``` |
|||
|
|||
You can find the `redirectUserToSignIn()` function is part of the [Blockstack Javascript documentation](https://blockstack.github.io/blockstack.js/). There is also a sign out button handler. This handler deletes the local user data and signs the user out: |
|||
|
|||
```js |
|||
document.getElementById('signout-button').addEventListener('click', function() { |
|||
blockstack.signUserOut(window.location.origin) |
|||
}) |
|||
``` |
|||
|
|||
The handlers are followed by a `showProfile()` function for showing the user's profile: |
|||
|
|||
```js |
|||
function showProfile(profile) { |
|||
var person = new blockstack.Person(profile) |
|||
document.getElementById('heading-name').innerHTML = person.name() ? person.name() : "Nameless Person" |
|||
if(person.avatarUrl()) { |
|||
document.getElementById('avatar-image').setAttribute('src', person.avatarUrl()) |
|||
} |
|||
document.getElementById('section-1').style.display = 'none' |
|||
document.getElementById('section-2').style.display = 'block' |
|||
} |
|||
``` |
|||
|
|||
Each `getElementById()` function refers to elemments in the `index.html` file. |
|||
|
|||
Once a user is successfully signed in, there is logic for loading the user |
|||
profile and displaying the application. As illustrated earlier, there are |
|||
several states the user can be in: |
|||
|
|||
- The user is already signed in |
|||
- The user has a pending sign in request |
|||
- The user is signed out |
|||
|
|||
The application handles these situtations as followed: |
|||
|
|||
```js |
|||
if (blockstack.isUserSignedIn()) { |
|||
var profile = blockstack.loadUserData().profile |
|||
showProfile(profile) |
|||
} else if (blockstack.isSignInPending()) { |
|||
blockstack.handlePendingSignIn().then(function(userData) { |
|||
window.location = window.location.origin |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
When the user is signed in, Blockstack loads the user data from local storage |
|||
and displays the profile with the `showProfile()` function. When the user has a |
|||
pending sign in request, the appplication signs the user in and redirects the |
|||
user back to the home page. |
|||
|
|||
### Application manifest |
|||
|
|||
The application's `/public/manifest.json` file configures your app. The |
|||
configurations dictate how the application is displayed in auth views and on |
|||
user home screens. The contents are very simple: |
|||
|
|||
```json |
|||
{ |
|||
"name": "Hello, Blockstack", |
|||
"start_url": "localhost:5000", |
|||
"description": "A simple demo of Blockstack Auth", |
|||
"icons": [{ |
|||
"src": "https://helloblockstack.com/icon-192x192.png", |
|||
"sizes": "192x192", |
|||
"type": "image/png" |
|||
}] |
|||
} |
|||
``` |
|||
|
|||
Keep it as is or fill it in with new information that describes your app. |
|||
|
|||
### Save your application code |
|||
|
|||
Complete the tutorial by storing your app code on GitHub. Before you begin, make sure you have a GitHub account and have configured your environment to use it. |
|||
|
|||
1. Initialize the application code as a Git repo. |
|||
|
|||
```bash |
|||
git init |
|||
``` |
|||
|
|||
2. Add and commit all of the files: |
|||
|
|||
```bash |
|||
git add . && git commit -m "first commit" |
|||
``` |
|||
|
|||
3. In GitHub, create a `hello-blockstack` repository. |
|||
|
|||
4. Back in your termininal window, add a remote for GitHub. |
|||
|
|||
Make sure to fill in your username: |
|||
|
|||
```bash |
|||
git remote add origin git@github.com:YOUR_USERNAME_HERE/hello-blockstack.git |
|||
``` |
|||
|
|||
5. Push your new code to the master branch of the remote repo: |
|||
|
|||
``` |
|||
git push origin master |
|||
``` |
|||
|
|||
You're done! You just built your first Blockstack app and shipped the code. |
|||
You're well on your way to becoming a Blockstack app legend. |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 24 KiB |
@ -0,0 +1,27 @@ |
|||
# Tutorials |
|||
|
|||
**Note: This data is being digested by blockstack.org. Do not change the formatting of this list unless you first make an adjustment to the code on blockstack.org.** |
|||
|
|||
### Multi-player Storage |
|||
|
|||
- urlSlug: multi-player-storage |
|||
- image: /images/tutorials/multi-player-storage.png |
|||
- description: Build a decentralized micro-blogging app using multi-player Gaia storage. |
|||
|
|||
### Managing Data with Gaia |
|||
|
|||
- urlSlug: managing-data-with-gaia |
|||
- image: /images/tutorials/managing-data-with-gaia.png |
|||
- description: This series will focus on teaching you to think like a Blockstack developer working with Gaia. |
|||
|
|||
### Blockstack Todo |
|||
|
|||
- urlSlug: todo-list |
|||
- image: /images/tutorials/todo-list.png |
|||
- description: Walk through creating a basic Todo application with Blockstack. Learn about Sign In flow and Gaia storage. |
|||
|
|||
### Hello Blockstack |
|||
|
|||
- urlSlug: hello-blockstack |
|||
- image: /images/tutorials/hello-blockstack.jpg |
|||
- description: Build a simple single-page JavaScript application that runs completely client-side without any servers. |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 62 KiB |
@ -0,0 +1,328 @@ |
|||
--- |
|||
title: Tour of a Blockstack application |
|||
description: Walk through creating a basic Todo application with Blockstack. Learn about Sign In flow and Gaia storage. |
|||
image: /images/article-photos/chalkboard.jpg |
|||
youtube: https://www.youtube.com/embed/oyvg-h0obFw |
|||
--- |
|||
|
|||
In this tutorial, you build the code for and run a single-page application (SPA) |
|||
with Blockstack and Vue.js. Once the application is running, you take a tour |
|||
through the applications' Blockstack functionality. You'll learn how it manages |
|||
authentiation using a Blockstack ID and how it stores information associated |
|||
with that ID using Blockstack Storage (Gaia). |
|||
|
|||
## Prerequisites |
|||
|
|||
Make sure you have [created at least one Blockstack ID](ids-introduction#create-an-initial-blockstack-id). You'll use this ID to Finteract with the Todo application. |
|||
|
|||
The applicaton code relies on both the `npm` and the `yarn` package managers. |
|||
Before you begin, verify you have these tools `npm` using the `which` command to |
|||
verify. |
|||
|
|||
```bash |
|||
$ which npm |
|||
/usr/local/bin/npm |
|||
$ which yarn |
|||
/usr/local/bin/yarn |
|||
``` |
|||
|
|||
[Install npm](https://www.npmjs.com/get-npm), [install |
|||
yarn](https://yarnpkg.com/lang/en/docs/install/#mac-stable), or both as needed. You |
|||
|
|||
While it stands alone, this tour does on the information from the [Hello |
|||
Blockstack tutorial](hello-blockstack). If you haven't worked through that |
|||
tutorial, you may want to do that before continuing. |
|||
|
|||
|
|||
## Install the applicaton code and retrieve the dependencies |
|||
|
|||
You can clone the source code with `git` or [download and unzip the code from |
|||
the |
|||
repository](https://github.com/blockstack/blockstack-todos/archive/master.zip). |
|||
These instructions assume you are cloning. |
|||
|
|||
|
|||
1. Install the code by cloning it. |
|||
|
|||
``` |
|||
$ git clone git@github.com:blockstack/blockstack-todos.git |
|||
``` |
|||
|
|||
2. Change to directory to the root of the code. |
|||
|
|||
``` |
|||
$ cd blockstack-todos |
|||
``` |
|||
|
|||
2. Use yarn to install the dependencies. |
|||
|
|||
|
|||
``` |
|||
$ yarn install |
|||
yarn install v1.9.2 |
|||
info No lockfile found. |
|||
... |
|||
[4/5] 🔗 Linking dependencies... |
|||
[5/5] 📃 Building fresh packages... |
|||
success Saved lockfile. |
|||
✨ Done in 19.90s. |
|||
``` |
|||
|
|||
## Understand the important application files |
|||
|
|||
The Todo application has a basic Vue.js structure. There are several configuration files but the central programming files are in the `src` directory: |
|||
|
|||
| File | Description | |
|||
|-----------------|-------------| |
|||
| `main.js` | Application initialization. | |
|||
| `App.vue ` | Code for handling the `authResponse`. | |
|||
| `Landing.vue ` | Code for the initial sign on page. | |
|||
| `Dashboard.vue` | Application data storage and user sign out. | |
|||
|
|||
The example application runs in a node server on your local host. In the next section, you start the application and interact with it. |
|||
|
|||
1. Start the application. |
|||
|
|||
``` |
|||
$ npm run start |
|||
``` |
|||
|
|||
You should see a simple application: |
|||
|
|||
![](images/todo-sign-in.png) |
|||
|
|||
2. Choose **Sign In with Blockstack**. |
|||
|
|||
|
|||
## Understand the sign in process |
|||
|
|||
At startup, the Todo application detects whether the user has the Blockstack client edition |
|||
installed or not. This is done automatically by the Blockstack API, more |
|||
about this later. What the authenticator displays depends on which whether the user has installed the Blockstack Authenticator client edition or not. |
|||
|
|||
| Client edition installed | Not installed | |
|||
|------------------|-----------------------------------| |
|||
| ![](images/login.gif) | ![](images/login-choice.png)| |
|||
|
|||
If the user was logged into the Blockstack authenticator (web or client) but |
|||
did not reset it, the web application to use the current identity: |
|||
|
|||
![](images/login-no-auth.png) |
|||
|
|||
If the user chooses **Deny**, the Blockstack authenticator opens but the user |
|||
is not logged into the sample application. |
|||
|
|||
![](images/windows-browser.png) |
|||
|
|||
If the login to the application is successful, the user is presented with the application: |
|||
|
|||
![](images/todo-app.png) |
|||
|
|||
Clicking the **Sign In With Blockstack** button brings up a modal that prompts |
|||
you to use an existing ID's session, create a new ID, or reset the browser with |
|||
another ID. When Blockstack is provided an ID, it generates an _ephemeral key_ |
|||
within the application. An ephemeral key is generated for each execution of a |
|||
key establishment process. This key is just used for the particular instance of |
|||
the application, in this case to sign a **Sign In** request. |
|||
|
|||
Blockstack also generates a public key token which is sent to the authenticator |
|||
as an `authRequest` from the authenticator to your local blockstack-core node. |
|||
The signed authentication request is sent to Blockstack through a JSON Web |
|||
Token. The JWT is passed in via a URL query string in the `authRequest` |
|||
parameter: |
|||
`https://browser.blockstack.org/auth?authRequest=j902120cn829n1jnvoa...`. To |
|||
decode the token and see what information it holds: |
|||
|
|||
1. Copy the `authRequest` string from the URL. |
|||
2. Navigate to [jwt.io](http://jwt.io/). |
|||
3. Paste the full token there. |
|||
|
|||
The output should look similar to below: |
|||
|
|||
```json |
|||
{ |
|||
"jti": "3i96e3ad-0626-4e32-a316-b243154212e2", |
|||
"iat": 1533136622, |
|||
"exp": 1533140228, |
|||
"iss": "did:btc-addr:1Nh8oQTunbEQWjrL666HBx2qMc81puLmMt", |
|||
"public_keys": [ |
|||
"0362173da080c6e1dec0653fa9a3eff5f5660546e387ce6c24u04a90c2fe1fdu73" |
|||
], |
|||
"domain_name": "http://localhost:8080", |
|||
"manifest_uri": "http://localhost:8080/manifest.json", |
|||
"redirect_uri": "http://localhost:8080/", |
|||
"version": "1.2.0", |
|||
"do_not_include_profile": true, |
|||
"supports_hub_url": true, |
|||
"scopes": [ |
|||
"store_write" |
|||
] |
|||
} |
|||
``` |
|||
|
|||
>**Note**: |
|||
> 1. The `iss` property is a decentralized identifier or `did`. This identifies you and your name to the application. The specific `did` is a `btc-addr`. |
|||
> 2. The Blockstack JWT implementation is different from other implementations because of the underlying cryptography we employ. There are libraries in [Javascript](https://github.com/blockstack/jsontokens-js) and [Ruby](https://github.com/blockstack/ruby-jwt-blockstack) available on the Blockstack Github to allow you to work with these tokens. |
|||
|
|||
When the blockstack-core receives the `authRequest`, it generates a session token and |
|||
returns an authentication response to the application. This response is similar |
|||
to the `authRequest` above in that the `authResponse` includes a private key |
|||
intended only for the application. This allows the application to encrypt data |
|||
on your personal Blockstack storage. |
|||
|
|||
You are now logged into the Todo application! |
|||
|
|||
## Undder the covers in the sign in code |
|||
|
|||
Now, go to the underlying `blockstack-todo` code you cloned or downloaded. Sign |
|||
in and sign out is handled in each of these files: |
|||
|
|||
| File | Description | |
|||
|-----------------|-------------| |
|||
| `App.vue ` | Handles the `authResponse`. | |
|||
| `Landing.vue ` | Generates the `authRequest`. | |
|||
| `Dashboard.vue` | Handles sign out. | |
|||
|
|||
The `src/components/Landing.vue` code calls a [`redirectToSignIn()`](https://blockstack.github.io/blockstack.js#redirectToSignIn) function which generates the `authRequest` and redirects the user to the Blockstack authenticator: |
|||
|
|||
```js |
|||
signIn () { |
|||
const blockstack = this.blockstack |
|||
blockstack.redirectToSignIn() |
|||
} |
|||
``` |
|||
|
|||
Once the user authenticates, the application handles the `authResponse` in the `src/App.vue` file. : |
|||
|
|||
```js |
|||
if (blockstack.isUserSignedIn()) { |
|||
this.user = blockstack.loadUserData().profile |
|||
} else if (blockstack.isSignInPending()) { |
|||
blockstack.handlePendingSignIn() |
|||
.then((userData) => { |
|||
window.location = window.location.origin |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
If [`blockstack.isUserSignedIn()`](https://blockstack.github.io/blockstack.js/#isusersignedin) is true, the user was previously signed in so Blockstack pulls the data from the browser and uses it in our application. If the check on [`blockstack.isSignInPending()`](https://blockstack.github.io/blockstack.js/#issigninpending) is true, a previous `authResponse` was sent to the application but hasn't been processed yet. The `handlePendingSignIn()` function processes any pending sign in. |
|||
|
|||
Signout is handled in `src/components/Dashboard.vue`. |
|||
```js |
|||
signOut () { |
|||
this.blockstack.signUserOut(window.location.href) |
|||
} |
|||
``` |
|||
|
|||
The method allows the application creator to decide where to redirect the user upon Sign Out: |
|||
|
|||
|
|||
## Working with the application |
|||
|
|||
Now trying adding a few todos using the application. For example, try making a list of applications you want to see built on top of Blockstack: |
|||
|
|||
![](images/make-a-list.png) |
|||
|
|||
Each list is immediately stored in the Gaia Hub linked to your Blockstack ID. |
|||
For more information about the Gaia hub, see the [hub |
|||
repository](https://github.com/blockstack/gaia). You can fetch the `todos.json` |
|||
file you just added by opening the Javascript console and running the following |
|||
command: |
|||
|
|||
```Javascript |
|||
blockstack.getFile("todos.json", { decrypt: true }).then((file) => {console.log(file)}) |
|||
``` |
|||
|
|||
You should see a JSON with the todos you just added: |
|||
|
|||
```json |
|||
[ |
|||
{ |
|||
"id":2, |
|||
"text":"Software package manager secured by the blockchain", |
|||
"completed":false |
|||
}, |
|||
{ |
|||
"id":1, |
|||
"text":"Mutable torrents with human readable names", |
|||
"completed":false |
|||
}, |
|||
{ |
|||
"id":0, |
|||
"text":"Decentralized twitter", |
|||
"completed":false |
|||
} |
|||
] |
|||
``` |
|||
|
|||
|
|||
Now, add another todo and check it off. When you fetch the newly generated file |
|||
using the Javascript console it will reflect the change look for `"completed":true`: |
|||
|
|||
```json |
|||
[ |
|||
{ |
|||
"id":3, |
|||
"text":"Blockstack Todo", |
|||
"completed":true |
|||
}, |
|||
{ |
|||
"id":2, |
|||
"text":"Software package manager secured by the blockchain", |
|||
"completed":false |
|||
}, |
|||
... |
|||
] |
|||
``` |
|||
|
|||
Now that you have seen the application in action, dig into how it works. |
|||
|
|||
|
|||
## Implementing storage |
|||
|
|||
Now, go to the underlying `blockstack-todo` code you cloned or downloaded. The |
|||
application interactions with your Gaia Hub originate in the |
|||
`src/components/Dashboard.vue` file. First lets see where the changes to the |
|||
Todos are processed: |
|||
|
|||
```js |
|||
todos: { |
|||
handler: function (todos) { |
|||
const blockstack = this.blockstack |
|||
|
|||
// encryption is now enabled by default |
|||
return blockstack.putFile(STORAGE_FILE, JSON.stringify(todos)) |
|||
}, |
|||
deep: true |
|||
} |
|||
``` |
|||
|
|||
Tje `todos` JSON object is passed in and the [`blockstack.putFile()`](https://blockstack.github.io/blockstack.js/#putfile) method to store it in our Gaia Hub. |
|||
|
|||
The code needs to read the Todo items from the storage with the [`blockstack.getFile()`](https://blockstack.github.io/blockstack.js/#getfile) method which returns a promise: |
|||
|
|||
```js |
|||
fetchData () { |
|||
const blockstack = this.blockstack |
|||
blockstack.getFile(STORAGE_FILE) // decryption is enabled by default |
|||
.then((todosText) => { |
|||
var todos = JSON.parse(todosText || '[]') |
|||
todos.forEach(function (todo, index) { |
|||
todo.id = index |
|||
}) |
|||
this.uidCount = todos.length |
|||
this.todos = todos |
|||
}) |
|||
}, |
|||
``` |
|||
|
|||
The `todos` data is retrieved from the promise. |
|||
|
|||
|
|||
## Summary |
|||
|
|||
You now have everything you need to construct complex applications complete with authentication and storage on the Decentralized Internet. Why not try coding [a sample application that accesses multiple profiles](multi-player-storage.md). |
|||
|
|||
If you would like to explore the Blockstack APIs, you can visit the [Blockstack Core API](https://core.blockstack.org/) documentation or the [Blockstack JS API](https://blockstack.github.io/blockstack.js). |
|||
|
|||
Go forth and build! |
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,288 @@ |
|||
--- |
|||
layout: usenew |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Use the Blockstack Browser |
|||
{:.no_toc} |
|||
|
|||
The Blockstack Browser gives users the ability to explore and use the |
|||
decentralized applications (dapps). The decentralized applications are a new way |
|||
to interact with the internet. Dapps give users control of their data. Data |
|||
about them personally, name, birthdate, phone number and data about what they do |
|||
such as visiting a website or buying an item. |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
## Understand the Blockstack Browser |
|||
|
|||
Through the Blockstack browser application you can create an identity. An identity |
|||
represents you as you interact with others through Dapps. The Blockstack |
|||
Browser is itself, a simple Dapp. It allows you to: |
|||
|
|||
* create one or more identities |
|||
* send and receive bitcoin |
|||
* manage the storage of your profile and application data |
|||
* find and launch Dapps |
|||
|
|||
There are editorions of the Blockstack Browser, the web edition is an |
|||
application you access through your web browser by simply [visit the |
|||
applications' address](https://browser.blockstack.org/) in your computer's |
|||
browser. You can also install the browser as a client application on your |
|||
computer. |
|||
|
|||
If all you want to do is create, manage, and fund an identity and then interact |
|||
with Dapps, you can simply use the web edition. If you have concerns about net |
|||
censorship, hightened security concerns, or develop a Dapp yourself, you may |
|||
want to download and install the browser's client edition. |
|||
|
|||
## Using the Browser on public computers |
|||
|
|||
Before you use the web application, it is important to note that once you log |
|||
into the application with the brower, your session does not expire until you |
|||
choose **Settings > RESET BROWSER**. For this reason, you should be careful |
|||
when using the browser on public computers. |
|||
|
|||
If you are in a library, for example, and log into the browser, simply |
|||
closing the tab or even rebooting the computer does not log you out. Instead, |
|||
you should be sure to choose **Settings > RESET BROWSER** before leaving the web |
|||
application. |
|||
|
|||
For more informatin about your identity and the browser, see [Get and use a Blockstack IDs](ids-introduction). |
|||
|
|||
## Install the client edition |
|||
|
|||
Remember, for most users the Blockstack Browser web application should |
|||
suffice. You only need to install the client if you have additional, advanced |
|||
concerns about Internet or identity. Though not required, some Dapp developrs |
|||
may find it useful to install the client edition. |
|||
|
|||
The Blockstack Browser installer is a _multiple-context_ installer. If you |
|||
run it as a user, the installation is avalable only for that user. If you |
|||
install as administrator, the installation installs for all users. To find an |
|||
installer for your operating system, visit [the Blockstack install |
|||
page](https://blockstack.org/install). |
|||
|
|||
### On Mac |
|||
|
|||
Installation requires that you have macOS High Sierra 10.12 or higher. Do the following to install: |
|||
|
|||
1. Download the OSX installer from [the browser installation page](https://blockstack.org/install). |
|||
2. Double-click the downloaded DMG file to launch the installer. |
|||
|
|||
![](images/ubuntu-browser.png) |
|||
|
|||
3. Drag the Blockstack.app to the `Applications` folder. |
|||
4. Double-click the Blockstack.app to launch it. |
|||
|
|||
The system displays a notice about opening the file: |
|||
|
|||
![](images/dmg-notice.png) |
|||
|
|||
5. Choose **Open**. |
|||
|
|||
The system opens your default browser with the Blockstack Browser |
|||
application, running from your machine (localhost). You'll also see the |
|||
Blockstack icon in your machine's |
|||
|
|||
![](images/browser-on-mac.png) |
|||
|
|||
If you have loaded an identity already via the Blockstack web application, |
|||
you are already logged into the local application: |
|||
|
|||
![](images/browser-on-mac-1.png) |
|||
|
|||
|
|||
### On Windows |
|||
|
|||
Installation requires that you have Windows 10 or higher. Do the following to |
|||
install: |
|||
|
|||
1. Download the Windows installer from from [the browser installation page](https://blockstack.org/install). |
|||
2. Double-click the installer package to launch it. |
|||
|
|||
![](images/windows-installer.png) |
|||
|
|||
3. Open the Wiindows **Start** menu and click on the recently added **Blockstack Browser**. |
|||
|
|||
![](images/windows-start.png) |
|||
|
|||
The system displays a Windows Security Alert. |
|||
|
|||
![](images/windows-security.png) |
|||
|
|||
4. Choose to **Allow access**. |
|||
|
|||
The system opens in the Blockstack Browser application. |
|||
|
|||
![](images/windows-browser.png) |
|||
|
|||
|
|||
### On Linux |
|||
|
|||
The Blockstack installation on Linux requires Docker. Before installing |
|||
Blockstack, [install the version of Docker appropriate for your operating system](https://docs.docker.com/install/). |
|||
|
|||
>**Note**: The Blockstack script used in this procedure runs `docker` commands. Depending on how you installed and configure Dockered on your system, it may or may not be necessary to have `root` or `sudo` privileges. For this reason, the commands below show the use of `sudo` when interacting with the script or the `docker` executable. If your installation allows running Docker with lower privileges, you can omit it. |
|||
|
|||
|
|||
1. Download the Linux installer from from [the browser installation page](https://blockstack.org/install). |
|||
|
|||
This downloads a `Blockstack-for-Linux-v0.30.0.sh` script to your local drive. |
|||
|
|||
2. Open a terminal and navigate to the directory containing the downloaded script. |
|||
|
|||
When the script downloads, it is not executable. |
|||
|
|||
3. Set the executable bit on the file. |
|||
|
|||
```bash |
|||
$ chmod u+x Blockstack-for-Linux-v0.309.0.0.sh |
|||
``` |
|||
|
|||
4. Enter the command without any arguments to see the available subcommands. |
|||
|
|||
```bash |
|||
$ sudo ./Blockstack-for-Linux-v0.309.0.0.sh |
|||
blockstack docker launcher commands: |
|||
Install-protocol-handler -> install a protocol handler for blockstack:// links |
|||
... |
|||
``` |
|||
|
|||
5. Use the script to `pull` the Blockstack Docker images you need. |
|||
|
|||
|
|||
```bash |
|||
$ sudo ./Blockstack-for-Linux-v0.309.0.0.sh pull |
|||
``` |
|||
|
|||
Depending on your network speed, this can take some time. |
|||
|
|||
7. Use the `docker image ls` command to confirm you have the image. |
|||
|
|||
```bash |
|||
$ sudo docker image Is |
|||
REPOSITORY TAG IMAGE ID CREATED |
|||
quay.io/blockstack/blockstack-browser v0.30.0 ad05fd844f59 2 days ago |
|||
``` |
|||
|
|||
8. Install the protocol handler |
|||
|
|||
```bash |
|||
$ sudo ./Blockstack-for-Linux-vO.30.0.sh install-protocol-handler
|
|||
Registering protocol handler |
|||
``` |
|||
|
|||
9. Start the Blockstack containers. |
|||
|
|||
```bash |
|||
$ sudo ./Blockstack-for-Linux-vO.30.0.sh start |
|||
c3092592e59abe3559fdb49d070a7aa5e99165c7d9f2flla20ecaf4e0dfc2f46
|
|||
cd92f61ae473d54398da987f5023f5462b29c03f08584ebb3c9fIbb4cd790c69
|
|||
Registering protocol handler |
|||
``` |
|||
|
|||
The system launches the Blockstack Browser application for you. |
|||
|
|||
![](images/ubuntu-browser.png) |
|||
|
|||
Until you stop the Blockstack containers, the application will continue to run |
|||
on your system. To display the status of the Blockstack containers, you can use |
|||
the `docker container ls` command. |
|||
|
|||
{% raw %} |
|||
```bash |
|||
$ sudo docker container ls --format '{{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}' |
|||
``` |
|||
{% endraw %} |
|||
|
|||
Use `./Blockstack-for-Linux-vO.30.0.sh stop` to stop the Blockstack Browser and its containers. |
|||
|
|||
|
|||
## Uninstall the browser |
|||
|
|||
If you installed the browser using an installer, follow the instructions for |
|||
your operating system. |
|||
|
|||
### On Mac |
|||
|
|||
1. Quit the Blockstack application if it is running. |
|||
|
|||
![](images/quit-blockstack.png) |
|||
|
|||
2. Check if you have a Blockstack device and eject it if you do. |
|||
|
|||
![](images/eject-blockstack.png) |
|||
|
|||
3. Use the Finder to open the **Applications** folder. |
|||
4. Locate the Blockstack application. |
|||
5. Open your `Applications` folder and locate the **Blockstack.app**. |
|||
6. Drag the appliation to the trash. |
|||
7. Delete the `/Users/USERNAME/Library/Application Support/Blockstack` folder. |
|||
|
|||
From the command line: |
|||
|
|||
```bash |
|||
$ rm -r /Users/moxiegirl/Library/Application\ Support/Blockstack |
|||
``` |
|||
|
|||
### On Windows |
|||
|
|||
1. Open the **Start** menu. |
|||
2. Click **Settings > System**. |
|||
3. Open for the **Apps & features** item. |
|||
|
|||
![](images/eject-blockstack.png) |
|||
|
|||
4. Locate the **Blockstack Browser** and choose **Uninstall**. |
|||
|
|||
![](images/browser-uninstall.png) |
|||
|
|||
|
|||
### On Linux |
|||
|
|||
Your Blockstack instalaltion relies on Docker containers and their associated |
|||
images. It also includes a supporting protocol handler you must remove. If you |
|||
installed Docker so that you can run Blockstack, you can uninstall Docker as well, |
|||
but that is not explained here. |
|||
|
|||
Do the following to uninstall Blockstack: |
|||
|
|||
1. Stop and remove the Docker containers if they are running. |
|||
|
|||
```bash |
|||
$ sudo ./Blockstack-for-Linux-vO.30.0.sh stop |
|||
stopping the running blockstack-browser containers |
|||
69a686799d4f |
|||
56fc6189ff97 |
|||
69a686799d4f |
|||
56fc6189ff97 |
|||
``` |
|||
|
|||
2. Remove the associated Blockstack images. |
|||
|
|||
```bash |
|||
$ sudo docker image ls |
|||
REPOSITORY TAG IMAGE ID CREATED |
|||
quay.io/blockstack/blockstack-browser v0.30.0 ad05fd844f59 3 days ago |
|||
$ sudo docker image rm ad05fd844f59
|
|||
Untagged : quay.io/blockstack/blockstack- browser :vO.30.0 |
|||
Untagged: quay.io/blockstack/blockstack-browser@sha256:b20c9514c56b99398fd4946af39e7537b807e85694943ac3b8807dlb3625833b |
|||
Deleted: Sha256:ad05fd844f5948blee06a0a09228df946478393c0a7588cbc65dlb8817f5b34e |
|||
Deleted: Sha256:7c3d0043f2ba01cf285f3fe09701b086c349b6380c2e42f25b31ac65c6626ec8 |
|||
Deleted: sha256:54ea2aa7d7d000e7483f299eeca9e5466fa86231f4cd4cld3c3096d97e61c5df |
|||
Deleted: sha256:38e61054355adefc3c2de031462114a9946cfc0e44444a38a27d0f115aba0da2 |
|||
.... |
|||
``` |
|||
|
|||
3. Use the script to remove the protocol handler |
|||
|
|||
```bash |
|||
$ sudo ./Blockstack-for-Linux-vO.30.0.sh remove-protocol-handler |
|||
``` |
|||
|
|||
4. Delete the script. |
|||
|
|||
```bash |
|||
$ rm Blockstack-for-Linux-vO.30.0.sh |
|||
``` |
After Width: | Height: | Size: 101 KiB |
@ -0,0 +1,812 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Blockstack Storage Tutorial |
|||
{:.no_toc} |
|||
|
|||
In this tutorial, you build a micro-blogging application using multi-player Gaia |
|||
storage. Gaia is Blockstack's [decentralized high-performance storage |
|||
system](https://github.com/blockstack/gaia). The tutorial contains the following |
|||
topics: |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
This tutorial does not teach you about authentication. That is covered in depth [in the hello-blockstack tutorial](hello-blockstack). |
|||
|
|||
<!--TODO: authentication tutorial--> |
|||
<!--Strictly speaking not sure it is necessary here to send them out--> |
|||
|
|||
## About this tutorial and the prerequisites you need |
|||
|
|||
At minimum, Blockstack requires macOS High Sierra. This tutorial was written for |
|||
a user running macOS High Sierra 10.13.4. The application you build is a |
|||
React.js application that is completely decentralized and server-less. While |
|||
not strictly required to follow along, basic familiarity with React.js is |
|||
helpful. |
|||
|
|||
When complete, the app is capable of the following: |
|||
|
|||
- authenticating users using Blockstack |
|||
- posting new statuses |
|||
- displaying statuses in the user profile |
|||
- looking up the profiles and statuses of other users |
|||
|
|||
The basic identity and storage services are provided by `blockstack.js`. To test |
|||
the application, you need to have already [registered a Blockstack ID](ids-introduction). |
|||
|
|||
The tutorial relies on the `npm` dependency manager. Before you begin, verify |
|||
you have installed `npm` using the `which` command. |
|||
|
|||
```bash |
|||
$ which npm |
|||
/usr/local/bin/npm |
|||
``` |
|||
|
|||
If you don't find `npm` in your system, [install |
|||
it](https://www.npmjs.com/get-npm). Finally, if you get stuck at any point |
|||
while working on the tutorial, the completed [source code is available for |
|||
you](https://github.com/larrysalibra/publik) to check your work against. |
|||
|
|||
## Use npm to install Yeoman and the Blockstack App Generator |
|||
|
|||
You use `npm` to install Yeoman. Yeoman is a generic scaffolding system that |
|||
helps users rapidly start new projects and streamline the maintenance of |
|||
existing projects. |
|||
|
|||
|
|||
1. Install Yeoman. |
|||
|
|||
```bash |
|||
npm install -g yo |
|||
``` |
|||
2. Install the Blockstack application generator. |
|||
|
|||
```bash |
|||
npm install -g generator-blockstack |
|||
``` |
|||
|
|||
<!-- Need to find out if user is required to have React installed before running Yeoman. Doesn't appear to be the case. --> |
|||
|
|||
## Generate and launch the public application |
|||
|
|||
In this section, you build an initial React.js application called Publik. |
|||
|
|||
1. Create a the `publik` directory. |
|||
|
|||
```bash |
|||
mkdir publik |
|||
``` |
|||
|
|||
2. Change into your new directory. |
|||
|
|||
```bash |
|||
cd publik |
|||
``` |
|||
|
|||
3. Use Yeoman and the Blockstack application generator to create your initial `publik` application. |
|||
|
|||
```bash |
|||
yo blockstack:react |
|||
``` |
|||
|
|||
You should see several interactive prompts. |
|||
|
|||
```bash |
|||
$ yo blockstack:react |
|||
? ========================================================================== |
|||
We're constantly looking for ways to make yo better! |
|||
May we anonymously report usage statistics to improve the tool over time? |
|||
More info: https://github.com/yeoman/insight & http://yeoman.io |
|||
========================================================================== No |
|||
|
|||
_-----_ ╭──────────────────────────╮ |
|||
| | │ Welcome to the │ |
|||
|--(o)--| │ Blockstack app │ |
|||
`---------´ │ generator! │ |
|||
( _´U`_ ) ╰──────────────────────────╯ |
|||
/___A___\ / |
|||
| ~ | |
|||
__'.___.'__ |
|||
´ ` |° ´ Y ` |
|||
|
|||
? Are you ready to build a Blockstack app in React? (Y/n) |
|||
``` |
|||
|
|||
4. Respond to the prompts to populate the initial app. |
|||
|
|||
After the process completes successfully, you see a prompt similar to the following: |
|||
|
|||
```bash |
|||
[fsevents] Success: |
|||
"/Users/theuser/repos/publik/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node" |
|||
is installed via remote npm notice created a lockfile as package-lock.json. |
|||
You should commit this file. added 1060 packages in 26.901s |
|||
``` |
|||
|
|||
5. Run the initial application. |
|||
|
|||
```bash |
|||
npm start |
|||
``` |
|||
|
|||
The system prompts you to accept incoming connections. |
|||
|
|||
![Network Connection](./images/network-connections.gif) |
|||
|
|||
6. Choose **Allow**. |
|||
|
|||
7. Open your browser to `http://localhost:8080`. |
|||
|
|||
You should see a simple React app. |
|||
|
|||
![](images/initial-app.gif) |
|||
|
|||
8. Choose **Sign In with Blockstack**. |
|||
|
|||
The application tells you it will **Read your basic info**. |
|||
|
|||
![](images/login.png) |
|||
|
|||
Leave your new application running and move onto the next section. |
|||
|
|||
## Add the `publish_data` scope to sign in requests |
|||
|
|||
Every app that uses Gaia storage must add itself to the user's `profile.json` |
|||
file. The Blockstack browser does this automatically when the `publish_data` |
|||
scope is requested during authentication. For this application, the user files |
|||
stored on Gaia are made visible to others via the `apps` property in the user's |
|||
`profile.json` file. |
|||
|
|||
Modify your authentication request to include the `publish_data` scope. |
|||
|
|||
1. Open `src/components/App.jsx` file. |
|||
|
|||
2. Locate the `handleSignIn` handler method. |
|||
|
|||
```javascript |
|||
handleSignIn(e) { |
|||
e.preventDefault(); |
|||
redirectToSignIn(); |
|||
} |
|||
``` |
|||
|
|||
2. Modify the method to this: |
|||
|
|||
```javascript |
|||
handleSignIn(e) { |
|||
e.preventDefault(); |
|||
const origin = window.location.origin |
|||
redirectToSignIn(origin, origin + '/manifest.json', ['store_write', 'publish_data']) |
|||
} |
|||
``` |
|||
|
|||
By default, authentication requests include the `store_write` scope which |
|||
enables storage. This is what allows you to store information to Gaia. |
|||
|
|||
3. Save your changes. |
|||
4. Go back to your app at `http://localhost:8080/`. |
|||
5. Log out and sign in again. |
|||
|
|||
The authentication request now prompts the user for permission to **Publish |
|||
data stored for the app**. |
|||
|
|||
![](images/publish-data-perm.png) |
|||
|
|||
## Understand Gaia storage methods |
|||
|
|||
Once you authenticate a user with `store_write` and `publish_data`, you can |
|||
begin to manage data for your users. Blockstack JS provides two methods |
|||
`getFile()` and `putFile()` for interacting with Gaia storage. The storage |
|||
methods support all file types. This means you can store SQL, Markdown, JSON, or |
|||
even a custom format. |
|||
|
|||
You can create a meaningful and complex data layer using these two methods. |
|||
Before creating an application, consider fundamental data architecture and make |
|||
some decisions about how you’re modeling data. For example, consider building a |
|||
simple grocery list app. A user should be able to create, read, update, and |
|||
delete grocery lists. |
|||
|
|||
A single file collection stores items as an array nested inside each grocery |
|||
list: |
|||
|
|||
```js |
|||
// grocerylists.json |
|||
{ |
|||
"3255": { |
|||
"items": [ |
|||
"1 Head of Lettuce", |
|||
"Haralson apples" |
|||
] |
|||
}, |
|||
// ...more lists with items |
|||
} |
|||
``` |
|||
|
|||
This is conceptually the simplest way to manage grocery lists. When you read a |
|||
`/grocerylists.json` file with `getFile()`, you get back one or more grocery |
|||
lists and their items. When you write a single list, the `putFile()` method |
|||
overwrites the entire list. So, a write operation for a new or updated grocery |
|||
list must submit all existings lists as well. |
|||
|
|||
Further, because this runs on the client where anything can go wrong. If the |
|||
client-side code encounters a parsing error with a user-input value and you |
|||
could overwrite the entire file with: |
|||
|
|||
`line 6: Parsing Error: Unexpected token.` |
|||
|
|||
Further, a single file makes pagination impossible and if your app stores a |
|||
single file for all list you have less control over file permissions. To avoid |
|||
these issues, you can create an index file that stores an array of IDs. These |
|||
IDs point to a name of another file in a `grocerylists` folder. |
|||
|
|||
![](images/multiple-lists.png) |
|||
|
|||
This design allows you to get only the files you need and avoid accidentally |
|||
overwriting all lists. Further, you’re only updating the index file when you add |
|||
or remove a grocery list; updating a list has no impact. |
|||
|
|||
|
|||
## Add support for user status submission and lookup |
|||
|
|||
In this step, you add three `blockstack.js` methods that support posting of "statuses". These are the `putFile()`, `getFile()`, and `lookupProfile()` methods. |
|||
|
|||
1. Open the `src/components/Profile.jsx` file. |
|||
|
|||
2. Expand the `import from blockstack` statement with data methods. |
|||
|
|||
The `Person` object holds a Blockstack profile. Add `putFile`, `getFile`, |
|||
and `lookupProfile` after `Person`. |
|||
|
|||
When you are done, the import statement should look like the following: |
|||
|
|||
```javascript |
|||
import { |
|||
isSignInPending, |
|||
loadUserData, |
|||
Person, |
|||
getFile, |
|||
putFile, |
|||
lookupProfile |
|||
} from 'blockstack'; |
|||
``` |
|||
|
|||
3. Replace the `constructor()` initial state so that it holds the key properties required by the app. |
|||
|
|||
This code constructs a Blockstack `Person` object to hold the profile. Your constructor should look like this: |
|||
|
|||
```javascript |
|||
constructor(props) { |
|||
super(props); |
|||
|
|||
this.state = { |
|||
person: { |
|||
name() { |
|||
return 'Anonymous'; |
|||
}, |
|||
avatarUrl() { |
|||
return avatarFallbackImage; |
|||
}, |
|||
}, |
|||
username: "", |
|||
newStatus: "", |
|||
statuses: [], |
|||
statusIndex: 0, |
|||
isLoading: false |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
|
|||
4. Locate the `render()` method. |
|||
5. Modify the `render()` method to add a text input and submit button to the application. |
|||
|
|||
The following code echos the `person.name` and `person.avatarURL` |
|||
properties from the profile on the display: |
|||
|
|||
```javascript |
|||
render() { |
|||
const { handleSignOut } = this.props; |
|||
const { person } = this.state; |
|||
const { username } = this.state; |
|||
|
|||
return ( |
|||
!isSignInPending() && person ? |
|||
<div className="container"> |
|||
<div className="row"> |
|||
<div className="col-md-offset-3 col-md-6"> |
|||
<div className="col-md-12"> |
|||
<div className="avatar-section"> |
|||
<img |
|||
src={ person.avatarUrl() ? person.avatarUrl() : avatarFallbackImage } |
|||
className="img-rounded avatar" |
|||
id="avatar-image" |
|||
/> |
|||
<div className="username"> |
|||
<h1> |
|||
<span id="heading-name">{ person.name() ? person.name() |
|||
: 'Nameless Person' }</span> |
|||
</h1> |
|||
<span>{username}</span> |
|||
<span> |
|||
| |
|||
<a onClick={ handleSignOut.bind(this) }>(Logout)</a> |
|||
</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="new-status"> |
|||
<div className="col-md-12"> |
|||
<textarea className="input-status" |
|||
value={this.state.newStatus} |
|||
onChange={e => this.handleNewStatusChange(e)} |
|||
placeholder="Enter a status" |
|||
/> |
|||
</div> |
|||
<div className="col-md-12"> |
|||
<button |
|||
className="btn btn-primary btn-lg" |
|||
onClick={e => this.handleNewStatusSubmit(e)} |
|||
> |
|||
Submit |
|||
</button> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> : null |
|||
); |
|||
} |
|||
``` |
|||
|
|||
This code allows the application to post statuses. It also displays the |
|||
user's Blockstack ID. To display this, your app must extract the ID from the |
|||
user profile data. |
|||
|
|||
6. Locate the `componentWillMount()` method. |
|||
7. Add the `username` property below the `person` property. |
|||
|
|||
You'll use the Blockstack `loadUserData()` method to access the `username`. |
|||
|
|||
|
|||
```javascript |
|||
componentWillMount() { |
|||
this.setState({ |
|||
person: new Person(loadUserData().profile), |
|||
username: loadUserData().username |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
7. Add two methods to handle the status input events: |
|||
|
|||
```javascript |
|||
handleNewStatusChange(event) { |
|||
this.setState({newStatus: event.target.value}) |
|||
} |
|||
|
|||
handleNewStatusSubmit(event) { |
|||
this.saveNewStatus(this.state.newStatus) |
|||
this.setState({ |
|||
newStatus: "" |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
8. Add a `saveNewStatus()` method to save the new statuses. |
|||
|
|||
```javascript |
|||
saveNewStatus(statusText) { |
|||
let statuses = this.state.statuses |
|||
|
|||
let status = { |
|||
id: this.state.statusIndex++, |
|||
text: statusText.trim(), |
|||
created_at: Date.now() |
|||
} |
|||
|
|||
statuses.unshift(status) |
|||
const options = { encrypt: false } |
|||
putFile('statuses.json', JSON.stringify(statuses), options) |
|||
.then(() => { |
|||
this.setState({ |
|||
statuses: statuses |
|||
}) |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
9. Save the `Profile.jsk` file. |
|||
|
|||
After the application compiles successfully, your application should appears as follows: |
|||
|
|||
![](images/display-complete.png) |
|||
|
|||
10. Enter your status in the text box and press the **Submit** button. |
|||
|
|||
At this point, nothing is blogged. In the next section you add code to display |
|||
the statuses back to the user as a blog entry. |
|||
|
|||
## Fetch and display statuses |
|||
|
|||
Update `Profile.jsx` again. |
|||
|
|||
1. Go back to the `render()` method. |
|||
2. Locate the `<div className="new-status">` containing the text input and **Submit** button. |
|||
3. Right after this opening `div` element, add this block. |
|||
|
|||
```javascript |
|||
<div className="col-md-12 statuses"> |
|||
{this.state.isLoading && <span>Loading...</span>} |
|||
{this.state.statuses.map((status) => ( |
|||
<div className="status" key={status.id}> |
|||
{status.text} |
|||
</div> |
|||
) |
|||
)} |
|||
</div> |
|||
``` |
|||
This loads existing state. Your code needs to fetch statuses on page load. |
|||
|
|||
4. Add a new method called `fetchData()` after the `statuses.unshift(status)` section. |
|||
|
|||
```javascript |
|||
|
|||
fetchData() { |
|||
this.setState({ isLoading: true }) |
|||
const options = { decrypt: false } |
|||
getFile('statuses.json', options) |
|||
.then((file) => { |
|||
var statuses = JSON.parse(file || '[]') |
|||
this.setState({ |
|||
person: new Person(loadUserData().profile), |
|||
username: loadUserData().username, |
|||
statusIndex: statuses.length, |
|||
statuses: statuses, |
|||
}) |
|||
}) |
|||
.finally(() => { |
|||
this.setState({ isLoading: false }) |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
5. Call `fetchData()` from the `componentDidMount()` method |
|||
|
|||
```javascript |
|||
|
|||
componentDidMount() { |
|||
this.fetchData() |
|||
} |
|||
``` |
|||
|
|||
6. Save the file. |
|||
|
|||
After the application compiles successfully, users are able to **Submit** |
|||
multiple statuses and review them in the app. |
|||
|
|||
![](images/saving-status.png) |
|||
|
|||
## Change the style |
|||
|
|||
1. Edit the `src/styles/style.css` file. |
|||
2. Replace the content with the following: |
|||
|
|||
|
|||
```css |
|||
/* Globals */ |
|||
a,a:focus,a:hover{color:#fff;} |
|||
html,body{height:100%;text-align:center;background-color:#191b22;} |
|||
body{color:#fff} |
|||
.hide{display:none;} |
|||
.landing-heading{font-family:'Lato',Sans-Serif;font-weight:400;} |
|||
|
|||
/* Buttons */ |
|||
.btn{font-family:'Lato',Sans-Serif;padding:0.5625rem 2.5rem;font-size:0.8125rem;font-weight:400;line-height:1.75rem;border-radius:0!important;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-ms-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;} |
|||
.btn-lg{font-size:1.5rem;padding:0.6875rem 3.4375rem;line-height:2.5rem;} |
|||
.btn:focus,.btn:active:focus,.btn.active:focus{outline:none;} |
|||
.btn-primary{color:#fff;border:1px solid #2C96FF;background-color:#2C96FF;} |
|||
.btn-primary:hover,.btn-primary:focus,.btn-primary:active{color:#fff;border:1px solid #1a6ec0;background-color:#1a6ec0;} |
|||
|
|||
/* Avatar */ |
|||
.avatar{width:100px;height:100px;} |
|||
.avatar-section{margin-bottom:25px;display:flex;text-align:left;} |
|||
.username{margin-left:20px;} |
|||
|
|||
/* Scaffolding */ |
|||
.site-wrapper{display:table;width:100%;height:100vh;min-height:100%;} |
|||
.site-wrapper-inner{display:flex;flex-direction:column;justify-content:center;margin-right:auto;margin-left:auto;width:100%;height:100vh;} |
|||
.panel-authed{padding:0 0 0 0;} |
|||
|
|||
/* Home button */ |
|||
.btn-home-hello{position:absolute;font-family:'Source Code Pro',monospace;font-size:11px;font-weight:400;color:rgba(255,255,255,0.85);top:15px;left:15px;padding:3px 20px;background-color:rgba(255,255,255,0.15);border-radius:6px;-webkit-box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);-moz-box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);} |
|||
|
|||
/* Input */ |
|||
input, textarea{color:#000;padding:10px;} |
|||
.input-status{width:100%;height:70px;border-radius:6px;} |
|||
.new-status{text-align:right;} |
|||
|
|||
/* Statuses */ |
|||
.statuses{padding-top:30px;} |
|||
.status{margin:15px 0px;padding:20px;background-color:#2e2e2e;border-radius:6px} |
|||
``` |
|||
|
|||
3. Save and close the `src/styles/style.css` file. |
|||
|
|||
After the application compiles, you should see the following: |
|||
|
|||
![Multi-reader storage authentication](images/multi-player-storage-status.png) |
|||
|
|||
|
|||
At this point, you have a basic micro-blogging app that users can use to post and |
|||
view statuses. However, there's no way to view other users' statuses. You'll add |
|||
that in the next section. |
|||
|
|||
## Lookup user profiles |
|||
|
|||
Let's now modify the `Profile.jsx` file to display profiles of other users. You'll |
|||
be using the `lookupProfile()` method that you added to the `import` statement |
|||
earlier. `lookupProfile()` takes a single parameter that is the Blockstack ID of |
|||
the profile and returns a profile object. |
|||
|
|||
### Add a new route |
|||
|
|||
Make some changes to the routing structure of your app so that users can view |
|||
other users' profiles by visiting `http://localhost:8080/other_user.id` |
|||
|
|||
|
|||
1. Make sure you are in the root of your `publik` project. |
|||
2. Install `react-router`: |
|||
|
|||
```bash |
|||
npm install --save react-router-dom |
|||
``` |
|||
|
|||
3. Edit `src/index.js` file. |
|||
4. Add an `import` to the file at the top: |
|||
|
|||
```javascript |
|||
import { BrowserRouter } from 'react-router-dom' |
|||
``` |
|||
|
|||
5. Change the `ReactDOM.render()` method in `src/index.js` to: |
|||
|
|||
```javascript |
|||
ReactDOM.render(( |
|||
<BrowserRouter> |
|||
<App /> |
|||
</BrowserRouter> |
|||
), document.getElementById('root')); |
|||
``` |
|||
6. Save and close the `src/index.js` file. |
|||
7. Edit the `src/components/App.jsx` file. |
|||
8. Add the new route by importing the `Switch` and `Route` components from `react-router-dom`: |
|||
|
|||
```javascript |
|||
import { Switch, Route } from 'react-router-dom' |
|||
``` |
|||
9. Locate this line below in the `render()` method: |
|||
|
|||
```javascript |
|||
: <Profile handleSignOut={ this.handleSignOut } /> |
|||
``` |
|||
|
|||
10. Replace it with the following: |
|||
|
|||
```javascript |
|||
: |
|||
<Switch> |
|||
<Route |
|||
path='/:username?' |
|||
render={ |
|||
routeProps => <Profile handleSignOut={ this.handleSignOut } {...routeProps} /> |
|||
} |
|||
/> |
|||
</Switch> |
|||
``` |
|||
|
|||
This sets up a route and captures the route parameter the app will use as the profile lookup username. |
|||
|
|||
11. Save and close the the `src/components/App.jsx` file. |
|||
|
|||
|
|||
### Add a rule to process URL paths with . (dot) |
|||
|
|||
You also need to add a rule to your webpack config so that you can properly |
|||
process URL paths that contain the `.` (dot) character for example, |
|||
`http://localhost:8080/other_user.id` |
|||
|
|||
**NOTE**: In a production app, you must ensure the web server is configured to handle this. |
|||
|
|||
|
|||
1. Open `webpack.config.js` in the root project directory and locate the following line: |
|||
|
|||
```javascript |
|||
historyApiFallback: true, |
|||
``` |
|||
|
|||
2. Replace it with this: |
|||
|
|||
```javascript |
|||
historyApiFallback: { |
|||
disableDotRule: true |
|||
}, |
|||
``` |
|||
|
|||
You will need to run `npm start` again for this change to take effect. Don't |
|||
worry, there is a later step for that to remind you. |
|||
|
|||
3. Save and close the `webpack.config.js` file. |
|||
|
|||
4. Edit the `src/components/Profile.jsx` file. |
|||
5. Add a single method that determines if the app is viewing the local user's profile or another user's profile. |
|||
|
|||
```javascript |
|||
isLocal() { |
|||
return this.props.match.params.username ? false : true |
|||
} |
|||
``` |
|||
|
|||
You use `isLocal()` to check if the user is viewing the local user profile or another user's profile. If it's the local user profile, the app runs the `getFile()` function you added in an earlier step. Otherwise, the app looks up the profile belonging to the `username` using the `lookupProfile()` method. |
|||
|
|||
6. Modify the `fetchData()` method like so: |
|||
|
|||
```javascript |
|||
fetchData() { |
|||
this.setState({ isLoading: true }) |
|||
if (this.isLocal()) { |
|||
const options = { decrypt: false } |
|||
getFile('statuses.json', options) |
|||
.then((file) => { |
|||
var statuses = JSON.parse(file || '[]') |
|||
this.setState({ |
|||
person: new Person(loadUserData().profile), |
|||
username: loadUserData().username, |
|||
statusIndex: statuses.length, |
|||
statuses: statuses, |
|||
}) |
|||
}) |
|||
.finally(() => { |
|||
this.setState({ isLoading: false }) |
|||
}) |
|||
} else { |
|||
const username = this.props.match.params.username |
|||
|
|||
lookupProfile(username) |
|||
.then((profile) => { |
|||
this.setState({ |
|||
person: new Person(profile), |
|||
username: username |
|||
}) |
|||
}) |
|||
.catch((error) => { |
|||
console.log('could not resolve profile') |
|||
}) |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**NOTE**: For `https` deployments, the default Blockstack Core API endpoint for name |
|||
lookups should be changed to point to a core API served over `https`. |
|||
Otherwise, name lookups fail due to browsers blocking mixed content. |
|||
Refer to the [Blockstack.js |
|||
documentation](http://blockstack.github.io/blockstack.js/#getfile) for |
|||
details. |
|||
|
|||
7. Add the following block to `fetchData()` right after the call to `lookupProfile(username)... catch((error)=>{..}` block: |
|||
|
|||
```javascript |
|||
const options = { username: username, decrypt: false } |
|||
getFile('statuses.json', options) |
|||
.then((file) => { |
|||
var statuses = JSON.parse(file || '[]') |
|||
this.setState({ |
|||
statusIndex: statuses.length, |
|||
statuses: statuses |
|||
}) |
|||
}) |
|||
.catch((error) => { |
|||
console.log('could not fetch statuses') |
|||
}) |
|||
.finally(() => { |
|||
this.setState({ isLoading: false }) |
|||
}) |
|||
``` |
|||
|
|||
This fetches the user statuses. |
|||
|
|||
Finally, you must conditionally render the logout button, status input textbox, and submit button so they don't show up when viewing another user's profile. |
|||
|
|||
8. Replace the `render()` method with the following: |
|||
|
|||
```javascript |
|||
render() { |
|||
const { handleSignOut } = this.props; |
|||
const { person } = this.state; |
|||
const { username } = this.state; |
|||
|
|||
return ( |
|||
!isSignInPending() && person ? |
|||
<div className="container"> |
|||
<div className="row"> |
|||
<div className="col-md-offset-3 col-md-6"> |
|||
<div className="col-md-12"> |
|||
<div className="avatar-section"> |
|||
<img |
|||
src={ person.avatarUrl() ? person.avatarUrl() : avatarFallbackImage } |
|||
className="img-rounded avatar" |
|||
id="avatar-image" |
|||
/> |
|||
<div className="username"> |
|||
<h1> |
|||
<span id="heading-name">{ person.name() ? person.name() |
|||
: 'Nameless Person' }</span> |
|||
</h1> |
|||
<span>{username}</span> |
|||
{this.isLocal() && |
|||
<span> |
|||
| |
|||
<a onClick={ handleSignOut.bind(this) }>(Logout)</a> |
|||
</span> |
|||
} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{this.isLocal() && |
|||
<div className="new-status"> |
|||
<div className="col-md-12"> |
|||
<textarea className="input-status" |
|||
value={this.state.newStatus} |
|||
onChange={e => this.handleNewStatusChange(e)} |
|||
placeholder="What's on your mind?" |
|||
/> |
|||
</div> |
|||
<div className="col-md-12 text-right"> |
|||
<button |
|||
className="btn btn-primary btn-lg" |
|||
onClick={e => this.handleNewStatusSubmit(e)} |
|||
> |
|||
Submit |
|||
</button> |
|||
</div> |
|||
</div> |
|||
} |
|||
<div className="col-md-12 statuses"> |
|||
{this.state.isLoading && <span>Loading...</span>} |
|||
{this.state.statuses.map((status) => ( |
|||
<div className="status" key={status.id}> |
|||
{status.text} |
|||
</div> |
|||
) |
|||
)} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> : null |
|||
); |
|||
} |
|||
``` |
|||
|
|||
### This checks to ensure that users are viewing their own profile, by wrapping the **Logout** button and inputs with the `{isLocal() && ...}` condition. |
|||
|
|||
### Put it all together |
|||
|
|||
1. Stop the running application by sending a CTL-C. |
|||
2. Restart the application so that the disabling of the `.` (dot) rule takes effect. |
|||
|
|||
```bash |
|||
npm start |
|||
``` |
|||
|
|||
3. Point your browser to `http://localhost:8080/your_blockstack.id` to see the final application. |
|||
|
|||
|
|||
|
|||
## Wrapping up |
|||
|
|||
Congratulations, you are all done! We hope you've enjoyed learning a bit more |
|||
about Blockstack. To use a working version of the app go |
|||
[here](http://publik.ykliao.com). |
@ -0,0 +1,812 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Manage Data with Gaia |
|||
{:.no_toc} |
|||
|
|||
In this tutorial, you build a micro-blogging application using multi-player Gaia |
|||
storage. Gaia is Blockstack's [decentralized high-performance storage |
|||
system](https://github.com/blockstack/gaia). The tutorial contains the following |
|||
topics: |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
This tutorial does not teach you about authentication. That is covered in depth [in the hello-blockstack tutorial](hello-blockstack.md). |
|||
|
|||
<!--TODO: authentication tutorial--> |
|||
<!--Strictly speaking not sure it is necessary here to send them out--> |
|||
|
|||
## About this tutorial and the prerequisites you need |
|||
|
|||
At minimum, Blockstack requires macOS High Sierra. This tutorial was written for |
|||
a user running macOS High Sierra 10.13.4. The application you build is a |
|||
React.js application that is completely decentralized and server-less. While |
|||
not strictly required to follow along, basic familiarity with React.js is |
|||
helpful. |
|||
|
|||
When complete, the app is capable of the following: |
|||
|
|||
- authenticating users using Blockstack |
|||
- posting new statuses |
|||
- displaying statuses in the user profile |
|||
- looking up the profiles and statuses of other users |
|||
|
|||
The basic identity and storage services are provided by `blockstack.js`. To test |
|||
the application, you need to have already [registered a Blockstack ID](ids-introduction.md). |
|||
|
|||
The tutorial relies on the `npm` dependency manager. Before you begin, verify |
|||
you have installed `npm` using the `which` command. |
|||
|
|||
```bash |
|||
$ which npm |
|||
/usr/local/bin/npm |
|||
``` |
|||
|
|||
If you don't find `npm` in your system, [install |
|||
it](https://www.npmjs.com/get-npm). Finally, if you get stuck at any point |
|||
while working on the tutorial, the completed [source code is available for |
|||
you](https://github.com/larrysalibra/publik) to check your work against. |
|||
|
|||
## Use npm to install Yeoman and the Blockstack App Generator |
|||
|
|||
You use `npm` to install Yeoman. Yeoman is a generic scaffolding system that |
|||
helps users rapidly start new projects and streamline the maintenance of |
|||
existing projects. |
|||
|
|||
|
|||
1. Install Yeoman. |
|||
|
|||
```bash |
|||
npm install -g yo |
|||
``` |
|||
2. Install the Blockstack application generator. |
|||
|
|||
```bash |
|||
npm install -g generator-blockstack |
|||
``` |
|||
|
|||
<!-- Need to find out if user is required to have React installed before running Yeoman. Doesn't appear to be the case. --> |
|||
|
|||
## Generate and launch the public application |
|||
|
|||
In this section, you build an initial React.js application called Publik. |
|||
|
|||
1. Create a the `publik` directory. |
|||
|
|||
```bash |
|||
mkdir publik |
|||
``` |
|||
|
|||
2. Change into your new directory. |
|||
|
|||
```bash |
|||
cd publik |
|||
``` |
|||
|
|||
3. Use Yeoman and the Blockstack application generator to create your initial `publik` application. |
|||
|
|||
```bash |
|||
yo blockstack:react |
|||
``` |
|||
|
|||
You should see several interactive prompts. |
|||
|
|||
```bash |
|||
$ yo blockstack:react |
|||
? ========================================================================== |
|||
We're constantly looking for ways to make yo better! |
|||
May we anonymously report usage statistics to improve the tool over time? |
|||
More info: https://github.com/yeoman/insight & http://yeoman.io |
|||
========================================================================== No |
|||
|
|||
_-----_ ╭──────────────────────────╮ |
|||
| | │ Welcome to the │ |
|||
|--(o)--| │ Blockstack app │ |
|||
`---------´ │ generator! │ |
|||
( _´U`_ ) ╰──────────────────────────╯ |
|||
/___A___\ / |
|||
| ~ | |
|||
__'.___.'__ |
|||
´ ` |° ´ Y ` |
|||
|
|||
? Are you ready to build a Blockstack app in React? (Y/n) |
|||
``` |
|||
|
|||
4. Respond to the prompts to populate the initial app. |
|||
|
|||
After the process completes successfully, you see a prompt similar to the following: |
|||
|
|||
```bash |
|||
[fsevents] Success: |
|||
"/Users/theuser/repos/publik/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node" |
|||
is installed via remote npm notice created a lockfile as package-lock.json. |
|||
You should commit this file. added 1060 packages in 26.901s |
|||
``` |
|||
|
|||
5. Run the initial application. |
|||
|
|||
```bash |
|||
npm start |
|||
``` |
|||
|
|||
The system prompts you to accept incoming connections. |
|||
|
|||
![Network Connection](./images/network-connections.gif) |
|||
|
|||
6. Choose **Allow**. |
|||
|
|||
7. Open your browser to `http://localhost:8080`. |
|||
|
|||
You should see a simple React app. |
|||
|
|||
![](images/initial-app.gif) |
|||
|
|||
8. Choose **Sign In with Blockstack**. |
|||
|
|||
The application tells you it will **Read your basic info**. |
|||
|
|||
![](images/login.png) |
|||
|
|||
Leave your new application running and move onto the next section. |
|||
|
|||
## Add the `publish_data` scope to sign in requests |
|||
|
|||
Every app that uses Gaia storage must add itself to the user's `profile.json` |
|||
file. The Blockstack browser does this automatically when the `publish_data` |
|||
scope is requested during authentication. For this application, the user files |
|||
stored on Gaia are made visible to others via the `apps` property in the user's |
|||
`profile.json` file. |
|||
|
|||
Modify your authentication request to include the `publish_data` scope. |
|||
|
|||
1. Open `src/components/App.jsx` file. |
|||
|
|||
2. Locate the `handleSignIn` handler method. |
|||
|
|||
```javascript |
|||
handleSignIn(e) { |
|||
e.preventDefault(); |
|||
redirectToSignIn(); |
|||
} |
|||
``` |
|||
|
|||
2. Modify the method to this: |
|||
|
|||
```javascript |
|||
handleSignIn(e) { |
|||
e.preventDefault(); |
|||
const origin = window.location.origin |
|||
redirectToSignIn(origin, origin + '/manifest.json', ['store_write', 'publish_data']) |
|||
} |
|||
``` |
|||
|
|||
By default, authentication requests include the `store_write` scope which |
|||
enables storage. This is what allows you to store information to Gaia. |
|||
|
|||
3. Save your changes. |
|||
4. Go back to your app at `http://localhost:8080/`. |
|||
5. Log out and sign in again. |
|||
|
|||
The authentication request now prompts the user for permission to **Publish |
|||
data stored for the app**. |
|||
|
|||
![](images/publish-data-perm.png) |
|||
|
|||
## Understand Gaia storage methods |
|||
|
|||
Once you authenticate a user with `store_write` and `publish_data`, you can |
|||
begin to manage data for your users. Blockstack JS provides two methods |
|||
`getFile()` and `putFile()` for interacting with Gaia storage. The storage |
|||
methods support all file types. This means you can store SQL, Markdown, JSON, or |
|||
even a custom format. |
|||
|
|||
You can create a meaningful and complex data layer using these two methods. |
|||
Before creating an application, consider fundamental data architecture and make |
|||
some decisions about how you’re modeling data. For example, consider building a |
|||
simple grocery list app. A user should be able to create, read, update, and |
|||
delete grocery lists. |
|||
|
|||
A single file collection stores items as an array nested inside each grocery |
|||
list: |
|||
|
|||
```js |
|||
// grocerylists.json |
|||
{ |
|||
"3255": { |
|||
"items": [ |
|||
"1 Head of Lettuce", |
|||
"Haralson apples" |
|||
] |
|||
}, |
|||
// ...more lists with items |
|||
} |
|||
``` |
|||
|
|||
This is conceptually the simplest way to manage grocery lists. When you read a |
|||
`/grocerylists.json` file with `getFile()`, you get back one or more grocery |
|||
lists and their items. When you write a single list, the `putFile()` method |
|||
overwrites the entire list. So, a write operation for a new or updated grocery |
|||
list must submit all existings lists as well. |
|||
|
|||
Further, because this runs on the client where anything can go wrong. If the |
|||
client-side code encounters a parsing error with a user-input value and you |
|||
could overwrite the entire file with: |
|||
|
|||
`line 6: Parsing Error: Unexpected token.` |
|||
|
|||
Further, a single file makes pagination impossible and if your app stores a |
|||
single file for all list you have less control over file permissions. To avoid |
|||
these issues, you can create an index file that stores an array of IDs. These |
|||
IDs point to a name of another file in a `grocerylists` folder. |
|||
|
|||
![](images/multiple-lists.png) |
|||
|
|||
This design allows you to get only the files you need and avoid accidentally |
|||
overwriting all lists. Further, you’re only updating the index file when you add |
|||
or remove a grocery list; updating a list has no impact. |
|||
|
|||
|
|||
## Add support for user status submission and lookup |
|||
|
|||
In this step, you add three `blockstack.js` methods that support posting of "statuses". These are the `putFile()`, `getFile()`, and `lookupProfile()` methods. |
|||
|
|||
1. Open the `src/components/Profile.jsx` file. |
|||
|
|||
2. Expand the `import from blockstack` statement with data methods. |
|||
|
|||
The `Person` object holds a Blockstack profile. Add `putFile`, `getFile`, |
|||
and `lookupProfile` after `Person`. |
|||
|
|||
When you are done, the import statement should look like the following: |
|||
|
|||
```javascript |
|||
import { |
|||
isSignInPending, |
|||
loadUserData, |
|||
Person, |
|||
getFile, |
|||
putFile, |
|||
lookupProfile |
|||
} from 'blockstack'; |
|||
``` |
|||
|
|||
3. Replace the `constructor()` initial state so that it holds the key properties required by the app. |
|||
|
|||
This code constructs a Blockstack `Person` object to hold the profile. Your constructor should look like this: |
|||
|
|||
```javascript |
|||
constructor(props) { |
|||
super(props); |
|||
|
|||
this.state = { |
|||
person: { |
|||
name() { |
|||
return 'Anonymous'; |
|||
}, |
|||
avatarUrl() { |
|||
return avatarFallbackImage; |
|||
}, |
|||
}, |
|||
username: "", |
|||
newStatus: "", |
|||
statuses: [], |
|||
statusIndex: 0, |
|||
isLoading: false |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
|
|||
4. Locate the `render()` method. |
|||
5. Modify the `render()` method to add a text input and submit button to the application. |
|||
|
|||
The following code echos the `person.name` and `person.avatarURL` |
|||
properties from the profile on the display: |
|||
|
|||
```javascript |
|||
render() { |
|||
const { handleSignOut } = this.props; |
|||
const { person } = this.state; |
|||
const { username } = this.state; |
|||
|
|||
return ( |
|||
!isSignInPending() && person ? |
|||
<div className="container"> |
|||
<div className="row"> |
|||
<div className="col-md-offset-3 col-md-6"> |
|||
<div className="col-md-12"> |
|||
<div className="avatar-section"> |
|||
<img |
|||
src={ person.avatarUrl() ? person.avatarUrl() : avatarFallbackImage } |
|||
className="img-rounded avatar" |
|||
id="avatar-image" |
|||
/> |
|||
<div className="username"> |
|||
<h1> |
|||
<span id="heading-name">{ person.name() ? person.name() |
|||
: 'Nameless Person' }</span> |
|||
</h1> |
|||
<span>{username}</span> |
|||
<span> |
|||
| |
|||
<a onClick={ handleSignOut.bind(this) }>(Logout)</a> |
|||
</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="new-status"> |
|||
<div className="col-md-12"> |
|||
<textarea className="input-status" |
|||
value={this.state.newStatus} |
|||
onChange={e => this.handleNewStatusChange(e)} |
|||
placeholder="Enter a status" |
|||
/> |
|||
</div> |
|||
<div className="col-md-12"> |
|||
<button |
|||
className="btn btn-primary btn-lg" |
|||
onClick={e => this.handleNewStatusSubmit(e)} |
|||
> |
|||
Submit |
|||
</button> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> : null |
|||
); |
|||
} |
|||
``` |
|||
|
|||
This code allows the application to post statuses. It also displays the |
|||
user's Blockstack ID. To display this, your app must extract the ID from the |
|||
user profile data. |
|||
|
|||
6. Locate the `componentWillMount()` method. |
|||
7. Add the `username` property below the `person` property. |
|||
|
|||
You'll use the Blockstack `loadUserData()` method to access the `username`. |
|||
|
|||
|
|||
```javascript |
|||
componentWillMount() { |
|||
this.setState({ |
|||
person: new Person(loadUserData().profile), |
|||
username: loadUserData().username |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
7. Add two methods to handle the status input events: |
|||
|
|||
```javascript |
|||
handleNewStatusChange(event) { |
|||
this.setState({newStatus: event.target.value}) |
|||
} |
|||
|
|||
handleNewStatusSubmit(event) { |
|||
this.saveNewStatus(this.state.newStatus) |
|||
this.setState({ |
|||
newStatus: "" |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
8. Add a `saveNewStatus()` method to save the new statuses. |
|||
|
|||
```javascript |
|||
saveNewStatus(statusText) { |
|||
let statuses = this.state.statuses |
|||
|
|||
let status = { |
|||
id: this.state.statusIndex++, |
|||
text: statusText.trim(), |
|||
created_at: Date.now() |
|||
} |
|||
|
|||
statuses.unshift(status) |
|||
const options = { encrypt: false } |
|||
putFile('statuses.json', JSON.stringify(statuses), options) |
|||
.then(() => { |
|||
this.setState({ |
|||
statuses: statuses |
|||
}) |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
9. Save the `Profile.jsk` file. |
|||
|
|||
After the application compiles successfully, your application should appears as follows: |
|||
|
|||
![](images/display-complete.png) |
|||
|
|||
10. Enter your status in the text box and press the **Submit** button. |
|||
|
|||
At this point, nothing is blogged. In the next section you add code to display |
|||
the statuses back to the user as a blog entry. |
|||
|
|||
## Fetch and display statuses |
|||
|
|||
Update `Profile.jsx` again. |
|||
|
|||
1. Go back to the `render()` method. |
|||
2. Locate the `<div className="new-status">` containing the text input and **Submit** button. |
|||
3. Right after this opening `div` element, add this block. |
|||
|
|||
```javascript |
|||
<div className="col-md-12 statuses"> |
|||
{this.state.isLoading && <span>Loading...</span>} |
|||
{this.state.statuses.map((status) => ( |
|||
<div className="status" key={status.id}> |
|||
{status.text} |
|||
</div> |
|||
) |
|||
)} |
|||
</div> |
|||
``` |
|||
This loads existing state. Your code needs to fetch statuses on page load. |
|||
|
|||
4. Add a new method called `fetchData()` after the `statuses.unshift(status)` section. |
|||
|
|||
```javascript |
|||
|
|||
fetchData() { |
|||
this.setState({ isLoading: true }) |
|||
const options = { decrypt: false } |
|||
getFile('statuses.json', options) |
|||
.then((file) => { |
|||
var statuses = JSON.parse(file || '[]') |
|||
this.setState({ |
|||
person: new Person(loadUserData().profile), |
|||
username: loadUserData().username, |
|||
statusIndex: statuses.length, |
|||
statuses: statuses, |
|||
}) |
|||
}) |
|||
.finally(() => { |
|||
this.setState({ isLoading: false }) |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
5. Call `fetchData()` from the `componentDidMount()` method |
|||
|
|||
```javascript |
|||
|
|||
componentDidMount() { |
|||
this.fetchData() |
|||
} |
|||
``` |
|||
|
|||
6. Save the file. |
|||
|
|||
After the application compiles successfully, users are able to **Submit** |
|||
multiple statuses and review them in the app. |
|||
|
|||
![](images/saving-status.png) |
|||
|
|||
## Change the style |
|||
|
|||
1. Edit the `src/styles/style.css` file. |
|||
2. Replace the content with the following: |
|||
|
|||
|
|||
```css |
|||
/* Globals */ |
|||
a,a:focus,a:hover{color:#fff;} |
|||
html,body{height:100%;text-align:center;background-color:#191b22;} |
|||
body{color:#fff} |
|||
.hide{display:none;} |
|||
.landing-heading{font-family:'Lato',Sans-Serif;font-weight:400;} |
|||
|
|||
/* Buttons */ |
|||
.btn{font-family:'Lato',Sans-Serif;padding:0.5625rem 2.5rem;font-size:0.8125rem;font-weight:400;line-height:1.75rem;border-radius:0!important;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-ms-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;} |
|||
.btn-lg{font-size:1.5rem;padding:0.6875rem 3.4375rem;line-height:2.5rem;} |
|||
.btn:focus,.btn:active:focus,.btn.active:focus{outline:none;} |
|||
.btn-primary{color:#fff;border:1px solid #2C96FF;background-color:#2C96FF;} |
|||
.btn-primary:hover,.btn-primary:focus,.btn-primary:active{color:#fff;border:1px solid #1a6ec0;background-color:#1a6ec0;} |
|||
|
|||
/* Avatar */ |
|||
.avatar{width:100px;height:100px;} |
|||
.avatar-section{margin-bottom:25px;display:flex;text-align:left;} |
|||
.username{margin-left:20px;} |
|||
|
|||
/* Scaffolding */ |
|||
.site-wrapper{display:table;width:100%;height:100vh;min-height:100%;} |
|||
.site-wrapper-inner{display:flex;flex-direction:column;justify-content:center;margin-right:auto;margin-left:auto;width:100%;height:100vh;} |
|||
.panel-authed{padding:0 0 0 0;} |
|||
|
|||
/* Home button */ |
|||
.btn-home-hello{position:absolute;font-family:'Source Code Pro',monospace;font-size:11px;font-weight:400;color:rgba(255,255,255,0.85);top:15px;left:15px;padding:3px 20px;background-color:rgba(255,255,255,0.15);border-radius:6px;-webkit-box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);-moz-box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);} |
|||
|
|||
/* Input */ |
|||
input, textarea{color:#000;padding:10px;} |
|||
.input-status{width:100%;height:70px;border-radius:6px;} |
|||
.new-status{text-align:right;} |
|||
|
|||
/* Statuses */ |
|||
.statuses{padding-top:30px;} |
|||
.status{margin:15px 0px;padding:20px;background-color:#2e2e2e;border-radius:6px} |
|||
``` |
|||
|
|||
3. Save and close the `src/styles/style.css` file. |
|||
|
|||
After the application compiles, you should see the following: |
|||
|
|||
![Multi-reader storage authentication](images/multi-player-storage-status.png) |
|||
|
|||
|
|||
At this point, you have a basic micro-blogging app that users can use to post and |
|||
view statuses. However, there's no way to view other users' statuses. You'll add |
|||
that in the next section. |
|||
|
|||
## Lookup user profiles |
|||
|
|||
Let's now modify the `Profile.jsx` file to display profiles of other users. You'll |
|||
be using the `lookupProfile()` method that you added to the `import` statement |
|||
earlier. `lookupProfile()` takes a single parameter that is the Blockstack ID of |
|||
the profile and returns a profile object. |
|||
|
|||
### Add a new route |
|||
|
|||
Make some changes to the routing structure of your app so that users can view |
|||
other users' profiles by visiting `http://localhost:8080/other_user.id` |
|||
|
|||
|
|||
1. Make sure you are in the root of your `publik` project. |
|||
2. Install `react-router`: |
|||
|
|||
```bash |
|||
npm install --save react-router-dom |
|||
``` |
|||
|
|||
3. Edit `src/index.js` file. |
|||
4. Add an `import` to the file at the top: |
|||
|
|||
```javascript |
|||
import { BrowserRouter } from 'react-router-dom' |
|||
``` |
|||
|
|||
5. Change the `ReactDOM.render()` method in `src/index.js` to: |
|||
|
|||
```javascript |
|||
ReactDOM.render(( |
|||
<BrowserRouter> |
|||
<App /> |
|||
</BrowserRouter> |
|||
), document.getElementById('root')); |
|||
``` |
|||
6. Save and close the `src/index.js` file. |
|||
7. Edit the `src/components/App.jsx` file. |
|||
8. Add the new route by importing the `Switch` and `Route` components from `react-router-dom`: |
|||
|
|||
```javascript |
|||
import { Switch, Route } from 'react-router-dom' |
|||
``` |
|||
9. Locate this line below in the `render()` method: |
|||
|
|||
```javascript |
|||
: <Profile handleSignOut={ this.handleSignOut } /> |
|||
``` |
|||
|
|||
10. Replace it with the following: |
|||
|
|||
```javascript |
|||
: |
|||
<Switch> |
|||
<Route |
|||
path='/:username?' |
|||
render={ |
|||
routeProps => <Profile handleSignOut={ this.handleSignOut } {...routeProps} /> |
|||
} |
|||
/> |
|||
</Switch> |
|||
``` |
|||
|
|||
This sets up a route and captures the route parameter the app will use as the profile lookup username. |
|||
|
|||
11. Save and close the the `src/components/App.jsx` file. |
|||
|
|||
|
|||
### Add a rule to process URL paths with . (dot) |
|||
|
|||
You also need to add a rule to your webpack config so that you can properly |
|||
process URL paths that contain the `.` (dot) character for example, |
|||
`http://localhost:8080/other_user.id` |
|||
|
|||
**NOTE**: In a production app, you must ensure the web server is configured to handle this. |
|||
|
|||
|
|||
1. Open `webpack.config.js` in the root project directory and locate the following line: |
|||
|
|||
```javascript |
|||
historyApiFallback: true, |
|||
``` |
|||
|
|||
2. Replace it with this: |
|||
|
|||
```javascript |
|||
historyApiFallback: { |
|||
disableDotRule: true |
|||
}, |
|||
``` |
|||
|
|||
You will need to run `npm start` again for this change to take effect. Don't |
|||
worry, there is a later step for that to remind you. |
|||
|
|||
3. Save and close the `webpack.config.js` file. |
|||
|
|||
4. Edit the `src/components/Profile.jsx` file. |
|||
5. Add a single method that determines if the app is viewing the local user's profile or another user's profile. |
|||
|
|||
```javascript |
|||
isLocal() { |
|||
return this.props.match.params.username ? false : true |
|||
} |
|||
``` |
|||
|
|||
You use `isLocal()` to check if the user is viewing the local user profile or another user's profile. If it's the local user profile, the app runs the `getFile()` function you added in an earlier step. Otherwise, the app looks up the profile belonging to the `username` using the `lookupProfile()` method. |
|||
|
|||
6. Modify the `fetchData()` method like so: |
|||
|
|||
```javascript |
|||
fetchData() { |
|||
this.setState({ isLoading: true }) |
|||
if (this.isLocal()) { |
|||
const options = { decrypt: false } |
|||
getFile('statuses.json', options) |
|||
.then((file) => { |
|||
var statuses = JSON.parse(file || '[]') |
|||
this.setState({ |
|||
person: new Person(loadUserData().profile), |
|||
username: loadUserData().username, |
|||
statusIndex: statuses.length, |
|||
statuses: statuses, |
|||
}) |
|||
}) |
|||
.finally(() => { |
|||
this.setState({ isLoading: false }) |
|||
}) |
|||
} else { |
|||
const username = this.props.match.params.username |
|||
|
|||
lookupProfile(username) |
|||
.then((profile) => { |
|||
this.setState({ |
|||
person: new Person(profile), |
|||
username: username |
|||
}) |
|||
}) |
|||
.catch((error) => { |
|||
console.log('could not resolve profile') |
|||
}) |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**NOTE**: For `https` deployments, the default Blockstack Core API endpoint for name |
|||
lookups should be changed to point to a core API served over `https`. |
|||
Otherwise, name lookups fail due to browsers blocking mixed content. |
|||
Refer to the [Blockstack.js |
|||
documentation](http://blockstack.github.io/blockstack.js/#getfile) for |
|||
details. |
|||
|
|||
7. Add the following block to `fetchData()` right after the call to `lookupProfile(username)... catch((error)=>{..}` block: |
|||
|
|||
```javascript |
|||
const options = { username: username, decrypt: false } |
|||
getFile('statuses.json', options) |
|||
.then((file) => { |
|||
var statuses = JSON.parse(file || '[]') |
|||
this.setState({ |
|||
statusIndex: statuses.length, |
|||
statuses: statuses |
|||
}) |
|||
}) |
|||
.catch((error) => { |
|||
console.log('could not fetch statuses') |
|||
}) |
|||
.finally(() => { |
|||
this.setState({ isLoading: false }) |
|||
}) |
|||
``` |
|||
|
|||
This fetches the user statuses. |
|||
|
|||
Finally, you must conditionally render the logout button, status input textbox, and submit button so they don't show up when viewing another user's profile. |
|||
|
|||
8. Replace the `render()` method with the following: |
|||
|
|||
```javascript |
|||
render() { |
|||
const { handleSignOut } = this.props; |
|||
const { person } = this.state; |
|||
const { username } = this.state; |
|||
|
|||
return ( |
|||
!isSignInPending() && person ? |
|||
<div className="container"> |
|||
<div className="row"> |
|||
<div className="col-md-offset-3 col-md-6"> |
|||
<div className="col-md-12"> |
|||
<div className="avatar-section"> |
|||
<img |
|||
src={ person.avatarUrl() ? person.avatarUrl() : avatarFallbackImage } |
|||
className="img-rounded avatar" |
|||
id="avatar-image" |
|||
/> |
|||
<div className="username"> |
|||
<h1> |
|||
<span id="heading-name">{ person.name() ? person.name() |
|||
: 'Nameless Person' }</span> |
|||
</h1> |
|||
<span>{username}</span> |
|||
{this.isLocal() && |
|||
<span> |
|||
| |
|||
<a onClick={ handleSignOut.bind(this) }>(Logout)</a> |
|||
</span> |
|||
} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{this.isLocal() && |
|||
<div className="new-status"> |
|||
<div className="col-md-12"> |
|||
<textarea className="input-status" |
|||
value={this.state.newStatus} |
|||
onChange={e => this.handleNewStatusChange(e)} |
|||
placeholder="What's on your mind?" |
|||
/> |
|||
</div> |
|||
<div className="col-md-12 text-right"> |
|||
<button |
|||
className="btn btn-primary btn-lg" |
|||
onClick={e => this.handleNewStatusSubmit(e)} |
|||
> |
|||
Submit |
|||
</button> |
|||
</div> |
|||
</div> |
|||
} |
|||
<div className="col-md-12 statuses"> |
|||
{this.state.isLoading && <span>Loading...</span>} |
|||
{this.state.statuses.map((status) => ( |
|||
<div className="status" key={status.id}> |
|||
{status.text} |
|||
</div> |
|||
) |
|||
)} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> : null |
|||
); |
|||
} |
|||
``` |
|||
|
|||
### This checks to ensure that users are viewing their own profile, by wrapping the **Logout** button and inputs with the `{isLocal() && ...}` condition. |
|||
|
|||
### Put it all together |
|||
|
|||
1. Stop the running application by sending a CTL-C. |
|||
2. Restart the application so that the disabling of the `.` (dot) rule takes effect. |
|||
|
|||
```bash |
|||
npm start |
|||
``` |
|||
|
|||
3. Point your browser to `http://localhost:8080/your_blockstack.id` to see the final application. |
|||
|
|||
|
|||
|
|||
## Wrapping up |
|||
|
|||
Congratulations, you are all done! We hope you've enjoyed learning a bit more |
|||
about Blockstack. To use a working version of the app go |
|||
[here](http://publik.ykliao.com). |
@ -0,0 +1,210 @@ |
|||
--- |
|||
layout: usenew |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Get and use a Blockstack ID |
|||
{:.no_toc} |
|||
|
|||
Through the Blockstack browser you can create an identity. Your identity is a |
|||
point of contact as you interact with others through Dapps. Others may be |
|||
individual users or companies or software. Unless you allow it, none of these |
|||
others have access to anything other than your identity label, for example |
|||
`moxiegirl.id.blockstack`. To learn more about you, others must ask and you can |
|||
choose to share -- or not. |
|||
|
|||
This document explains one type of identity, the Blockstack ID. You learn how to |
|||
create them, as well as when and how to restore them. It contains the following sections: |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
|
|||
## Understand Blockstack IDs |
|||
|
|||
Interacting within the decentralized internet requires that you have at least |
|||
one identity, though you can create several. Your identity is created through a |
|||
registrar. Blockstack maintains a registrar for creating identities that you |
|||
can use to interact with distributed applications (Dapps). |
|||
|
|||
To use the Blockstack Browser or to develop a decentralized application you |
|||
require a Blockstack ID, for example `moxiegirl.id.blockstack`. A Blockstack ID |
|||
is a digital identity that is registered With Blockstack. Your personal data and |
|||
storage are built around this ID. Decentralized applications that want to access |
|||
your data need your identity and your permission. |
|||
|
|||
When you first sign up through the Blockstack browser, you create an initial |
|||
human-readable identity in the `id.blockstack` domain. This initial identity has |
|||
the format: |
|||
|
|||
_`USERNAME`_`.id.blockstack` |
|||
|
|||
The _`USERNAME`_ portion must be unique. You enter an email and password to |
|||
create the initial identity. Blockstack uses the password to: |
|||
|
|||
* seed a _recovery code_ an encrypted string, for example `36mWivFdy0YPH2z31EflpQz/Y0UMrOrJ++lH=0EI7c3mop2JuRBm5W1P0BwXxSTazJsUjOAmC55rFUWINqDqGccLio0iwzGclAhaHGZQ5M52` |
|||
* seed a _recovery key_ which is a squence of words `applied binge crisp pictorial fiery dancing agreeable frogs light finish ping apple` |
|||
|
|||
The email is provided to allow either Blockstack or a decentralized application |
|||
to communicate information to you. In Blockstacks' case, the email is used to |
|||
send you reovery information. |
|||
|
|||
While Blockstack registers your human readable ID and the recovery key. _You_ must |
|||
record the: |
|||
|
|||
* recovery key |
|||
* recovery code (in the order the words apepar) |
|||
* initial password |
|||
|
|||
Blockstack does not store them, so it can't give them to you later if they are |
|||
lost. |
|||
|
|||
Your initial ID is created in the `id.blockstack` domain. The initial identity |
|||
remains primary, and you need this primary ID and its associated information |
|||
(recovery code, recovery key, and password) to use the browser again. |
|||
|
|||
Finally, the `id.blockstack` domain is sponsored by the Blockstack registrar and |
|||
identities on it are free. Once you are using the Blockstack Browser, you can |
|||
create additional identities outside of this domain and controlled by other |
|||
registrars. Coin is required to purchase identities on other domains. |
|||
|
|||
## Create an initial Blockstack ID |
|||
|
|||
To create an inititial Blockstack ID, do the following: |
|||
|
|||
1. Open the [Blockstack web application in your browser](https://browser.blockstack.org/sign-up?redirect=%2F). |
|||
|
|||
The application prompts you for an email address. |
|||
|
|||
![](images/create-id-0.png) |
|||
|
|||
Blockstack uses this email address to send you recovery information. |
|||
|
|||
2. Enter an email address and press **Next**. |
|||
|
|||
The application prompts you to enter a password. Blockstack users this |
|||
password to encrypt your recovery code. You must record and save this |
|||
initial password. |
|||
|
|||
**NOTE**:The Blockstack team cannot restore your password for you. |
|||
|
|||
3. Enter a password, confirm it, and press **Next**. |
|||
|
|||
![](images/create-id-1.png) |
|||
|
|||
The browser prompts you to register a unique username in the `id.blockstack` |
|||
domain. This is your identity in the decentralized internet. The format of the id |
|||
is: |
|||
|
|||
_`username`_`.id.blockstack` |
|||
|
|||
You'll use this initial ID to access the Blockstack Browser. |
|||
|
|||
3. Enter a unique username and press **Check Availability**. |
|||
|
|||
![](images/create-id-2.png) |
|||
|
|||
When you choose an ID that is available, the system responds with the following: |
|||
|
|||
![](images/create-id-3.png) |
|||
|
|||
4. Press **Continue**. |
|||
|
|||
The system prompts you to save your **recovery code**. A recovery code is a |
|||
sequence of words. These words allow you to recover an `id.blockstack` |
|||
that you've created. You should store the words along with their order, for |
|||
example, `#1 applied` and so forth. |
|||
|
|||
5. Click **I have written down all the words** when you are done. |
|||
|
|||
The system places you in the Blockstack browser. You can begin exploring and |
|||
using Dapps. |
|||
|
|||
## Restore a Blockstack ID |
|||
|
|||
When you return to the Blockstack Browser, the browser prompts you to create a |
|||
new Blockstack ID or restore an existing Blockstack ID. If you have a |
|||
Blockstack identity, you can open the browser by restoring the identity. To |
|||
restore an identity, there are two available methods. |
|||
|
|||
Method 1: Supply the identity recovery code (`36mWivFdy0YPH2z31E...`) and the |
|||
password you provided when you _initially_ created your identity. Method 2: |
|||
Supply the recovery key which is a sequence of words (`applied binge ...`) |
|||
|
|||
If you loose either the recovery code or the password you provided when you |
|||
_initially_ created your identity, you can no longer use method 1 to restore |
|||
your identity. If you lose the recovery key, you can no longer use method 2. |
|||
Once you no longer have access to either method, your identity is estranged and |
|||
not accessible by anyone. |
|||
|
|||
### Restore with a recovery key |
|||
|
|||
1. Open the [Blockstack web application in your browser](https://browser.blockstack.org/sign-up?redirect=%2F). |
|||
2. Choose **Restore a Blockstack ID**. |
|||
|
|||
The system displays a dialog where you can enter your recovery code or a |
|||
recovery key. |
|||
|
|||
3. Enter the recovery key. |
|||
|
|||
The recovery key is a squence of words. |
|||
|
|||
![](images/recovery-code.png) |
|||
|
|||
4. Press **Next**. |
|||
|
|||
The system prompts you for an email address. This email can be one you |
|||
entered previously or an entirely new one. Blockstack doesn't store this |
|||
address; it is used during your current Blockstack browser interaction to communicate |
|||
important information with you. |
|||
|
|||
5. Enter an email and press **Next**. |
|||
|
|||
The system prompts you for an password and its confirmation. This password |
|||
can be one you entered previously or an entirely new one. Write this password |
|||
down. You can use the password during your current Blockstack browser |
|||
interaction to reveal your keychain or change your password. Blockstack does |
|||
not store this information past the session. |
|||
|
|||
6. Enter a password and press **Next**. |
|||
|
|||
The system welcomes you back. |
|||
|
|||
![](images/welcome-back.png) |
|||
|
|||
At this point, you can go onto work with Dapps or you can review your recovery key. |
|||
|
|||
### Restore with a recovery code and original password |
|||
|
|||
1. Open the [Blockstack web application in your browser](https://browser.blockstack.org/sign-up?redirect=%2F). |
|||
2. Choose **Restore a Blockstack ID**. |
|||
|
|||
The system displays a dialog where you can enter your recovery code or a |
|||
recovery key. |
|||
|
|||
3. Enter your recovery code. |
|||
|
|||
The recovery code is an encrypted string. |
|||
|
|||
![](images/recovery-code.png) |
|||
|
|||
4. Press **Next**. |
|||
|
|||
The system prompts you for an email address. This email can be one you |
|||
entered previously or an entirely new one. Blockstack doesn't store this |
|||
address; it is used during your current Blockstack browser interaction to |
|||
communicate important information with you. |
|||
|
|||
5. Enter an email and press **Next**. |
|||
|
|||
The system prompts you for an password. This must be the password entered |
|||
when you first created your identity. If you have forgetten this passowrd, |
|||
Blockstack cannot provide it to you. Instead, you must switch to using your |
|||
recovery key rather than your code to restore your identity. |
|||
|
|||
6. Enter your origin password and press **Next**. |
|||
|
|||
The system welcomes you back. |
|||
|
|||
![](images/welcome-back.png) |
|||
|
|||
At this point, you can go work with Dapps or you can review your recovery key. |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 59 KiB |
@ -0,0 +1,80 @@ |
|||
--- |
|||
layout: usenew |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Users Frequently Asked Questions (FAQ) |
|||
{:.no_toc} |
|||
|
|||
This is a general FAQ intended for users of decentralized applications. If you are a developer, refer to the [technical FAQ]. |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
|
|||
## What is the decentralized internet? |
|||
|
|||
The Blockstack ecosystem enables the new decentralized internet. Decentralization means authority moving from central authority to local authority. Simply put, in a decentralized interent, you your data, not the applications or services you use. |
|||
|
|||
Blockstack envisions a world where users control their own data, and power flows back to the users. For example, when you buy something from an online company, that company collects and stores data about you: |
|||
|
|||
* your name |
|||
* the information about the product you bought |
|||
* the number on your credit card |
|||
|
|||
In the decentralied internet, that information stays with you and it isn't |
|||
collected or stored by anyone. Applications that operate in the decentralized |
|||
internet are called _decentralized applications_ or Dapps for short. |
|||
|
|||
## What is Blockstack? |
|||
|
|||
Blockstack is an ecosystem to support both Dapp users and Dapp developers. For |
|||
users, Blockstack makes software to create Blockstack identities and find Dapps. |
|||
For developers, Blockstack develops an ecosystem of software and services that developrs can use to build Dapps. |
|||
|
|||
Blockstack through its |
|||
|
|||
## How do Dapps differ applications I typically use? |
|||
|
|||
Dapps differ from Web applications in two ways: |
|||
|
|||
* **Users have identities not accounts**. The user brings their identity to the |
|||
applications; applications do not require the user to create accounts and |
|||
passwords. |
|||
* **Users own their data**. Users control access to their data. Users decide where to store their data and which applications can read or write to your. Companies and their decentralized applications don't keep or save your data. |
|||
|
|||
|
|||
## Where is my identity kept? |
|||
|
|||
Your identity is a unique string you and other people can read such as `moxiegirl.id`. A private key is a string of letters only you know. When you create an identity, a **hash** is made of your id and your private key registered with Blockstack. |
|||
|
|||
Your personal data storage is built around this ID. Apps that want to access your data use your permission and identity to find it. |
|||
|
|||
You have to remember and keep your private key in a safe place like a password manager. If you lose a private key, _no one else, no person, no software, can help you get your identity back_. |
|||
|
|||
## Can Blockstack control my data or ID when I use it? |
|||
|
|||
No. When you're using a Blockstack client you control your data and ID with a |
|||
private key. As long as no one gets access to your private key, no one can |
|||
control your data or ID. When you use Blockstack, by design, your private keys |
|||
are never sent to any remote servers. |
|||
|
|||
## Do Dapps uwork with a regular browser? |
|||
|
|||
Yes! Dapps run in the web browsers (Chrome, Safari, Internet Explorer, etc.) you know and love. |
|||
|
|||
## What is the Blockstack Browser? |
|||
|
|||
The Blockstack Browser is the Dapp users use to create and manage their identities and configure their data storage. The browser is also where users can go to discover and use apps using Blockstack. |
|||
|
|||
The Blockstack Browser also handles login requests from Dapps that allow their users to log in with Blockstack. When a user clicks a **Log In with Blockstack** button, they are redirected to the Blockstack Browser to approve the request before being logged into the app. |
|||
|
|||
|
|||
## Are Blockstack applications usable today? |
|||
|
|||
Yes! Blockstack applications are as easy to use as normal Web applications, if |
|||
not easier. Moreover, they are just as performant if not more so. |
|||
|
|||
If you install the [Blockstack |
|||
Browser](https://github.com/blockstack/blockstack-browser), or use our |
|||
[Web-hosted Blockstack Browser](https://browser.blockstack.org), you can get |
|||
started with them right away. |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 123 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 99 KiB |
@ -0,0 +1,27 @@ |
|||
# Tutorials |
|||
|
|||
**Note: This data is being digested by blockstack.org. Do not change the formatting of this list unless you first make an adjustment to the code on blockstack.org.** |
|||
|
|||
### Multi-player Storage |
|||
|
|||
- urlSlug: multi-player-storage |
|||
- image: /images/tutorials/multi-player-storage.png |
|||
- description: Build a decentralized micro-blogging app using multi-player Gaia storage. |
|||
|
|||
### Managing Data with Gaia |
|||
|
|||
- urlSlug: managing-data-with-gaia |
|||
- image: /images/tutorials/managing-data-with-gaia.png |
|||
- description: This series will focus on teaching you to think like a Blockstack developer working with Gaia. |
|||
|
|||
### Blockstack Todo |
|||
|
|||
- urlSlug: todo-list |
|||
- image: /images/tutorials/todo-list.png |
|||
- description: Walk through creating a basic Todo application with Blockstack. Learn about Sign In flow and Gaia storage. |
|||
|
|||
### Hello Blockstack |
|||
|
|||
- urlSlug: hello-blockstack |
|||
- image: /images/tutorials/hello-blockstack.jpg |
|||
- description: Build a simple single-page JavaScript application that runs completely client-side without any servers. |
@ -0,0 +1,812 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Blockstack Storage Tutorial |
|||
{:.no_toc} |
|||
|
|||
In this tutorial, you build a micro-blogging application using multi-player Gaia |
|||
storage. Gaia is Blockstack's [decentralized high-performance storage |
|||
system](https://github.com/blockstack/gaia). The tutorial contains the following |
|||
topics: |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
This tutorial does not teach you about authentication. That is covered in depth [in the hello-blockstack tutorial](hello-blockstack). |
|||
|
|||
<!--TODO: authentication tutorial--> |
|||
<!--Strictly speaking not sure it is necessary here to send them out--> |
|||
|
|||
{% include note.html content="This tutorial was written on macOS High Sierra 10.13.4. If you use a Windows or Linux system, you can still follow along. However, you will need to \"translate\" appropriately for your operating system. Additionally, this tutorial assumes you are accessing the Blockstack Browser web application via Chrome. The application you build will also work with a local installation and/or with browsers other than Chrome. " %} |
|||
|
|||
## About this tutorial and the prerequisites you need |
|||
|
|||
The storage application you build with this tutorial is a React.js application |
|||
that is completely decentralized and server-less. While not strictly required |
|||
to follow along, basic familiarity with React.js is helpful. |
|||
|
|||
When complete, the app is capable of the following: |
|||
|
|||
- authenticating users using Blockstack |
|||
- posting new statuses |
|||
- displaying statuses in the user profile |
|||
- looking up the profiles and statuses of other users |
|||
|
|||
The basic identity and storage services are provided by `blockstack.js`. To test |
|||
the application, you need to have already [registered a Blockstack ID](ids-introduction). |
|||
|
|||
The tutorial relies on the `npm` dependency manager. Before you begin, verify |
|||
you have installed `npm` using the `which` command. |
|||
|
|||
```bash |
|||
$ which npm |
|||
/usr/local/bin/npm |
|||
``` |
|||
|
|||
If you don't find `npm` in your system, [install |
|||
it](https://www.npmjs.com/get-npm). Finally, if you get stuck at any point |
|||
while working on the tutorial, the completed [source code is available for |
|||
you](https://github.com/larrysalibra/publik) to check your work against. |
|||
|
|||
## Use npm to install Yeoman and the Blockstack App Generator |
|||
|
|||
You use `npm` to install Yeoman. Yeoman is a generic scaffolding system that |
|||
helps users rapidly start new projects and streamline the maintenance of |
|||
existing projects. |
|||
|
|||
|
|||
1. Install Yeoman. |
|||
|
|||
```bash |
|||
npm install -g yo |
|||
``` |
|||
2. Install the Blockstack application generator. |
|||
|
|||
```bash |
|||
npm install -g generator-blockstack |
|||
``` |
|||
|
|||
<!-- Need to find out if user is required to have React installed before running Yeoman. Doesn't appear to be the case. --> |
|||
|
|||
## Generate and launch the public application |
|||
|
|||
In this section, you build an initial React.js application called Publik. |
|||
|
|||
1. Create a the `publik` directory. |
|||
|
|||
```bash |
|||
mkdir publik |
|||
``` |
|||
|
|||
2. Change into your new directory. |
|||
|
|||
```bash |
|||
cd publik |
|||
``` |
|||
|
|||
3. Use Yeoman and the Blockstack application generator to create your initial `publik` application. |
|||
|
|||
```bash |
|||
yo blockstack:react |
|||
``` |
|||
|
|||
You should see several interactive prompts. |
|||
|
|||
```bash |
|||
$ yo blockstack:react |
|||
? ========================================================================== |
|||
We're constantly looking for ways to make yo better! |
|||
May we anonymously report usage statistics to improve the tool over time? |
|||
More info: https://github.com/yeoman/insight & http://yeoman.io |
|||
========================================================================== No |
|||
|
|||
_-----_ ╭──────────────────────────╮ |
|||
| | │ Welcome to the │ |
|||
|--(o)--| │ Blockstack app │ |
|||
`---------´ │ generator! │ |
|||
( _´U`_ ) ╰──────────────────────────╯ |
|||
/___A___\ / |
|||
| ~ | |
|||
__'.___.'__ |
|||
´ ` |° ´ Y ` |
|||
|
|||
? Are you ready to build a Blockstack app in React? (Y/n) |
|||
``` |
|||
|
|||
4. Respond to the prompts to populate the initial app. |
|||
|
|||
After the process completes successfully, you see a prompt similar to the following: |
|||
|
|||
```bash |
|||
[fsevents] Success: |
|||
"/Users/theuser/repos/publik/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node" |
|||
is installed via remote npm notice created a lockfile as package-lock.json. |
|||
You should commit this file. added 1060 packages in 26.901s |
|||
``` |
|||
|
|||
5. Run the initial application. |
|||
|
|||
```bash |
|||
npm start |
|||
``` |
|||
|
|||
The system prompts you to accept incoming connections. |
|||
|
|||
![Network Connection](./images/network-connections.gif) |
|||
|
|||
6. Choose **Allow**. |
|||
|
|||
7. Open your browser to `http://localhost:8080`. |
|||
|
|||
You should see a simple React app. |
|||
|
|||
![](images/initial-app.gif) |
|||
|
|||
8. Choose **Sign In with Blockstack**. |
|||
|
|||
The application tells you it will **Read your basic info**. |
|||
|
|||
![](images/login.png) |
|||
|
|||
Leave your new application running and move onto the next section. |
|||
|
|||
## Add the `publish_data` scope to sign in requests |
|||
|
|||
Every app that uses Gaia storage must add itself to the user's `profile.json` |
|||
file. The Blockstack browser does this automatically when the `publish_data` |
|||
scope is requested during authentication. For this application, the user files |
|||
stored on Gaia are made visible to others via the `apps` property in the user's |
|||
`profile.json` file. |
|||
|
|||
Modify your authentication request to include the `publish_data` scope. |
|||
|
|||
1. Open `src/components/App.jsx` file. |
|||
|
|||
2. Locate the `handleSignIn` handler method. |
|||
|
|||
```javascript |
|||
handleSignIn(e) { |
|||
e.preventDefault(); |
|||
redirectToSignIn(); |
|||
} |
|||
``` |
|||
|
|||
2. Modify the method to this: |
|||
|
|||
```javascript |
|||
handleSignIn(e) { |
|||
e.preventDefault(); |
|||
const origin = window.location.origin |
|||
redirectToSignIn(origin, origin + '/manifest.json', ['store_write', 'publish_data']) |
|||
} |
|||
``` |
|||
|
|||
By default, authentication requests include the `store_write` scope which |
|||
enables storage. This is what allows you to store information to Gaia. |
|||
|
|||
3. Save your changes. |
|||
4. Go back to your app at `http://localhost:8080/`. |
|||
5. Log out and sign in again. |
|||
|
|||
The authentication request now prompts the user for permission to **Publish |
|||
data stored for the app**. |
|||
|
|||
![](images/publish-data-perm.png) |
|||
|
|||
## Understand Gaia storage methods |
|||
|
|||
Once you authenticate a user with `store_write` and `publish_data`, you can |
|||
begin to manage data for your users. Blockstack JS provides two methods |
|||
`getFile()` and `putFile()` for interacting with Gaia storage. The storage |
|||
methods support all file types. This means you can store SQL, Markdown, JSON, or |
|||
even a custom format. |
|||
|
|||
You can create a meaningful and complex data layer using these two methods. |
|||
Before creating an application, consider fundamental data architecture and make |
|||
some decisions about how you’re modeling data. For example, consider building a |
|||
simple grocery list app. A user should be able to create, read, update, and |
|||
delete grocery lists. |
|||
|
|||
A single file collection stores items as an array nested inside each grocery |
|||
list: |
|||
|
|||
```js |
|||
// grocerylists.json |
|||
{ |
|||
"3255": { |
|||
"items": [ |
|||
"1 Head of Lettuce", |
|||
"Haralson apples" |
|||
] |
|||
}, |
|||
// ...more lists with items |
|||
} |
|||
``` |
|||
|
|||
This is conceptually the simplest way to manage grocery lists. When you read a |
|||
`/grocerylists.json` file with `getFile()`, you get back one or more grocery |
|||
lists and their items. When you write a single list, the `putFile()` method |
|||
overwrites the entire list. So, a write operation for a new or updated grocery |
|||
list must submit all existings lists as well. |
|||
|
|||
Further, because this runs on the client where anything can go wrong. If the |
|||
client-side code encounters a parsing error with a user-input value and you |
|||
could overwrite the entire file with: |
|||
|
|||
`line 6: Parsing Error: Unexpected token.` |
|||
|
|||
Further, a single file makes pagination impossible and if your app stores a |
|||
single file for all list you have less control over file permissions. To avoid |
|||
these issues, you can create an index file that stores an array of IDs. These |
|||
IDs point to a name of another file in a `grocerylists` folder. |
|||
|
|||
![](images/multiple-lists.png) |
|||
|
|||
This design allows you to get only the files you need and avoid accidentally |
|||
overwriting all lists. Further, you’re only updating the index file when you add |
|||
or remove a grocery list; updating a list has no impact. |
|||
|
|||
|
|||
## Add support for user status submission and lookup |
|||
|
|||
In this step, you add three `blockstack.js` methods that support posting of "statuses". These are the `putFile()`, `getFile()`, and `lookupProfile()` methods. |
|||
|
|||
1. Open the `src/components/Profile.jsx` file. |
|||
|
|||
2. Expand the `import from blockstack` statement with data methods. |
|||
|
|||
The `Person` object holds a Blockstack profile. Add `putFile`, `getFile`, |
|||
and `lookupProfile` after `Person`. |
|||
|
|||
When you are done, the import statement should look like the following: |
|||
|
|||
```javascript |
|||
import { |
|||
isSignInPending, |
|||
loadUserData, |
|||
Person, |
|||
getFile, |
|||
putFile, |
|||
lookupProfile |
|||
} from 'blockstack'; |
|||
``` |
|||
|
|||
3. Replace the `constructor()` initial state so that it holds the key properties required by the app. |
|||
|
|||
This code constructs a Blockstack `Person` object to hold the profile. Your constructor should look like this: |
|||
|
|||
```javascript |
|||
constructor(props) { |
|||
super(props); |
|||
|
|||
this.state = { |
|||
person: { |
|||
name() { |
|||
return 'Anonymous'; |
|||
}, |
|||
avatarUrl() { |
|||
return avatarFallbackImage; |
|||
}, |
|||
}, |
|||
username: "", |
|||
newStatus: "", |
|||
statuses: [], |
|||
statusIndex: 0, |
|||
isLoading: false |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
|
|||
4. Locate the `render()` method. |
|||
5. Modify the `render()` method to add a text input and submit button to the application. |
|||
|
|||
The following code echos the `person.name` and `person.avatarURL` |
|||
properties from the profile on the display: |
|||
|
|||
```javascript |
|||
render() { |
|||
const { handleSignOut } = this.props; |
|||
const { person } = this.state; |
|||
const { username } = this.state; |
|||
|
|||
return ( |
|||
!isSignInPending() && person ? |
|||
<div className="container"> |
|||
<div className="row"> |
|||
<div className="col-md-offset-3 col-md-6"> |
|||
<div className="col-md-12"> |
|||
<div className="avatar-section"> |
|||
<img |
|||
src={ person.avatarUrl() ? person.avatarUrl() : avatarFallbackImage } |
|||
className="img-rounded avatar" |
|||
id="avatar-image" |
|||
/> |
|||
<div className="username"> |
|||
<h1> |
|||
<span id="heading-name">{ person.name() ? person.name() |
|||
: 'Nameless Person' }</span> |
|||
</h1> |
|||
<span>{username}</span> |
|||
<span> |
|||
| |
|||
<a onClick={ handleSignOut.bind(this) }>(Logout)</a> |
|||
</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="new-status"> |
|||
<div className="col-md-12"> |
|||
<textarea className="input-status" |
|||
value={this.state.newStatus} |
|||
onChange={e => this.handleNewStatusChange(e)} |
|||
placeholder="Enter a status" |
|||
/> |
|||
</div> |
|||
<div className="col-md-12"> |
|||
<button |
|||
className="btn btn-primary btn-lg" |
|||
onClick={e => this.handleNewStatusSubmit(e)} |
|||
> |
|||
Submit |
|||
</button> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> : null |
|||
); |
|||
} |
|||
``` |
|||
|
|||
This code allows the application to post statuses. It also displays the |
|||
user's Blockstack ID. To display this, your app must extract the ID from the |
|||
user profile data. |
|||
|
|||
6. Locate the `componentWillMount()` method. |
|||
7. Add the `username` property below the `person` property. |
|||
|
|||
You'll use the Blockstack `loadUserData()` method to access the `username`. |
|||
|
|||
|
|||
```javascript |
|||
componentWillMount() { |
|||
this.setState({ |
|||
person: new Person(loadUserData().profile), |
|||
username: loadUserData().username |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
7. Add two methods to handle the status input events: |
|||
|
|||
```javascript |
|||
handleNewStatusChange(event) { |
|||
this.setState({newStatus: event.target.value}) |
|||
} |
|||
|
|||
handleNewStatusSubmit(event) { |
|||
this.saveNewStatus(this.state.newStatus) |
|||
this.setState({ |
|||
newStatus: "" |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
8. Add a `saveNewStatus()` method to save the new statuses. |
|||
|
|||
```javascript |
|||
saveNewStatus(statusText) { |
|||
let statuses = this.state.statuses |
|||
|
|||
let status = { |
|||
id: this.state.statusIndex++, |
|||
text: statusText.trim(), |
|||
created_at: Date.now() |
|||
} |
|||
|
|||
statuses.unshift(status) |
|||
const options = { encrypt: false } |
|||
putFile('statuses.json', JSON.stringify(statuses), options) |
|||
.then(() => { |
|||
this.setState({ |
|||
statuses: statuses |
|||
}) |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
9. Save the `Profile.jsk` file. |
|||
|
|||
After the application compiles successfully, your application should appears as follows: |
|||
|
|||
![](images/display-complete.png) |
|||
|
|||
10. Enter your status in the text box and press the **Submit** button. |
|||
|
|||
At this point, nothing is blogged. In the next section you add code to display |
|||
the statuses back to the user as a blog entry. |
|||
|
|||
## Fetch and display statuses |
|||
|
|||
Update `Profile.jsx` again. |
|||
|
|||
1. Go back to the `render()` method. |
|||
2. Locate the `<div className="new-status">` containing the text input and **Submit** button. |
|||
3. Right after this opening `div` element, add this block. |
|||
|
|||
```javascript |
|||
<div className="col-md-12 statuses"> |
|||
{this.state.isLoading && <span>Loading...</span>} |
|||
{this.state.statuses.map((status) => ( |
|||
<div className="status" key={status.id}> |
|||
{status.text} |
|||
</div> |
|||
) |
|||
)} |
|||
</div> |
|||
``` |
|||
This loads existing state. Your code needs to fetch statuses on page load. |
|||
|
|||
4. Add a new method called `fetchData()` after the `statuses.unshift(status)` section. |
|||
|
|||
```javascript |
|||
|
|||
fetchData() { |
|||
this.setState({ isLoading: true }) |
|||
const options = { decrypt: false } |
|||
getFile('statuses.json', options) |
|||
.then((file) => { |
|||
var statuses = JSON.parse(file || '[]') |
|||
this.setState({ |
|||
person: new Person(loadUserData().profile), |
|||
username: loadUserData().username, |
|||
statusIndex: statuses.length, |
|||
statuses: statuses, |
|||
}) |
|||
}) |
|||
.finally(() => { |
|||
this.setState({ isLoading: false }) |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
5. Call `fetchData()` from the `componentDidMount()` method |
|||
|
|||
```javascript |
|||
|
|||
componentDidMount() { |
|||
this.fetchData() |
|||
} |
|||
``` |
|||
|
|||
6. Save the file. |
|||
|
|||
After the application compiles successfully, users are able to **Submit** |
|||
multiple statuses and review them in the app. |
|||
|
|||
![](images/saving-status.png) |
|||
|
|||
## Change the style |
|||
|
|||
1. Edit the `src/styles/style.css` file. |
|||
2. Replace the content with the following: |
|||
|
|||
|
|||
```css |
|||
/* Globals */ |
|||
a,a:focus,a:hover{color:#fff;} |
|||
html,body{height:100%;text-align:center;background-color:#191b22;} |
|||
body{color:#fff} |
|||
.hide{display:none;} |
|||
.landing-heading{font-family:'Lato',Sans-Serif;font-weight:400;} |
|||
|
|||
/* Buttons */ |
|||
.btn{font-family:'Lato',Sans-Serif;padding:0.5625rem 2.5rem;font-size:0.8125rem;font-weight:400;line-height:1.75rem;border-radius:0!important;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-ms-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;} |
|||
.btn-lg{font-size:1.5rem;padding:0.6875rem 3.4375rem;line-height:2.5rem;} |
|||
.btn:focus,.btn:active:focus,.btn.active:focus{outline:none;} |
|||
.btn-primary{color:#fff;border:1px solid #2C96FF;background-color:#2C96FF;} |
|||
.btn-primary:hover,.btn-primary:focus,.btn-primary:active{color:#fff;border:1px solid #1a6ec0;background-color:#1a6ec0;} |
|||
|
|||
/* Avatar */ |
|||
.avatar{width:100px;height:100px;} |
|||
.avatar-section{margin-bottom:25px;display:flex;text-align:left;} |
|||
.username{margin-left:20px;} |
|||
|
|||
/* Scaffolding */ |
|||
.site-wrapper{display:table;width:100%;height:100vh;min-height:100%;} |
|||
.site-wrapper-inner{display:flex;flex-direction:column;justify-content:center;margin-right:auto;margin-left:auto;width:100%;height:100vh;} |
|||
.panel-authed{padding:0 0 0 0;} |
|||
|
|||
/* Home button */ |
|||
.btn-home-hello{position:absolute;font-family:'Source Code Pro',monospace;font-size:11px;font-weight:400;color:rgba(255,255,255,0.85);top:15px;left:15px;padding:3px 20px;background-color:rgba(255,255,255,0.15);border-radius:6px;-webkit-box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);-moz-box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);} |
|||
|
|||
/* Input */ |
|||
input, textarea{color:#000;padding:10px;} |
|||
.input-status{width:100%;height:70px;border-radius:6px;} |
|||
.new-status{text-align:right;} |
|||
|
|||
/* Statuses */ |
|||
.statuses{padding-top:30px;} |
|||
.status{margin:15px 0px;padding:20px;background-color:#2e2e2e;border-radius:6px} |
|||
``` |
|||
|
|||
3. Save and close the `src/styles/style.css` file. |
|||
|
|||
After the application compiles, you should see the following: |
|||
|
|||
![Multi-reader storage authentication](images/multi-player-storage-status.png) |
|||
|
|||
|
|||
At this point, you have a basic micro-blogging app that users can use to post and |
|||
view statuses. However, there's no way to view other users' statuses. You'll add |
|||
that in the next section. |
|||
|
|||
## Lookup user profiles |
|||
|
|||
Let's now modify the `Profile.jsx` file to display profiles of other users. You'll |
|||
be using the `lookupProfile()` method that you added to the `import` statement |
|||
earlier. `lookupProfile()` takes a single parameter that is the Blockstack ID of |
|||
the profile and returns a profile object. |
|||
|
|||
### Add a new route |
|||
|
|||
Make some changes to the routing structure of your app so that users can view |
|||
other users' profiles by visiting `http://localhost:8080/other_user.id` |
|||
|
|||
|
|||
1. Make sure you are in the root of your `publik` project. |
|||
2. Install `react-router`: |
|||
|
|||
```bash |
|||
npm install --save react-router-dom |
|||
``` |
|||
|
|||
3. Edit `src/index.js` file. |
|||
4. Add an `import` to the file at the top: |
|||
|
|||
```javascript |
|||
import { BrowserRouter } from 'react-router-dom' |
|||
``` |
|||
|
|||
5. Change the `ReactDOM.render()` method in `src/index.js` to: |
|||
|
|||
```javascript |
|||
ReactDOM.render(( |
|||
<BrowserRouter> |
|||
<App /> |
|||
</BrowserRouter> |
|||
), document.getElementById('root')); |
|||
``` |
|||
6. Save and close the `src/index.js` file. |
|||
7. Edit the `src/components/App.jsx` file. |
|||
8. Add the new route by importing the `Switch` and `Route` components from `react-router-dom`: |
|||
|
|||
```javascript |
|||
import { Switch, Route } from 'react-router-dom' |
|||
``` |
|||
9. Locate this line below in the `render()` method: |
|||
|
|||
```javascript |
|||
: <Profile handleSignOut={ this.handleSignOut } /> |
|||
``` |
|||
|
|||
10. Replace it with the following: |
|||
|
|||
```javascript |
|||
: |
|||
<Switch> |
|||
<Route |
|||
path='/:username?' |
|||
render={ |
|||
routeProps => <Profile handleSignOut={ this.handleSignOut } {...routeProps} /> |
|||
} |
|||
/> |
|||
</Switch> |
|||
``` |
|||
|
|||
This sets up a route and captures the route parameter the app will use as the profile lookup username. |
|||
|
|||
11. Save and close the the `src/components/App.jsx` file. |
|||
|
|||
|
|||
### Add a rule to process URL paths with . (dot) |
|||
|
|||
You also need to add a rule to your webpack config so that you can properly |
|||
process URL paths that contain the `.` (dot) character for example, |
|||
`http://localhost:8080/other_user.id` |
|||
|
|||
**NOTE**: In a production app, you must ensure the web server is configured to handle this. |
|||
|
|||
|
|||
1. Open `webpack.config.js` in the root project directory and locate the following line: |
|||
|
|||
```javascript |
|||
historyApiFallback: true, |
|||
``` |
|||
|
|||
2. Replace it with this: |
|||
|
|||
```javascript |
|||
historyApiFallback: { |
|||
disableDotRule: true |
|||
}, |
|||
``` |
|||
|
|||
You will need to run `npm start` again for this change to take effect. Don't |
|||
worry, there is a later step for that to remind you. |
|||
|
|||
3. Save and close the `webpack.config.js` file. |
|||
|
|||
4. Edit the `src/components/Profile.jsx` file. |
|||
5. Add a single method that determines if the app is viewing the local user's profile or another user's profile. |
|||
|
|||
```javascript |
|||
isLocal() { |
|||
return this.props.match.params.username ? false : true |
|||
} |
|||
``` |
|||
|
|||
You use `isLocal()` to check if the user is viewing the local user profile or another user's profile. If it's the local user profile, the app runs the `getFile()` function you added in an earlier step. Otherwise, the app looks up the profile belonging to the `username` using the `lookupProfile()` method. |
|||
|
|||
6. Modify the `fetchData()` method like so: |
|||
|
|||
```javascript |
|||
fetchData() { |
|||
this.setState({ isLoading: true }) |
|||
if (this.isLocal()) { |
|||
const options = { decrypt: false } |
|||
getFile('statuses.json', options) |
|||
.then((file) => { |
|||
var statuses = JSON.parse(file || '[]') |
|||
this.setState({ |
|||
person: new Person(loadUserData().profile), |
|||
username: loadUserData().username, |
|||
statusIndex: statuses.length, |
|||
statuses: statuses, |
|||
}) |
|||
}) |
|||
.finally(() => { |
|||
this.setState({ isLoading: false }) |
|||
}) |
|||
} else { |
|||
const username = this.props.match.params.username |
|||
|
|||
lookupProfile(username) |
|||
.then((profile) => { |
|||
this.setState({ |
|||
person: new Person(profile), |
|||
username: username |
|||
}) |
|||
}) |
|||
.catch((error) => { |
|||
console.log('could not resolve profile') |
|||
}) |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**NOTE**: For `https` deployments, the default Blockstack Core API endpoint for name |
|||
lookups should be changed to point to a core API served over `https`. |
|||
Otherwise, name lookups fail due to browsers blocking mixed content. |
|||
Refer to the [Blockstack.js |
|||
documentation](http://blockstack.github.io/blockstack.js/#getfile) for |
|||
details. |
|||
|
|||
7. Add the following block to `fetchData()` right after the call to `lookupProfile(username)... catch((error)=>{..}` block: |
|||
|
|||
```javascript |
|||
const options = { username: username, decrypt: false } |
|||
getFile('statuses.json', options) |
|||
.then((file) => { |
|||
var statuses = JSON.parse(file || '[]') |
|||
this.setState({ |
|||
statusIndex: statuses.length, |
|||
statuses: statuses |
|||
}) |
|||
}) |
|||
.catch((error) => { |
|||
console.log('could not fetch statuses') |
|||
}) |
|||
.finally(() => { |
|||
this.setState({ isLoading: false }) |
|||
}) |
|||
``` |
|||
|
|||
This fetches the user statuses. |
|||
|
|||
Finally, you must conditionally render the logout button, status input textbox, and submit button so they don't show up when viewing another user's profile. |
|||
|
|||
8. Replace the `render()` method with the following: |
|||
|
|||
```javascript |
|||
render() { |
|||
const { handleSignOut } = this.props; |
|||
const { person } = this.state; |
|||
const { username } = this.state; |
|||
|
|||
return ( |
|||
!isSignInPending() && person ? |
|||
<div className="container"> |
|||
<div className="row"> |
|||
<div className="col-md-offset-3 col-md-6"> |
|||
<div className="col-md-12"> |
|||
<div className="avatar-section"> |
|||
<img |
|||
src={ person.avatarUrl() ? person.avatarUrl() : avatarFallbackImage } |
|||
className="img-rounded avatar" |
|||
id="avatar-image" |
|||
/> |
|||
<div className="username"> |
|||
<h1> |
|||
<span id="heading-name">{ person.name() ? person.name() |
|||
: 'Nameless Person' }</span> |
|||
</h1> |
|||
<span>{username}</span> |
|||
{this.isLocal() && |
|||
<span> |
|||
| |
|||
<a onClick={ handleSignOut.bind(this) }>(Logout)</a> |
|||
</span> |
|||
} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{this.isLocal() && |
|||
<div className="new-status"> |
|||
<div className="col-md-12"> |
|||
<textarea className="input-status" |
|||
value={this.state.newStatus} |
|||
onChange={e => this.handleNewStatusChange(e)} |
|||
placeholder="What's on your mind?" |
|||
/> |
|||
</div> |
|||
<div className="col-md-12 text-right"> |
|||
<button |
|||
className="btn btn-primary btn-lg" |
|||
onClick={e => this.handleNewStatusSubmit(e)} |
|||
> |
|||
Submit |
|||
</button> |
|||
</div> |
|||
</div> |
|||
} |
|||
<div className="col-md-12 statuses"> |
|||
{this.state.isLoading && <span>Loading...</span>} |
|||
{this.state.statuses.map((status) => ( |
|||
<div className="status" key={status.id}> |
|||
{status.text} |
|||
</div> |
|||
) |
|||
)} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> : null |
|||
); |
|||
} |
|||
``` |
|||
|
|||
### This checks to ensure that users are viewing their own profile, by wrapping the **Logout** button and inputs with the `{isLocal() && ...}` condition. |
|||
|
|||
### Put it all together |
|||
|
|||
1. Stop the running application by sending a CTL-C. |
|||
2. Restart the application so that the disabling of the `.` (dot) rule takes effect. |
|||
|
|||
```bash |
|||
npm start |
|||
``` |
|||
|
|||
3. Point your browser to `http://localhost:8080/your_blockstack.id` to see the final application. |
|||
|
|||
|
|||
|
|||
## Wrapping up |
|||
|
|||
Congratulations, you are all done! We hope you've enjoyed learning a bit more |
|||
about Blockstack. To use a working version of the app go |
|||
[here](http://publik.ykliao.com). |
@ -0,0 +1,86 @@ |
|||
--- |
|||
layout: usenew |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Introduction to the Blockstack Browser |
|||
|
|||
{:.no_toc} |
|||
|
|||
The Blockstack Browser gives users the ability to explore and use the |
|||
decentralized applications (Dapps). Dapps are a new way to interact with the |
|||
internet. Dapps give users control of their data. Data about them personally, |
|||
name, birthdate, phone number and data about what they do such as visiting a |
|||
website or buying an item. |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
|
|||
## Understand the Blockstack Browser |
|||
|
|||
Through the Blockstack browser application you can create an identity. An identity |
|||
represents you as you interact with others through Dapps. The Blockstack |
|||
Browser is itself, a simple Dapp. It allows you to: |
|||
|
|||
* create one or more identities |
|||
* send and receive bitcoin |
|||
* manage the storage of your profile and application data |
|||
* find and launch Dapps |
|||
|
|||
There are two editions of the Blockstack Browser, the web edition is an |
|||
application you access through your web browser by simply <a |
|||
href="https://browser.blockstack.org/" target="\_blank">visiting the application's |
|||
address</a> in your computer's browser. You can also install the browser as a |
|||
client application on your computer. |
|||
|
|||
If all you want to do is create, manage, and fund an identity and then interact |
|||
with Dapps, you can simply use the web edition. If you have concerns about net |
|||
censorship, heightened security concerns, or want to develop a Dapp yourself, you may |
|||
want to download and install the browser's client edition. |
|||
|
|||
To use the web application to create an identity, |
|||
|
|||
## Using the Browser on public computers |
|||
|
|||
Before you use the web application, it is important to note that once you log |
|||
into the application with the brower, your session does not expire until you |
|||
choose **Settings > RESET BROWSER**. For this reason, you should be careful |
|||
when using the browser on public computers. |
|||
|
|||
If you are in a library, for example, and log into the browser, simply |
|||
closing the tab or even rebooting the computer does not log you out. Instead, |
|||
you should be sure to choose **Settings > RESET BROWSER** before leaving the web |
|||
application. |
|||
|
|||
For more informatin about your identity and the browser, see [Get and use a Blockstack ID](ids-introduction). |
|||
|
|||
## Overview of the browser functions |
|||
|
|||
The Blockstack Browser allows you to use the new internet. Toward this end, the Browser navigation has the following areas: |
|||
|
|||
![](images/navigation-top.png) |
|||
|
|||
<table class="uk-table"> |
|||
<thead> |
|||
<th>Item</th> |
|||
<th>Purpose</th> |
|||
</thead> |
|||
<tbody> |
|||
<tr> |
|||
<td>Home</td> |
|||
<td>Takes you to the initial page. This page contains a list of the available applications. You can also find available applications at the <a href="https://app.co/blockstack" target="\_blank">App.co</a> site.</td> |
|||
</tr> |
|||
<tr> |
|||
<td>IDs</td> |
|||
<td>Allows you to establish the identity of your primary ID. You also go here to create additional, sub identities.</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Wallet</td> |
|||
<td>Allows you to send and receive Bitcoin.</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Settings</td> |
|||
<td>Configure settings for storage, change session password, reset the Browser, and more.</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
@ -0,0 +1,17 @@ |
|||
--- |
|||
layout: usenew |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
|
|||
|
|||
On this page, you learn more about working with **IDs**, the **Wallet**, and **Settings**. |
|||
|
|||
## Working with IDS |
|||
|
|||
Blockstack allows you to create one free identity in the Blockstack |
|||
_namespace_. A namespace a top-level category in the Blockstack Ecosystem. A |
|||
namespace is similar to the domains you are familiar with from using the web, |
|||
`google.com` is domain and so is the `gouvernement.fr` domain. An identity in |
|||
the `.blockstack` namespace has the _`username`_`.id.blockstack` format. |
|||
|
|||
You can also purchase your own identity (`.id`) using bitcoin (BTC). A name's price depends on its length in characters and the current Bitcoin fees. For example, ranges such as .00831 and .001943 BTC 20-80 US dollars is not unusual. |
@ -0,0 +1,80 @@ |
|||
--- |
|||
layout: usenew |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Users Frequently Asked Questions (FAQ) |
|||
{:.no_toc} |
|||
|
|||
This is a general FAQ intended for users of decentralized applications. If you are a developer, refer to the [technical FAQ]. |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
|
|||
## What is the decentralized internet? |
|||
|
|||
The Blockstack ecosystem enables the new decentralized internet. Decentralization means authority moving from central authority to local authority. Simply put, in a decentralized interent, you your data, not the applications or services you use. |
|||
|
|||
Blockstack envisions a world where users control their own data, and power flows back to the users. For example, when you buy something from an online company, that company collects and stores data about you: |
|||
|
|||
* your name |
|||
* the information about the product you bought |
|||
* the number on your credit card |
|||
|
|||
In the decentralied internet, that information stays with you and it isn't |
|||
collected or stored by anyone. Applications that operate in the decentralized |
|||
internet are called _decentralized applications_ or Dapps for short. |
|||
|
|||
## What is Blockstack? |
|||
|
|||
Blockstack is an ecosystem to support both Dapp users and Dapp developers. For |
|||
users, Blockstack makes software to create Blockstack identities and find Dapps. |
|||
For developers, Blockstack develops an ecosystem of software and services that developrs can use to build Dapps. |
|||
|
|||
Blockstack through its |
|||
|
|||
## How do Dapps differ applications I typically use? |
|||
|
|||
Dapps differ from Web applications in two ways: |
|||
|
|||
* **Users have identities not accounts**. The user brings their identity to the |
|||
applications; applications do not require the user to create accounts and |
|||
passwords. |
|||
* **Users own their data**. Users control access to their data. Users decide where to store their data and which applications can read or write to your. Companies and their decentralized applications don't keep or save your data. |
|||
|
|||
|
|||
## Where is my identity kept? |
|||
|
|||
Your identity is a unique string you and other people can read such as `moxiegirl.id`. A private key is a string of letters only you know. When you create an identity, a **hash** is made of your id and your private key registered with Blockstack. |
|||
|
|||
Your personal data storage is built around this ID. Apps that want to access your data use your permission and identity to find it. |
|||
|
|||
You have to remember and keep your private key in a safe place like a password manager. If you lose a private key, _no one else, no person, no software, can help you get your identity back_. |
|||
|
|||
## Can Blockstack control my data or ID when I use it? |
|||
|
|||
No. When you're using a Blockstack client you control your data and ID with a |
|||
private key. As long as no one gets access to your private key, no one can |
|||
control your data or ID. When you use Blockstack, by design, your private keys |
|||
are never sent to any remote servers. |
|||
|
|||
## Do Dapps uwork with a regular browser? |
|||
|
|||
Yes! Dapps run in the web browsers (Chrome, Safari, Internet Explorer, etc.) you know and love. |
|||
|
|||
## What is the Blockstack Browser? |
|||
|
|||
The Blockstack Browser is the Dapp users use to create and manage their identities and configure their data storage. The browser is also where users can go to discover and use apps using Blockstack. |
|||
|
|||
The Blockstack Browser also handles login requests from Dapps that allow their users to log in with Blockstack. When a user clicks a **Log In with Blockstack** button, they are redirected to the Blockstack Browser to approve the request before being logged into the app. |
|||
|
|||
|
|||
## Are Blockstack applications usable today? |
|||
|
|||
Yes! Blockstack applications are as easy to use as normal Web applications, if |
|||
not easier. Moreover, they are just as performant if not more so. |
|||
|
|||
If you install the [Blockstack |
|||
Browser](https://github.com/blockstack/blockstack-browser), or use our |
|||
[Web-hosted Blockstack Browser](https://browser.blockstack.org), you can get |
|||
started with them right away. |
@ -0,0 +1,330 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Hello, Blockstack Tutorial |
|||
|
|||
In this tutorial, you generate a simple application on Blockstack. The application |
|||
is a single-page application (SPA) that runs completely client-side. The |
|||
application has no backend API to talk to, other than the identity and storage |
|||
API that the user provides. In this sense, the application is a completely |
|||
decentralized, server-less application. You work through the following sections: |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
{% include note.html content="This tutorial was written on macOS High Sierra 10.13.4. If you use a Windows or Linux system, you can still follow along. However, you will need to \"translate\" appropriately for your operating system. Additionally, this tutorial assumes you are accessing the Blockstack Browser web application via Chrome. The application you build will also work with a local installation and/or with browsers other than Chrome. " %} |
|||
|
|||
## About this tutorial and the prerequisites you need |
|||
|
|||
For this tutorial, we will use the following tools: |
|||
|
|||
- `npm` to manage dependencies and scripts |
|||
- `browserify` to compile node code into browser-ready code |
|||
- `blockstack.js` to authenticate the user and work with the user's identity/profile information |
|||
|
|||
The application you build is a React.js application that is completely |
|||
decentralized and server-less. While not strictly required to follow along, |
|||
basic familiarity with React.js is helpful. |
|||
|
|||
When complete, the app is capable of the following: |
|||
|
|||
- authenticating users using Blockstack |
|||
- posting new statuses |
|||
- displaying statuses in the user profile |
|||
- looking up the profiles and statuses of other users |
|||
|
|||
The basic identity and storage services are provided by `blockstack.js`. To test |
|||
the application, you need to have already registered a Blockstack ID. |
|||
|
|||
The tutorial relies on the `npm` dependency manager. Before you begin, verify |
|||
you have installed `npm` using the `which` command to verify. |
|||
|
|||
```bash |
|||
$ which npm |
|||
/usr/local/bin/npm |
|||
``` |
|||
|
|||
If you don't find `npm` in your system, [install |
|||
it](https://www.npmjs.com/get-npm). |
|||
|
|||
Finally, make sure you have [created at least one Blockstack ID]({{ site.baseurl }}/browser/ids-introduction.html#create-an-initial-blockstack-id). You'll use this ID to interat with the application. |
|||
|
|||
## Use npm to install Yeoman and the Blockstack App Generator |
|||
|
|||
You use `npm` to install Yeoman. Yeoman is a generic scaffolding system that |
|||
helps users rapidly start new projects and streamline the maintenance of |
|||
existing projects. |
|||
|
|||
|
|||
1. Install Yeoman. |
|||
|
|||
```bash |
|||
npm install -g yo |
|||
``` |
|||
2. Install the Blockstack application generator. |
|||
|
|||
```bash |
|||
npm install -g generator-blockstack |
|||
``` |
|||
|
|||
## Generate an initial Blockstack application |
|||
|
|||
In this section, you build an initial React.js application called `hello-world-tutorial`. |
|||
|
|||
1. Create the `hello-world-tutorial` directory. |
|||
|
|||
```bash |
|||
mkdir hello-world-tutorial |
|||
``` |
|||
|
|||
2. Change into your new directory. |
|||
|
|||
```bash |
|||
cd hello-world-tutorial |
|||
``` |
|||
|
|||
3. Use Yeoman and the Blockstack application generator to create your initial `hello-world-tutorial` application. |
|||
|
|||
```bash |
|||
yo blockstack |
|||
``` |
|||
|
|||
You should see several interactive prompts. |
|||
|
|||
```bash |
|||
$ yo blockstack |
|||
|
|||
_-----_ ╭──────────────────────────╮ |
|||
| | │ Welcome to the │ |
|||
|--(o)--| │ Blockstack app │ |
|||
`---------´ │ generator! │ |
|||
( _´U`_ ) ╰──────────────────────────╯ |
|||
/___A___\ / |
|||
| ~ | |
|||
__'.___.'__ |
|||
´ ` |° ´ Y ` |
|||
|
|||
? Are you ready to build a Blockstack app in React? (Y/n) |
|||
``` |
|||
|
|||
4. Respond to the prompts to populate the initial app. |
|||
|
|||
After the process completes successfully, you see a prompt similar to the following: |
|||
|
|||
```bash |
|||
... |
|||
create public/icon-192x192.png |
|||
create public/index.html |
|||
create public/robots.txt |
|||
create public/manifest.json |
|||
|
|||
|
|||
I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself. |
|||
``` |
|||
|
|||
Depending on your environment you may have some problems with the `npm` packages. Go ahead and fix these before continuing to the next section. |
|||
|
|||
## Review the basic application structure |
|||
|
|||
The initial application you create is a generic Javascript application you run |
|||
with a local express node. Before you continue, take a moment to examine the |
|||
structure of this generic application structure: |
|||
|
|||
| File | Description | |
|||
|------------------|-----------------------------------| |
|||
| .editorconfig | Sets universal values for editor. | |
|||
| .gitignore | Git configuration file. | |
|||
| firebase.json | Configuragion for mobile application.| |
|||
| package.json | Specifies required packages. | |
|||
| requires.js | A Javascript module loader. | |
|||
| server.js | Simple static server configuration.| |
|||
|
|||
In the `public` folder you find these files: |
|||
|
|||
| File | Description | |
|||
|------------------|-----------------------------------| |
|||
| app.css | Contains application styles. | |
|||
| app.js | Main application file. | |
|||
| boostrap.min.css | Minifield css for production. | |
|||
| icon-192x192.png | Application icon | |
|||
| index.html | Single page. | |
|||
| manifest.json | Tells the browser about the application and how it should behave.| |
|||
| robots.txt | Configures crawling and indexing. | |
|||
|
|||
The simple static file server in the `server.js`file serves all of the files in |
|||
the `/public` directory, including `index.html`, `app.js`, `bootstrap.min.css` |
|||
and `app.css`. The main file of the application is in the `app.js`. It contains |
|||
the majority of the application logic. |
|||
|
|||
## Start the server and view the application |
|||
|
|||
When you start the server, it will create a Node.js server, start it locally, |
|||
and open your browser 'http://localhost:5000'. From the root of your new application directory: |
|||
|
|||
1. Start the application server. |
|||
|
|||
```bash |
|||
npm start |
|||
``` |
|||
|
|||
The first time you run it, your system prompts you to accept incoming connections. |
|||
|
|||
![Network Connection](images/network-connections.gif) |
|||
|
|||
2. Choose **Allow**. |
|||
|
|||
3. Open your browser to `http://localhost:8080`. |
|||
|
|||
You should see a simple application: |
|||
|
|||
![](images/initial-app.gif) |
|||
|
|||
4. Choose **Sign In with Blockstack**. |
|||
|
|||
The application detects whether the user has the Blockstack client edition installed or |
|||
not. This is done automatically by the Blockstack API, more about this later. |
|||
What the browser displays depends on the users' current state. |
|||
|
|||
| Using web app | Has client edition installed | |
|||
|------------------|-----------------------------------| |
|||
| ![](images/login-choice.png) | ![](images/login.gif) | |
|||
|
|||
If the user logged into the Blockstack Browser but not reset it, the user can |
|||
simply use the exiting identity. |
|||
|
|||
![](images/login-no-auth.png) |
|||
|
|||
If the user chooses **Deny**, the Blockstack Browser displays its |
|||
**Home** page but the user is not logged into the sample application. |
|||
|
|||
5. Leave your new application running and move onto the next section. |
|||
|
|||
## Understand the generated application code |
|||
|
|||
In this section, you look at the basic application generated with the `yo |
|||
blockstack` command. The generated code contains simple authentication methods |
|||
that allow a user to log into the browser. The main application code is located |
|||
in the `public/app.css` file. Open this file now. |
|||
|
|||
All of the code in the file is wrapped in an event |
|||
listener. |
|||
|
|||
```js |
|||
document.addEventListener("DOMContentLoaded", function(event) { |
|||
}) |
|||
``` |
|||
|
|||
This listener that waits until the DOM content is loaded. Then, it creates an auth request and redirects the user to sign in: |
|||
|
|||
```js |
|||
document.getElementById('signin-button').addEventListener('click', function() { |
|||
blockstack.redirectUserToSignIn() |
|||
}) |
|||
``` |
|||
|
|||
You can find the `redirectUserToSignIn()` function is part of the [Blockstack Javascript documentation](https://blockstack.github.io/blockstack.js/). There is also a sign out button handler. This handler deletes the local user data and signs the user out: |
|||
|
|||
```js |
|||
document.getElementById('signout-button').addEventListener('click', function() { |
|||
blockstack.signUserOut(window.location.origin) |
|||
}) |
|||
``` |
|||
|
|||
The handlers are followed by a `showProfile()` function for showing the user's profile: |
|||
|
|||
```js |
|||
function showProfile(profile) { |
|||
var person = new blockstack.Person(profile) |
|||
document.getElementById('heading-name').innerHTML = person.name() ? person.name() : "Nameless Person" |
|||
if(person.avatarUrl()) { |
|||
document.getElementById('avatar-image').setAttribute('src', person.avatarUrl()) |
|||
} |
|||
document.getElementById('section-1').style.display = 'none' |
|||
document.getElementById('section-2').style.display = 'block' |
|||
} |
|||
``` |
|||
|
|||
Each `getElementById()` function refers to elemments in the `index.html` file. |
|||
|
|||
Once a user is successfully signed in, there is logic for loading the user |
|||
profile and displaying the application. As illustrated earlier, there are |
|||
several states the user can be in: |
|||
|
|||
- The user is already signed in |
|||
- The user has a pending sign in request |
|||
- The user is signed out |
|||
|
|||
The application handles these situtations as followed: |
|||
|
|||
```js |
|||
if (blockstack.isUserSignedIn()) { |
|||
var profile = blockstack.loadUserData().profile |
|||
showProfile(profile) |
|||
} else if (blockstack.isSignInPending()) { |
|||
blockstack.handlePendingSignIn().then(function(userData) { |
|||
window.location = window.location.origin |
|||
}) |
|||
} |
|||
``` |
|||
|
|||
When the user is signed in, Blockstack loads the user data from local storage |
|||
and displays the profile with the `showProfile()` function. When the user has a |
|||
pending sign in request, the appplication signs the user in and redirects the |
|||
user back to the home page. |
|||
|
|||
### Application manifest |
|||
|
|||
The application's `/public/manifest.json` file configures your app. The |
|||
configurations dictate how the application is displayed in auth views and on |
|||
user home screens. The contents are very simple: |
|||
|
|||
```json |
|||
{ |
|||
"name": "Hello, Blockstack", |
|||
"start_url": "localhost:5000", |
|||
"description": "A simple demo of Blockstack Auth", |
|||
"icons": [{ |
|||
"src": "https://helloblockstack.com/icon-192x192.png", |
|||
"sizes": "192x192", |
|||
"type": "image/png" |
|||
}] |
|||
} |
|||
``` |
|||
|
|||
Keep it as is or fill it in with new information that describes your app. |
|||
|
|||
### Save your application code |
|||
|
|||
Complete the tutorial by storing your app code on GitHub. Before you begin, make sure you have a GitHub account and have configured your environment to use it. |
|||
|
|||
1. Initialize the application code as a Git repo. |
|||
|
|||
```bash |
|||
git init |
|||
``` |
|||
|
|||
2. Add and commit all of the files: |
|||
|
|||
```bash |
|||
git add . && git commit -m "first commit" |
|||
``` |
|||
|
|||
3. In GitHub, create a `hello-blockstack` repository. |
|||
|
|||
4. Back in your termininal window, add a remote for GitHub. |
|||
|
|||
Make sure to fill in your username: |
|||
|
|||
```bash |
|||
git remote add origin git@github.com:YOUR_USERNAME_HERE/hello-blockstack.git |
|||
``` |
|||
|
|||
5. Push your new code to the master branch of the remote repo: |
|||
|
|||
``` |
|||
git push origin master |
|||
``` |
|||
|
|||
You're done! You just built your first Blockstack app and shipped the code. |
|||
You're well on your way to becoming a Blockstack app legend. |
@ -0,0 +1,41 @@ |
|||
--- |
|||
layout: usenew |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Working with IDS |
|||
|
|||
Blockstack allows you to create one free identity in the Blockstack |
|||
_namespace_. A namespace a top-level category in the Blockstack Ecosystem. A |
|||
namespace is similar to the domains you are familiar with from using the web, |
|||
`google.com` is domain and so is the `gouvernement.fr` domain. An identity in |
|||
the `.blockstack` namespace has the _`username`_`.id.blockstack` format. |
|||
|
|||
You can also purchase your own identity (`.id`) using bitcoin (BTC). A name's |
|||
price depends on its length in characters and the current Bitcoin fees. For |
|||
example, ranges such as .00831 and .001943 BTC 20-80 US dollars is not |
|||
unusual. |
|||
|
|||
|
|||
## Create an ID |
|||
|
|||
To, create an id, do the following: |
|||
|
|||
1. Go to the <a href="https://browser.blockstack.org" target="\_blank">Blockstack Web Browser</a>. |
|||
|
|||
|
|||
You have Blockstack Browser installed: If you start from ADD USERNAME from IDs window, jump to 6. |
|||
1/ You do not have the Blockstack Browser installed: go to https://explorer.blockstack.org/ 99 |
|||
2/ Search for your name typing “yourname.id” on the search box. If your .id is “free”, the response will be “Ooops ! The name yourname.id doesn’t exist.” or will show a owner “mnbhbu235j46ijnowejjybjb” with Expires field empty and just NAME_IMPORT, no NAME_REGISTRATION. |
|||
3/ Install de Blockstack Browser, open an account, write by hand on a piece of paper and make two copies of mnemonic 12 words phrase, DO NOT KEEP IT IN THE COMPUTER, DO NOT UPLOAD IT TO THE CLOUD, DO NOT COPY IT IN THE CLIPBOARD. |
|||
4/ Go to IDs |
|||
5/ Click ADD USERNAME. (or MORE and CREATE NEW ID if you want more than one .id’s) |
|||
6/ Try your selected name (without “.id”) |
|||
7/ If available, check the price. |
|||
(If you want to buy a name with your wallet empty the process is a little more complex because the transfer can take a long time and the process will seems frozen, so I recommend to supply your wallet before, go to step 8) |
|||
8/ Click WALLET |
|||
9/ Fund your wallet with the correct amount or more. |
|||
10/ Wait until you see the bitcoin amount in your wallet. Sometimes bitcoin net can take a minute or one hour (or 5) to perform the transaction depending on the demand of network operations, in the following link you will see approx. the time it will take to complete the transaction based on your fee: https://bitcoinfees.earn.com/ 22 |
|||
10/ Back to ADD USERNAME |
|||
11/ Write your name and click search. |
|||
12/ Click BUY |
|||
13/ The process takes one hour or six blocks, DO NOT TURN OFF the browser or the computer for two hours. |
@ -0,0 +1,222 @@ |
|||
--- |
|||
layout: usenew |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Get and use a Blockstack ID |
|||
{:.no_toc} |
|||
|
|||
Through the Blockstack browser you can create an identity. Your identity is a |
|||
point of contact as you interact with others through Dapps. Others may be |
|||
individual users or companies or software. Unless you allow it, none of these |
|||
others have access to anything other than your identity label, for example |
|||
`moxiegirl.id.blockstack`. To learn more about you, others must ask and you can |
|||
choose to share -- or not. |
|||
|
|||
This document explains one type of identity, the Blockstack ID. You learn how to |
|||
create them, as well as when and how to restore them. It contains the following sections: |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
|
|||
## Understand Blockstack IDs |
|||
|
|||
Interacting within the decentralized internet requires that you have at least |
|||
one identity, though you can create several. Your identity is created through a |
|||
registrar. Blockstack maintains a registrar for creating identities that you |
|||
can use to interact with distributed applications (Dapps). |
|||
|
|||
To use the Blockstack Browser or to develop a Dapp you |
|||
require a Blockstack ID, for example `moxiegirl.id.blockstack`. A Blockstack ID |
|||
is a digital identity that is registered With Blockstack. Think of the ID as a |
|||
form of identification, like a drivers license, but this license identifies you |
|||
on the virtual internet highway. |
|||
|
|||
|
|||
Your personal data storage is linked to this ID. You use this ID to |
|||
identify yourself to other users and to sign into applications. When you add a |
|||
picture to a Dapp, the picture appears in the Dapp but the picture's bits and bytes |
|||
are stored in your personal storage. |
|||
|
|||
|
|||
When you log into another application with your ID, that application can ask for access to that storage and then use that picture. The application must ask you, it knows you by your ID, and |
|||
|
|||
Decentralized applications that want to access your data need your |
|||
identity and your permission. |
|||
|
|||
When you first sign up through the Blockstack browser, you create an initial |
|||
human-readable identity in the `id.blockstack` domain. This initial identity has |
|||
the format: |
|||
|
|||
_`USERNAME`_`.id.blockstack` |
|||
|
|||
The _`USERNAME`_ portion must be unique. You enter an email and password to |
|||
create the initial identity. Blockstack uses the password to: |
|||
|
|||
* seed a _recovery code_ an encrypted string, for example `36mWivFdy0YPH2z31EflpQz/Y0UMrOrJ++lH=0EI7c3mop2JuRBm5W1P0BwXxSTazJsUjOAmC55rFUWINqDqGccLio0iwzGclAhaHGZQ5M52` |
|||
* seed a _recovery key_ which is a squence of words `applied binge crisp pictorial fiery dancing agreeable frogs light finish ping apple` |
|||
|
|||
The email is provided to allow either Blockstack or a decentralized application |
|||
to communicate information to you. In Blockstacks' case, the email is used to |
|||
send you reovery information. |
|||
|
|||
While Blockstack registers your human readable ID and the recovery key. _You_ must |
|||
record the: |
|||
|
|||
* recovery key |
|||
* recovery code (in the order the words apepar) |
|||
* initial password |
|||
|
|||
Blockstack does not store them, so it can't give them to you later if they are |
|||
lost. |
|||
|
|||
Your initial ID is created in the `id.blockstack` domain. The initial identity |
|||
remains primary, and you need this primary ID and its associated information |
|||
(recovery code, recovery key, and password) to use the browser again. |
|||
|
|||
Finally, the `id.blockstack` domain is sponsored by the Blockstack registrar and |
|||
identities on it are free. Once you are using the Blockstack Browser, you can |
|||
create additional identities outside of this domain and controlled by other |
|||
registrars. Coin is required to purchase identities on other domains. |
|||
|
|||
## Create an initial Blockstack ID |
|||
|
|||
To create an inititial Blockstack ID, do the following: |
|||
|
|||
1. Open the [Blockstack web application in your browser](https://browser.blockstack.org/sign-up?redirect=%2F). |
|||
|
|||
The application prompts you for an email address. |
|||
|
|||
![](images/create-id-0.png) |
|||
|
|||
Blockstack uses this email address to send you recovery information. |
|||
|
|||
2. Enter an email address and press **Next**. |
|||
|
|||
The application prompts you to enter a password. Blockstack users this |
|||
password to encrypt your recovery code. You must record and save this |
|||
initial password. |
|||
|
|||
**NOTE**:The Blockstack team cannot restore your password for you. |
|||
|
|||
3. Enter a password, confirm it, and press **Next**. |
|||
|
|||
![](images/create-id-1.png) |
|||
|
|||
The browser prompts you to register a unique username in the `id.blockstack` |
|||
domain. This is your identity in the decentralized internet. The format of the id |
|||
is: |
|||
|
|||
_`username`_`.id.blockstack` |
|||
|
|||
You'll use this initial ID to access the Blockstack Browser. |
|||
|
|||
3. Enter a unique username and press **Check Availability**. |
|||
|
|||
![](images/create-id-2.png) |
|||
|
|||
When you choose an ID that is available, the system responds with the following: |
|||
|
|||
![](images/create-id-3.png) |
|||
|
|||
4. Press **Continue**. |
|||
|
|||
The system prompts you to save your **recovery code**. A recovery code is a |
|||
sequence of words. These words allow you to recover an `id.blockstack` |
|||
that you've created. You should store the words along with their order, for |
|||
example, `#1 applied` and so forth. |
|||
|
|||
5. Click **I have written down all the words** when you are done. |
|||
|
|||
The system places you in the Blockstack browser. You can begin exploring and |
|||
using Dapps. |
|||
|
|||
## Restore a Blockstack ID |
|||
|
|||
When you return to the Blockstack Browser, the browser prompts you to create a |
|||
new Blockstack ID or restore an existing Blockstack ID. If you have a |
|||
Blockstack identity, you can open the browser by restoring the identity. To |
|||
restore an identity, there are two available methods. |
|||
|
|||
Method 1: Supply the identity recovery code (`36mWivFdy0YPH2z31E...`) and the |
|||
password you provided when you _initially_ created your identity. Method 2: |
|||
Supply the recovery key which is a sequence of words (`applied binge ...`) |
|||
|
|||
If you loose either the recovery code or the password you provided when you |
|||
_initially_ created your identity, you can no longer use method 1 to restore |
|||
your identity. If you lose the recovery key, you can no longer use method 2. |
|||
Once you no longer have access to either method, your identity is estranged and |
|||
not accessible by anyone. |
|||
|
|||
### Restore with a recovery key |
|||
|
|||
1. Open the [Blockstack web application in your browser](https://browser.blockstack.org/sign-up?redirect=%2F). |
|||
2. Choose **Restore a Blockstack ID**. |
|||
|
|||
The system displays a dialog where you can enter your recovery code or a |
|||
recovery key. |
|||
|
|||
3. Enter the recovery key. |
|||
|
|||
The recovery key is a squence of words. |
|||
|
|||
![](images/recovery-code.png) |
|||
|
|||
4. Press **Next**. |
|||
|
|||
The system prompts you for an email address. This email can be one you |
|||
entered previously or an entirely new one. Blockstack doesn't store this |
|||
address; it is used during your current Blockstack browser interaction to communicate |
|||
important information with you. |
|||
|
|||
5. Enter an email and press **Next**. |
|||
|
|||
The system prompts you for an password and its confirmation. This password |
|||
can be one you entered previously or an entirely new one. Write this password |
|||
down. You can use the password during your current Blockstack browser |
|||
interaction to reveal your keychain or change your password. Blockstack does |
|||
not store this information past the session. |
|||
|
|||
6. Enter a password and press **Next**. |
|||
|
|||
The system welcomes you back. |
|||
|
|||
![](images/welcome-back.png) |
|||
|
|||
At this point, you can go onto work with Dapps or you can review your recovery key. |
|||
|
|||
### Restore with a recovery code and original password |
|||
|
|||
1. Open the [Blockstack web application in your browser](https://browser.blockstack.org/sign-up?redirect=%2F). |
|||
2. Choose **Restore a Blockstack ID**. |
|||
|
|||
The system displays a dialog where you can enter your recovery code or a |
|||
recovery key. |
|||
|
|||
3. Enter your recovery code. |
|||
|
|||
The recovery code is an encrypted string. |
|||
|
|||
![](images/recovery-code.png) |
|||
|
|||
4. Press **Next**. |
|||
|
|||
The system prompts you for an email address. This email can be one you |
|||
entered previously or an entirely new one. Blockstack doesn't store this |
|||
address; it is used during your current Blockstack browser interaction to |
|||
communicate important information with you. |
|||
|
|||
5. Enter an email and press **Next**. |
|||
|
|||
The system prompts you for an password. This must be the password entered |
|||
when you first created your identity. If you have forgetten this passowrd, |
|||
Blockstack cannot provide it to you. Instead, you must switch to using your |
|||
recovery key rather than your code to restore your identity. |
|||
|
|||
6. Enter your origin password and press **Next**. |
|||
|
|||
The system welcomes you back. |
|||
|
|||
![](images/welcome-back.png) |
|||
|
|||
At this point, you can go work with Dapps or you can review your recovery key. |
After Width: | Height: | Size: 12 KiB |