mirror of https://github.com/lukechilds/docs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1262 lines
91 KiB
1262 lines
91 KiB
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
|
<title>Manage Data with Gaia | Blockstack</title>
|
|
<meta name="generator" content="Jekyll v3.8.3" />
|
|
<meta property="og:title" content="Manage Data with Gaia" />
|
|
<meta name="author" content="Blockstack" />
|
|
<meta property="og:locale" content="en_US" />
|
|
<meta name="description" content="Manage Data with Gaia" />
|
|
<meta property="og:description" content="Manage Data with Gaia" />
|
|
<link rel="canonical" href="https://zbabystack.netlify.com/browser/multi-player-storage.html" />
|
|
<meta property="og:url" content="https://zbabystack.netlify.com/browser/multi-player-storage.html" />
|
|
<meta property="og:site_name" content="Blockstack" />
|
|
<meta property="og:type" content="article" />
|
|
<meta property="article:published_time" content="2018-09-10T15:46:41-07:00" />
|
|
<script type="application/ld+json">
|
|
{"description":"Manage Data with Gaia","author":{"@type":"Person","name":"Blockstack"},"@type":"BlogPosting","url":"https://zbabystack.netlify.com/browser/multi-player-storage.html","headline":"Manage Data with Gaia","dateModified":"2018-09-10T15:46:41-07:00","datePublished":"2018-09-10T15:46:41-07:00","mainEntityOfPage":{"@type":"WebPage","@id":"https://zbabystack.netlify.com/browser/multi-player-storage.html"},"@context":"http://schema.org"}</script>
|
|
<!-- End Jekyll SEO tag -->
|
|
|
|
<!-- <meta property="og:image" content="https://zbabystack.netlify.com/assets/posts/logo.png"/> -->
|
|
<meta property="og:image" content="/assets/posts/logo.png"/>
|
|
<link rel="stylesheet" href="/assets/css/main.css">
|
|
<link rel="shortcut icon" type="image/png" href="/assets/img/touch-icon.png" >
|
|
<link rel="alternate" type="application/rss+xml" title="Blockstack" href="/feed.xml">
|
|
<script src="/assets/js/main.js"></script>
|
|
|
|
</head>
|
|
|
|
|
|
<body>
|
|
|
|
<header class="uk-background-secondary">
|
|
<div data-uk-sticky="sel-target: .uk-navbar-container; cls-active: uk-navbar-sticky" class="uk-sticky uk-sticky-fixed" style="position: fixed; top: 0px; width: 1904px;">
|
|
<nav class="uk-navbar-container">
|
|
<div class="uk-container">
|
|
<div data-uk-navbar>
|
|
<div class="uk-navbar-left">
|
|
|
|
<!-- <a class="uk-navbar-item uk-logo" href="/"><img src="https://zbabystack.netlify.com/assets/posts/logo.png" alt="Docs"></a> -->
|
|
<a class="uk-navbar-item uk-logo" href="/"><img src="/assets/posts/logo.png" alt="Docs"></a>
|
|
|
|
</div>
|
|
<div class="uk-navbar-right">
|
|
<ul class="uk-navbar-nav uk-visible@m">
|
|
|
|
|
|
|
|
|
|
<li><a href="https://blockstack.org" target="_blank" >Blockstack.org</a></li>
|
|
|
|
|
|
|
|
|
|
|
|
<li><a href="https://forum.blockstack.org/" target="_blank" >Forums</a></li>
|
|
|
|
|
|
|
|
|
|
|
|
<li><a href="https://github.com/blockstack" target="_blank" >GitHub</a></li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
<div>
|
|
<a class="uk-navbar-toggle" uk-search-icon href="#"></a>
|
|
<div class="uk-drop uk-background-default uk-border-rounded" uk-drop="mode: click; pos: left-center; offset: 0">
|
|
<form class="uk-search uk-search-navbar uk-width-1-1" onsubmit="return false;">
|
|
<input id="searchBox" class="uk-search-input" type="search" placeholder="Search..." autofocus>
|
|
</form>
|
|
<ul id="searchBox-results" class="uk-position-absolute uk-width-1-1 uk-list"></ul>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<a class="uk-navbar-toggle uk-hidden@m" href="#offcanvas" data-uk-navbar-toggle-icon data-uk-toggle></a>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<div class="uk-section">
|
|
<div class="uk-container">
|
|
<div class="uk-grid-large" data-uk-grid>
|
|
|
|
<div class="sidebar-fixed-width uk-visible@m">
|
|
<div class="sidebar-docs uk-position-fixed">
|
|
<!-- -->
|
|
|
|
|
|
<h5>Try a tutorial</h5>
|
|
|
|
<ul class="uk-nav uk-nav-default doc-nav">
|
|
|
|
|
|
<!-- -->
|
|
|
|
<li class=""><a href="/browser/hello-blockstack.html">Hello, Blockstack Tutorial</a></li>
|
|
|
|
|
|
<!-- -->
|
|
|
|
<li class="uk-active"><a href="/browser/multi-player-storage.html">Manage Data with Gaia</a></li>
|
|
|
|
</ul>
|
|
|
|
|
|
<h5>Work with an SDK</h5>
|
|
|
|
<ul class="uk-nav uk-nav-default doc-nav">
|
|
|
|
|
|
<!-- -->
|
|
|
|
<li class=""><a href="/android/tutorial.html">Android SDK Tutorial (Pre-release)</a></li>
|
|
|
|
|
|
<!-- -->
|
|
|
|
<li class=""><a href="/ios/tutorial.html">iOS SDK Tutorial (Pre-release)</a></li>
|
|
|
|
</ul>
|
|
|
|
|
|
<h5>Reference</h5>
|
|
|
|
<ul class="uk-nav uk-nav-default doc-nav">
|
|
|
|
|
|
<!-- -->
|
|
|
|
<li class=""><a href="/core/faq_developer.html">Developer FAQs</a></li>
|
|
|
|
|
|
<!-- -->
|
|
|
|
<li class=""><a href="/common/javascript_ref.html">Blockstack Javascript Reference</a></li>
|
|
|
|
</ul>
|
|
|
|
<!-- -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="uk-width-1-1 uk-width-expand@m">
|
|
|
|
<article class="uk-article">
|
|
|
|
<h1 class="uk-article-title">Manage Data with Gaia</h1>
|
|
|
|
|
|
|
|
|
|
<div class="uk-article-meta uk-margin-top uk-margin-medium-bottom">
|
|
|
|
|
|
|
|
<!-- <img class="avatar avatar-small" alt="Blockstack" width="32" height="32" data-proofer-ignore="true" src="https://avatars2.githubusercontent.com/Blockstack?v=3&s=32" srcset="https://avatars2.githubusercontent.com/Blockstack?v=3&s=32 1x, https://avatars2.githubusercontent.com/Blockstack?v=3&s=64 2x, https://avatars2.githubusercontent.com/Blockstack?v=3&s=96 3x, https://avatars2.githubusercontent.com/Blockstack?v=3&s=128 4x" /> -->
|
|
|
|
|
|
|
|
<!-- Written by <span itemprop="author" itemscope itemtype="http://schema.org/Person"><span itemprop="name">Blockstack</span></span><br> -->
|
|
|
|
|
|
<time datetime="2018-09-10T15:46:41-07:00" itemprop="datePublished">
|
|
|
|
|
|
|
|
<a "target="_blank" href="https://github.com/blockstack/blockstack-browser/blob/master/docs/multi-player-storage.md" class="btn btn-default githubEditButton" role="button">
|
|
<span data-uk-icon="icon: pencil; ratio: 1.2"></span> Edit this page on Github</a>
|
|
<span style="font-family:Wingdings">w</span> Sep 10, 2018
|
|
</time>
|
|
|
|
</div>
|
|
|
|
<div class="article-content">
|
|
|
|
<p class="no_toc">In this tutorial, you build a micro-blogging application using multi-player Gaia
|
|
storage. Gaia is Blockstack’s <a href="https://github.com/blockstack/gaia">decentralized high-performance storage
|
|
system</a>. The tutorial contains the following
|
|
topics:</p>
|
|
|
|
<ul id="markdown-toc">
|
|
<li><a href="#about-this-tutorial-and-the-prerequisites-you-need" id="markdown-toc-about-this-tutorial-and-the-prerequisites-you-need">About this tutorial and the prerequisites you need</a></li>
|
|
<li><a href="#use-npm-to-install-yeoman-and-the-blockstack-app-generator" id="markdown-toc-use-npm-to-install-yeoman-and-the-blockstack-app-generator">Use npm to install Yeoman and the Blockstack App Generator</a></li>
|
|
<li><a href="#generate-and-launch-the-public-application" id="markdown-toc-generate-and-launch-the-public-application">Generate and launch the public application</a></li>
|
|
<li><a href="#add-the-publish_data-scope-to-sign-in-requests" id="markdown-toc-add-the-publish_data-scope-to-sign-in-requests">Add the <code class="highlighter-rouge">publish_data</code> scope to sign in requests</a></li>
|
|
<li><a href="#understand-gaia-storage-methods" id="markdown-toc-understand-gaia-storage-methods">Understand Gaia storage methods</a></li>
|
|
<li><a href="#add-support-for-user-status-submission-and-lookup" id="markdown-toc-add-support-for-user-status-submission-and-lookup">Add support for user status submission and lookup</a></li>
|
|
<li><a href="#fetch-and-display-statuses" id="markdown-toc-fetch-and-display-statuses">Fetch and display statuses</a></li>
|
|
<li><a href="#change-the-style" id="markdown-toc-change-the-style">Change the style</a></li>
|
|
<li><a href="#lookup-user-profiles" id="markdown-toc-lookup-user-profiles">Lookup user profiles</a> <ul>
|
|
<li><a href="#add-a-new-route" id="markdown-toc-add-a-new-route">Add a new route</a></li>
|
|
<li><a href="#add-a-rule-to-process-url-paths-with--dot" id="markdown-toc-add-a-rule-to-process-url-paths-with--dot">Add a rule to process URL paths with . (dot)</a></li>
|
|
<li><a href="#put-it-all-together" id="markdown-toc-put-it-all-together">Put it all together</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#wrapping-up" id="markdown-toc-wrapping-up">Wrapping up</a></li>
|
|
</ul>
|
|
|
|
<p>This tutorial does not teach you about authentication. That is covered in depth <a href="hello-blockstack.md">in the hello-blockstack tutorial</a>.</p>
|
|
|
|
<!--TODO: authentication tutorial-->
|
|
<!--Strictly speaking not sure it is necessary here to send them out-->
|
|
|
|
<h2 id="about-this-tutorial-and-the-prerequisites-you-need">About this tutorial and the prerequisites you need</h2>
|
|
|
|
<p>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.</p>
|
|
|
|
<p>When complete, the app is capable of the following:</p>
|
|
|
|
<ul>
|
|
<li>authenticating users using Blockstack</li>
|
|
<li>posting new statuses</li>
|
|
<li>displaying statuses in the user profile</li>
|
|
<li>looking up the profiles and statuses of other users</li>
|
|
</ul>
|
|
|
|
<p>The basic identity and storage services are provided by <code class="highlighter-rouge">blockstack.js</code>. To test
|
|
the application, you need to have already <a href="ids-introduction.md">registered a Blockstack ID</a>.</p>
|
|
|
|
<p>The tutorial relies on the <code class="highlighter-rouge">npm</code> dependency manager. Before you begin, verify
|
|
you have installed <code class="highlighter-rouge">npm</code> using the <code class="highlighter-rouge">which</code> command.</p>
|
|
|
|
<div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>which npm
|
|
/usr/local/bin/npm
|
|
</code></pre>
|
|
</div>
|
|
|
|
<p>If you don’t find <code class="highlighter-rouge">npm</code> in your system, <a href="https://www.npmjs.com/get-npm">install
|
|
it</a>. Finally, if you get stuck at any point
|
|
while working on the tutorial, the completed <a href="https://github.com/larrysalibra/publik">source code is available for
|
|
you</a> to check your work against.</p>
|
|
|
|
<h2 id="use-npm-to-install-yeoman-and-the-blockstack-app-generator">Use npm to install Yeoman and the Blockstack App Generator</h2>
|
|
|
|
<p>You use <code class="highlighter-rouge">npm</code> to install Yeoman. Yeoman is a generic scaffolding system that
|
|
helps users rapidly start new projects and streamline the maintenance of
|
|
existing projects.</p>
|
|
|
|
<ol>
|
|
<li>
|
|
<p>Install Yeoman.</p>
|
|
|
|
<div class="language-bash highlighter-rouge"><pre class="highlight"><code> npm install -g yo
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Install the Blockstack application generator.</p>
|
|
|
|
<div class="language-bash highlighter-rouge"><pre class="highlight"><code> npm install -g generator-blockstack
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
</ol>
|
|
|
|
<!-- Need to find out if user is required to have React installed before running Yeoman. Doesn't appear to be the case. -->
|
|
|
|
<h2 id="generate-and-launch-the-public-application">Generate and launch the public application</h2>
|
|
|
|
<p>In this section, you build an initial React.js application called Publik.</p>
|
|
|
|
<ol>
|
|
<li>
|
|
<p>Create a the <code class="highlighter-rouge">publik</code> directory.</p>
|
|
|
|
<div class="language-bash highlighter-rouge"><pre class="highlight"><code> mkdir publik
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Change into your new directory.</p>
|
|
|
|
<div class="language-bash highlighter-rouge"><pre class="highlight"><code> <span class="nb">cd </span>publik
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Use Yeoman and the Blockstack application generator to create your initial <code class="highlighter-rouge">publik</code> application.</p>
|
|
|
|
<div class="language-bash highlighter-rouge"><pre class="highlight"><code> yo blockstack:react
|
|
</code></pre>
|
|
</div>
|
|
|
|
<p>You should see several interactive prompts.</p>
|
|
|
|
<div class="language-bash highlighter-rouge"><pre class="highlight"><code> <span class="nv">$ </span>yo blockstack:react
|
|
? <span class="o">==========================================================================</span>
|
|
We<span class="s1">'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___\ /
|
|
| ~ |
|
|
__'</span>.___.<span class="s1">'__
|
|
´ ` |° ´ Y `
|
|
|
|
? Are you ready to build a Blockstack app in React? (Y/n)
|
|
</span></code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Respond to the prompts to populate the initial app.</p>
|
|
|
|
<p>After the process completes successfully, you see a prompt similar to the following:</p>
|
|
|
|
<div class="language-bash highlighter-rouge"><pre class="highlight"><code> <span class="o">[</span>fsevents] Success:
|
|
<span class="s2">"/Users/theuser/repos/publik/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node"</span>
|
|
is installed via remote npm notice created a lockfile as package-lock.json.
|
|
You should commit this file. added 1060 packages <span class="k">in </span>26.901s
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Run the initial application.</p>
|
|
|
|
<div class="language-bash highlighter-rouge"><pre class="highlight"><code> npm start
|
|
</code></pre>
|
|
</div>
|
|
|
|
<p>The system prompts you to accept incoming connections.</p>
|
|
|
|
<p><img src="./images/network-connections.gif" alt="Network Connection" /></p>
|
|
</li>
|
|
<li>
|
|
<p>Choose <strong>Allow</strong>.</p>
|
|
</li>
|
|
<li>
|
|
<p>Open your browser to <code class="highlighter-rouge">http://localhost:8080</code>.</p>
|
|
|
|
<p>You should see a simple React app.</p>
|
|
|
|
<p><img src="images/initial-app.gif" alt="" /></p>
|
|
</li>
|
|
<li>
|
|
<p>Choose <strong>Sign In with Blockstack</strong>.</p>
|
|
|
|
<p>The application tells you it will <strong>Read your basic info</strong>.</p>
|
|
|
|
<p><img src="images/login.png" alt="" /></p>
|
|
</li>
|
|
</ol>
|
|
|
|
<p>Leave your new application running and move onto the next section.</p>
|
|
|
|
<h2 id="add-the-publish_data-scope-to-sign-in-requests">Add the <code class="highlighter-rouge">publish_data</code> scope to sign in requests</h2>
|
|
|
|
<p>Every app that uses Gaia storage must add itself to the user’s <code class="highlighter-rouge">profile.json</code>
|
|
file. The Blockstack browser does this automatically when the <code class="highlighter-rouge">publish_data</code>
|
|
scope is requested during authentication. For this application, the user files
|
|
stored on Gaia are made visible to others via the <code class="highlighter-rouge">apps</code> property in the user’s
|
|
<code class="highlighter-rouge">profile.json</code> file.</p>
|
|
|
|
<p>Modify your authentication request to include the <code class="highlighter-rouge">publish_data</code> scope.</p>
|
|
|
|
<ol>
|
|
<li>
|
|
<p>Open <code class="highlighter-rouge">src/components/App.jsx</code> file.</p>
|
|
</li>
|
|
<li>
|
|
<p>Locate the <code class="highlighter-rouge">handleSignIn</code> handler method.</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">handleSignIn</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
|
|
<span class="nx">redirectToSignIn</span><span class="p">();</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Modify the method to this:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">handleSignIn</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
|
|
<span class="kr">const</span> <span class="nx">origin</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">origin</span>
|
|
<span class="nx">redirectToSignIn</span><span class="p">(</span><span class="nx">origin</span><span class="p">,</span> <span class="nx">origin</span> <span class="o">+</span> <span class="s1">'/manifest.json'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'store_write'</span><span class="p">,</span> <span class="s1">'publish_data'</span><span class="p">])</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
|
|
<p>By default, authentication requests include the <code class="highlighter-rouge">store_write</code> scope which
|
|
enables storage. This is what allows you to store information to Gaia.</p>
|
|
</li>
|
|
<li>Save your changes.</li>
|
|
<li>Go back to your app at <code class="highlighter-rouge">http://localhost:8080/</code>.</li>
|
|
<li>
|
|
<p>Log out and sign in again.</p>
|
|
|
|
<p>The authentication request now prompts the user for permission to <strong>Publish
|
|
data stored for the app</strong>.</p>
|
|
|
|
<p><img src="images/publish-data-perm.png" alt="" /></p>
|
|
</li>
|
|
</ol>
|
|
|
|
<h2 id="understand-gaia-storage-methods">Understand Gaia storage methods</h2>
|
|
|
|
<p>Once you authenticate a user with <code class="highlighter-rouge">store_write</code> and <code class="highlighter-rouge">publish_data</code>, you can
|
|
begin to manage data for your users. Blockstack JS provides two methods
|
|
<code class="highlighter-rouge">getFile()</code> and <code class="highlighter-rouge">putFile()</code> 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.</p>
|
|
|
|
<p>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.</p>
|
|
|
|
<p>A single file collection stores items as an array nested inside each grocery
|
|
list:</p>
|
|
|
|
<div class="language-js highlighter-rouge"><pre class="highlight"><code><span class="c1">// grocerylists.json</span>
|
|
<span class="p">{</span>
|
|
<span class="s2">"3255"</span><span class="err">:</span> <span class="p">{</span>
|
|
<span class="s2">"items"</span><span class="err">:</span> <span class="p">[</span>
|
|
<span class="s2">"1 Head of Lettuce"</span><span class="p">,</span>
|
|
<span class="s2">"Haralson apples"</span>
|
|
<span class="p">]</span>
|
|
<span class="p">},</span>
|
|
<span class="c1">// ...more lists with items</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
|
|
<p>This is conceptually the simplest way to manage grocery lists. When you read a
|
|
<code class="highlighter-rouge">/grocerylists.json</code> file with <code class="highlighter-rouge">getFile()</code>, you get back one or more grocery
|
|
lists and their items. When you write a single list, the <code class="highlighter-rouge">putFile()</code> method
|
|
overwrites the entire list. So, a write operation for a new or updated grocery
|
|
list must submit all existings lists as well.</p>
|
|
|
|
<p>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:</p>
|
|
|
|
<p><code class="highlighter-rouge">line 6: Parsing Error: Unexpected token.</code></p>
|
|
|
|
<p>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 <code class="highlighter-rouge">grocerylists</code> folder.</p>
|
|
|
|
<p><img src="images/multiple-lists.png" alt="" /></p>
|
|
|
|
<p>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.</p>
|
|
|
|
<h2 id="add-support-for-user-status-submission-and-lookup">Add support for user status submission and lookup</h2>
|
|
|
|
<p>In this step, you add three <code class="highlighter-rouge">blockstack.js</code> methods that support posting of “statuses”. These are the <code class="highlighter-rouge">putFile()</code>, <code class="highlighter-rouge">getFile()</code>, and <code class="highlighter-rouge">lookupProfile()</code> methods.</p>
|
|
|
|
<ol>
|
|
<li>
|
|
<p>Open the <code class="highlighter-rouge">src/components/Profile.jsx</code> file.</p>
|
|
</li>
|
|
<li>
|
|
<p>Expand the <code class="highlighter-rouge">import from blockstack</code> statement with data methods.</p>
|
|
|
|
<p>The <code class="highlighter-rouge">Person</code> object holds a Blockstack profile. Add <code class="highlighter-rouge">putFile</code>, <code class="highlighter-rouge">getFile</code>,
|
|
and <code class="highlighter-rouge">lookupProfile</code> after <code class="highlighter-rouge">Person</code>.</p>
|
|
|
|
<div class="highlighter-rouge"><pre class="highlight"><code> When you are done, the import statement should look like the following:
|
|
</code></pre>
|
|
</div>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="kr">import</span> <span class="p">{</span>
|
|
<span class="nx">isSignInPending</span><span class="p">,</span>
|
|
<span class="nx">loadUserData</span><span class="p">,</span>
|
|
<span class="nx">Person</span><span class="p">,</span>
|
|
<span class="nx">getFile</span><span class="p">,</span>
|
|
<span class="nx">putFile</span><span class="p">,</span>
|
|
<span class="nx">lookupProfile</span>
|
|
<span class="p">}</span> <span class="nx">from</span> <span class="s1">'blockstack'</span><span class="p">;</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Replace the <code class="highlighter-rouge">constructor()</code> initial state so that it holds the key properties required by the app.</p>
|
|
|
|
<p>This code constructs a Blockstack <code class="highlighter-rouge">Person</code> object to hold the profile. Your constructor should look like this:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">constructor</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="kr">super</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
|
|
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="p">{</span>
|
|
<span class="na">person</span><span class="p">:</span> <span class="p">{</span>
|
|
<span class="nx">name</span><span class="p">()</span> <span class="p">{</span>
|
|
<span class="k">return</span> <span class="s1">'Anonymous'</span><span class="p">;</span>
|
|
<span class="p">},</span>
|
|
<span class="nx">avatarUrl</span><span class="p">()</span> <span class="p">{</span>
|
|
<span class="k">return</span> <span class="nx">avatarFallbackImage</span><span class="p">;</span>
|
|
<span class="p">},</span>
|
|
<span class="p">},</span>
|
|
<span class="na">username</span><span class="p">:</span> <span class="s2">""</span><span class="p">,</span>
|
|
<span class="na">newStatus</span><span class="p">:</span> <span class="s2">""</span><span class="p">,</span>
|
|
<span class="na">statuses</span><span class="p">:</span> <span class="p">[],</span>
|
|
<span class="na">statusIndex</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
|
|
<span class="na">isLoading</span><span class="p">:</span> <span class="kc">false</span>
|
|
<span class="p">};</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>Locate the <code class="highlighter-rouge">render()</code> method.</li>
|
|
<li>
|
|
<p>Modify the <code class="highlighter-rouge">render()</code> method to add a text input and submit button to the application.</p>
|
|
|
|
<p>The following code echos the <code class="highlighter-rouge">person.name</code> and <code class="highlighter-rouge">person.avatarURL</code>
|
|
properties from the profile on the display:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
|
|
<span class="kr">const</span> <span class="p">{</span> <span class="nx">handleSignOut</span> <span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
|
|
<span class="kr">const</span> <span class="p">{</span> <span class="nx">person</span> <span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">;</span>
|
|
<span class="kr">const</span> <span class="p">{</span> <span class="nx">username</span> <span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">;</span>
|
|
|
|
<span class="k">return</span> <span class="p">(</span>
|
|
<span class="o">!</span><span class="nx">isSignInPending</span><span class="p">()</span> <span class="o">&&</span> <span class="nx">person</span> <span class="p">?</span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"container"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"row"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"col-md-offset-3 col-md-6"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"col-md-12"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"avatar-section"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">img</span>
|
|
<span class="nx">src</span><span class="o">=</span><span class="p">{</span> <span class="nx">person</span><span class="p">.</span><span class="nx">avatarUrl</span><span class="p">()</span> <span class="p">?</span> <span class="nx">person</span><span class="p">.</span><span class="nx">avatarUrl</span><span class="p">()</span> <span class="p">:</span> <span class="nx">avatarFallbackImage</span> <span class="p">}</span>
|
|
<span class="nx">className</span><span class="o">=</span><span class="s2">"img-rounded avatar"</span>
|
|
<span class="nx">id</span><span class="o">=</span><span class="s2">"avatar-image"</span>
|
|
<span class="sr">/</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"username"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">h1</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">span</span> <span class="nx">id</span><span class="o">=</span><span class="s2">"heading-name"</span><span class="o">></span><span class="p">{</span> <span class="nx">person</span><span class="p">.</span><span class="nx">name</span><span class="p">()</span> <span class="p">?</span> <span class="nx">person</span><span class="p">.</span><span class="nx">name</span><span class="p">()</span>
|
|
<span class="p">:</span> <span class="s1">'Nameless Person'</span> <span class="p">}</span><span class="o"><</span><span class="sr">/span</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/h1</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="nx">span</span><span class="o">></span><span class="p">{</span><span class="nx">username</span><span class="p">}</span><span class="o"><</span><span class="sr">/span</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="nx">span</span><span class="o">></span>
|
|
<span class="o">&</span><span class="nx">nbsp</span><span class="p">;</span><span class="o">|&</span><span class="nx">nbsp</span><span class="p">;</span>
|
|
<span class="o"><</span><span class="nx">a</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{</span> <span class="nx">handleSignOut</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">)</span> <span class="p">}</span><span class="o">></span><span class="p">(</span><span class="nx">Logout</span><span class="p">)</span><span class="o"><</span><span class="sr">/a</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/span</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"new-status"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"col-md-12"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">textarea</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"input-status"</span>
|
|
<span class="nx">value</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">newStatus</span><span class="p">}</span>
|
|
<span class="nx">onChange</span><span class="o">=</span><span class="p">{</span><span class="nx">e</span> <span class="o">=></span> <span class="k">this</span><span class="p">.</span><span class="nx">handleNewStatusChange</span><span class="p">(</span><span class="nx">e</span><span class="p">)}</span>
|
|
<span class="nx">placeholder</span><span class="o">=</span><span class="s2">"Enter a status"</span>
|
|
<span class="sr">/</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"col-md-12"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">button</span>
|
|
<span class="nx">className</span><span class="o">=</span><span class="s2">"btn btn-primary btn-lg"</span>
|
|
<span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">e</span> <span class="o">=></span> <span class="k">this</span><span class="p">.</span><span class="nx">handleNewStatusSubmit</span><span class="p">(</span><span class="nx">e</span><span class="p">)}</span>
|
|
<span class="o">></span>
|
|
<span class="nx">Submit</span>
|
|
<span class="o"><</span><span class="sr">/button</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span>
|
|
<span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div> : nul</span><span class="err">l
|
|
</span> <span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
|
|
<p>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.</p>
|
|
</li>
|
|
<li>Locate the <code class="highlighter-rouge">componentWillMount()</code> method.</li>
|
|
<li>
|
|
<p>Add the <code class="highlighter-rouge">username</code> property below the <code class="highlighter-rouge">person</code> property.</p>
|
|
|
|
<p>You’ll use the Blockstack <code class="highlighter-rouge">loadUserData()</code> method to access the <code class="highlighter-rouge">username</code>.</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">componentWillMount</span><span class="p">()</span> <span class="p">{</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span>
|
|
<span class="na">person</span><span class="p">:</span> <span class="k">new</span> <span class="nx">Person</span><span class="p">(</span><span class="nx">loadUserData</span><span class="p">().</span><span class="nx">profile</span><span class="p">),</span>
|
|
<span class="na">username</span><span class="p">:</span> <span class="nx">loadUserData</span><span class="p">().</span><span class="nx">username</span>
|
|
<span class="p">});</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Add two methods to handle the status input events:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">handleNewStatusChange</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span><span class="na">newStatus</span><span class="p">:</span> <span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span><span class="p">})</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="nx">handleNewStatusSubmit</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">saveNewStatus</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">newStatus</span><span class="p">)</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span>
|
|
<span class="na">newStatus</span><span class="p">:</span> <span class="s2">""</span>
|
|
<span class="p">})</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Add a <code class="highlighter-rouge">saveNewStatus()</code> method to save the new statuses.</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">saveNewStatus</span><span class="p">(</span><span class="nx">statusText</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="kd">let</span> <span class="nx">statuses</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">statuses</span>
|
|
|
|
<span class="kd">let</span> <span class="nx">status</span> <span class="o">=</span> <span class="p">{</span>
|
|
<span class="na">id</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">statusIndex</span><span class="o">++</span><span class="p">,</span>
|
|
<span class="na">text</span><span class="p">:</span> <span class="nx">statusText</span><span class="p">.</span><span class="nx">trim</span><span class="p">(),</span>
|
|
<span class="na">created_at</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="nx">statuses</span><span class="p">.</span><span class="nx">unshift</span><span class="p">(</span><span class="nx">status</span><span class="p">)</span>
|
|
<span class="kr">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span> <span class="na">encrypt</span><span class="p">:</span> <span class="kc">false</span> <span class="p">}</span>
|
|
<span class="nx">putFile</span><span class="p">(</span><span class="s1">'statuses.json'</span><span class="p">,</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">statuses</span><span class="p">),</span> <span class="nx">options</span><span class="p">)</span>
|
|
<span class="p">.</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span>
|
|
<span class="na">statuses</span><span class="p">:</span> <span class="nx">statuses</span>
|
|
<span class="p">})</span>
|
|
<span class="p">})</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Save the <code class="highlighter-rouge">Profile.jsk</code> file.</p>
|
|
|
|
<p>After the application compiles successfully, your application should appears as follows:</p>
|
|
|
|
<p><img src="images/display-complete.png" alt="" /></p>
|
|
</li>
|
|
<li>
|
|
<p>Enter your status in the text box and press the <strong>Submit</strong> button.</p>
|
|
|
|
<p>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.</p>
|
|
</li>
|
|
</ol>
|
|
|
|
<h2 id="fetch-and-display-statuses">Fetch and display statuses</h2>
|
|
|
|
<p>Update <code class="highlighter-rouge">Profile.jsx</code> again.</p>
|
|
|
|
<ol>
|
|
<li>Go back to the <code class="highlighter-rouge">render()</code> method.</li>
|
|
<li>Locate the <code class="highlighter-rouge"><div className="new-status"></code> containing the text input and <strong>Submit</strong> button.</li>
|
|
<li>
|
|
<p>Right after this opening <code class="highlighter-rouge">div</code> element, add this block.</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"col-md-12 statuses"</span><span class="o">></span>
|
|
<span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">isLoading</span> <span class="o">&&</span> <span class="o"><</span><span class="nx">span</span><span class="o">></span><span class="nx">Loading</span><span class="p">...</span><span class="o"><</span><span class="sr">/span></span><span class="err">}
|
|
</span> <span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">statuses</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">status</span><span class="p">)</span> <span class="o">=></span> <span class="p">(</span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"status"</span> <span class="nx">key</span><span class="o">=</span><span class="p">{</span><span class="nx">status</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="o">></span>
|
|
<span class="p">{</span><span class="nx">status</span><span class="p">.</span><span class="nx">text</span><span class="p">}</span>
|
|
<span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="p">)</span>
|
|
<span class="p">)}</span>
|
|
<span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span></code></pre>
|
|
</div>
|
|
<p>This loads existing state. Your code needs to fetch statuses on page load.</p>
|
|
</li>
|
|
<li>
|
|
<p>Add a new method called <code class="highlighter-rouge">fetchData()</code> after the <code class="highlighter-rouge">statuses.unshift(status)</code> section.</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code>
|
|
<span class="nx">fetchData</span><span class="p">()</span> <span class="p">{</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span> <span class="na">isLoading</span><span class="p">:</span> <span class="kc">true</span> <span class="p">})</span>
|
|
<span class="kr">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span> <span class="na">decrypt</span><span class="p">:</span> <span class="kc">false</span> <span class="p">}</span>
|
|
<span class="nx">getFile</span><span class="p">(</span><span class="s1">'statuses.json'</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span>
|
|
<span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">file</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
|
|
<span class="kd">var</span> <span class="nx">statuses</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">file</span> <span class="o">||</span> <span class="s1">'[]'</span><span class="p">)</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span>
|
|
<span class="na">person</span><span class="p">:</span> <span class="k">new</span> <span class="nx">Person</span><span class="p">(</span><span class="nx">loadUserData</span><span class="p">().</span><span class="nx">profile</span><span class="p">),</span>
|
|
<span class="na">username</span><span class="p">:</span> <span class="nx">loadUserData</span><span class="p">().</span><span class="nx">username</span><span class="p">,</span>
|
|
<span class="na">statusIndex</span><span class="p">:</span> <span class="nx">statuses</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span>
|
|
<span class="na">statuses</span><span class="p">:</span> <span class="nx">statuses</span><span class="p">,</span>
|
|
<span class="p">})</span>
|
|
<span class="p">})</span>
|
|
<span class="p">.</span><span class="k">finally</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span> <span class="na">isLoading</span><span class="p">:</span> <span class="kc">false</span> <span class="p">})</span>
|
|
<span class="p">})</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Call <code class="highlighter-rouge">fetchData()</code> from the <code class="highlighter-rouge">componentDidMount()</code> method</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code>
|
|
<span class="nx">componentDidMount</span><span class="p">()</span> <span class="p">{</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">fetchData</span><span class="p">()</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Save the file.</p>
|
|
|
|
<p>After the application compiles successfully, users are able to <strong>Submit</strong>
|
|
multiple statuses and review them in the app.</p>
|
|
|
|
<p><img src="images/saving-status.png" alt="" /></p>
|
|
</li>
|
|
</ol>
|
|
|
|
<h2 id="change-the-style">Change the style</h2>
|
|
|
|
<ol>
|
|
<li>Edit the <code class="highlighter-rouge">src/styles/style.css</code> file.</li>
|
|
<li>
|
|
<p>Replace the content with the following:</p>
|
|
|
|
<div class="language-css highlighter-rouge"><pre class="highlight"><code> <span class="c">/* Globals */</span>
|
|
<span class="nt">a</span><span class="o">,</span><span class="nt">a</span><span class="nd">:focus</span><span class="o">,</span><span class="nt">a</span><span class="nd">:hover</span><span class="p">{</span><span class="nl">color</span><span class="p">:</span><span class="m">#fff</span><span class="p">;}</span>
|
|
<span class="nt">html</span><span class="o">,</span><span class="nt">body</span><span class="p">{</span><span class="nl">height</span><span class="p">:</span><span class="m">100%</span><span class="p">;</span><span class="nl">text-align</span><span class="p">:</span><span class="nb">center</span><span class="p">;</span><span class="nl">background-color</span><span class="p">:</span><span class="m">#191b22</span><span class="p">;}</span>
|
|
<span class="nt">body</span><span class="p">{</span><span class="nl">color</span><span class="p">:</span><span class="m">#fff</span><span class="p">}</span>
|
|
<span class="nc">.hide</span><span class="p">{</span><span class="nl">display</span><span class="p">:</span><span class="nb">none</span><span class="p">;}</span>
|
|
<span class="nc">.landing-heading</span><span class="p">{</span><span class="nl">font-family</span><span class="p">:</span><span class="s2">'Lato'</span><span class="p">,</span><span class="n">Sans-Serif</span><span class="p">;</span><span class="nl">font-weight</span><span class="p">:</span><span class="m">400</span><span class="p">;}</span>
|
|
|
|
<span class="c">/* Buttons */</span>
|
|
<span class="nc">.btn</span><span class="p">{</span><span class="nl">font-family</span><span class="p">:</span><span class="s2">'Lato'</span><span class="p">,</span><span class="n">Sans-Serif</span><span class="p">;</span><span class="nl">padding</span><span class="p">:</span><span class="m">0.5625rem</span> <span class="m">2.5rem</span><span class="p">;</span><span class="nl">font-size</span><span class="p">:</span><span class="m">0.8125rem</span><span class="p">;</span><span class="nl">font-weight</span><span class="p">:</span><span class="m">400</span><span class="p">;</span><span class="nl">line-height</span><span class="p">:</span><span class="m">1.75rem</span><span class="p">;</span><span class="nl">border-radius</span><span class="p">:</span><span class="m">0</span><span class="cp">!important</span><span class="p">;</span><span class="nl">-webkit-transition</span><span class="p">:</span><span class="n">all</span> <span class="m">.2s</span> <span class="n">ease-in-out</span><span class="p">;</span><span class="nl">-moz-transition</span><span class="p">:</span><span class="n">all</span> <span class="m">.2s</span> <span class="n">ease-in-out</span><span class="p">;</span><span class="nl">-ms-transition</span><span class="p">:</span><span class="n">all</span> <span class="m">.2s</span> <span class="n">ease-in-out</span><span class="p">;</span><span class="nl">-o-transition</span><span class="p">:</span><span class="n">all</span> <span class="m">.2s</span> <span class="n">ease-in-out</span><span class="p">;</span><span class="nl">transition</span><span class="p">:</span><span class="n">all</span> <span class="m">.2s</span> <span class="n">ease-in-out</span><span class="p">;</span><span class="nl">-webkit-user-select</span><span class="p">:</span><span class="nb">none</span><span class="p">;</span><span class="nl">-moz-user-select</span><span class="p">:</span><span class="nb">none</span><span class="p">;</span><span class="nl">-ms-user-select</span><span class="p">:</span><span class="nb">none</span><span class="p">;</span><span class="py">user-select</span><span class="p">:</span><span class="nb">none</span><span class="p">;}</span>
|
|
<span class="nc">.btn-lg</span><span class="p">{</span><span class="nl">font-size</span><span class="p">:</span><span class="m">1.5rem</span><span class="p">;</span><span class="nl">padding</span><span class="p">:</span><span class="m">0.6875rem</span> <span class="m">3.4375rem</span><span class="p">;</span><span class="nl">line-height</span><span class="p">:</span><span class="m">2.5rem</span><span class="p">;}</span>
|
|
<span class="nc">.btn</span><span class="nd">:focus</span><span class="o">,</span><span class="nc">.btn</span><span class="nd">:active:focus</span><span class="o">,</span><span class="nc">.btn.active</span><span class="nd">:focus</span><span class="p">{</span><span class="nl">outline</span><span class="p">:</span><span class="nb">none</span><span class="p">;}</span>
|
|
<span class="nc">.btn-primary</span><span class="p">{</span><span class="nl">color</span><span class="p">:</span><span class="m">#fff</span><span class="p">;</span><span class="nl">border</span><span class="p">:</span><span class="m">1px</span> <span class="nb">solid</span> <span class="m">#2C96FF</span><span class="p">;</span><span class="nl">background-color</span><span class="p">:</span><span class="m">#2C96FF</span><span class="p">;}</span>
|
|
<span class="nc">.btn-primary</span><span class="nd">:hover</span><span class="o">,</span><span class="nc">.btn-primary</span><span class="nd">:focus</span><span class="o">,</span><span class="nc">.btn-primary</span><span class="nd">:active</span><span class="p">{</span><span class="nl">color</span><span class="p">:</span><span class="m">#fff</span><span class="p">;</span><span class="nl">border</span><span class="p">:</span><span class="m">1px</span> <span class="nb">solid</span> <span class="m">#1a6ec0</span><span class="p">;</span><span class="nl">background-color</span><span class="p">:</span><span class="m">#1a6ec0</span><span class="p">;}</span>
|
|
|
|
<span class="c">/* Avatar */</span>
|
|
<span class="nc">.avatar</span><span class="p">{</span><span class="nl">width</span><span class="p">:</span><span class="m">100px</span><span class="p">;</span><span class="nl">height</span><span class="p">:</span><span class="m">100px</span><span class="p">;}</span>
|
|
<span class="nc">.avatar-section</span><span class="p">{</span><span class="nl">margin-bottom</span><span class="p">:</span><span class="m">25px</span><span class="p">;</span><span class="nl">display</span><span class="p">:</span><span class="n">flex</span><span class="p">;</span><span class="nl">text-align</span><span class="p">:</span><span class="nb">left</span><span class="p">;}</span>
|
|
<span class="nc">.username</span><span class="p">{</span><span class="nl">margin-left</span><span class="p">:</span><span class="m">20px</span><span class="p">;}</span>
|
|
|
|
<span class="c">/* Scaffolding */</span>
|
|
<span class="nc">.site-wrapper</span><span class="p">{</span><span class="nl">display</span><span class="p">:</span><span class="n">table</span><span class="p">;</span><span class="nl">width</span><span class="p">:</span><span class="m">100%</span><span class="p">;</span><span class="nl">height</span><span class="p">:</span><span class="m">100vh</span><span class="p">;</span><span class="nl">min-height</span><span class="p">:</span><span class="m">100%</span><span class="p">;}</span>
|
|
<span class="nc">.site-wrapper-inner</span><span class="p">{</span><span class="nl">display</span><span class="p">:</span><span class="n">flex</span><span class="p">;</span><span class="nl">flex-direction</span><span class="p">:</span><span class="n">column</span><span class="p">;</span><span class="nl">justify-content</span><span class="p">:</span><span class="nb">center</span><span class="p">;</span><span class="nl">margin-right</span><span class="p">:</span><span class="nb">auto</span><span class="p">;</span><span class="nl">margin-left</span><span class="p">:</span><span class="nb">auto</span><span class="p">;</span><span class="nl">width</span><span class="p">:</span><span class="m">100%</span><span class="p">;</span><span class="nl">height</span><span class="p">:</span><span class="m">100vh</span><span class="p">;}</span>
|
|
<span class="nc">.panel-authed</span><span class="p">{</span><span class="nl">padding</span><span class="p">:</span><span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span><span class="p">;}</span>
|
|
|
|
<span class="c">/* Home button */</span>
|
|
<span class="nc">.btn-home-hello</span><span class="p">{</span><span class="nl">position</span><span class="p">:</span><span class="nb">absolute</span><span class="p">;</span><span class="nl">font-family</span><span class="p">:</span><span class="s2">'Source Code Pro'</span><span class="p">,</span><span class="nb">monospace</span><span class="p">;</span><span class="nl">font-size</span><span class="p">:</span><span class="m">11px</span><span class="p">;</span><span class="nl">font-weight</span><span class="p">:</span><span class="m">400</span><span class="p">;</span><span class="nl">color</span><span class="p">:</span><span class="n">rgba</span><span class="p">(</span><span class="m">255</span><span class="p">,</span><span class="m">255</span><span class="p">,</span><span class="m">255</span><span class="p">,</span><span class="m">0.85</span><span class="p">);</span><span class="nl">top</span><span class="p">:</span><span class="m">15px</span><span class="p">;</span><span class="nl">left</span><span class="p">:</span><span class="m">15px</span><span class="p">;</span><span class="nl">padding</span><span class="p">:</span><span class="m">3px</span> <span class="m">20px</span><span class="p">;</span><span class="nl">background-color</span><span class="p">:</span><span class="n">rgba</span><span class="p">(</span><span class="m">255</span><span class="p">,</span><span class="m">255</span><span class="p">,</span><span class="m">255</span><span class="p">,</span><span class="m">0.15</span><span class="p">);</span><span class="nl">border-radius</span><span class="p">:</span><span class="m">6px</span><span class="p">;</span><span class="nl">-webkit-box-shadow</span><span class="p">:</span><span class="m">0px</span> <span class="m">0px</span> <span class="m">20px</span> <span class="m">0px</span> <span class="n">rgba</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0.15</span><span class="p">);</span><span class="nl">-moz-box-shadow</span><span class="p">:</span><span class="m">0px</span> <span class="m">0px</span> <span class="m">20px</span> <span class="m">0px</span> <span class="n">rgba</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0.15</span><span class="p">);</span><span class="nl">box-shadow</span><span class="p">:</span><span class="m">0px</span> <span class="m">0px</span> <span class="m">20px</span> <span class="m">0px</span> <span class="n">rgba</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0.15</span><span class="p">);}</span>
|
|
|
|
<span class="c">/* Input */</span>
|
|
<span class="nt">input</span><span class="o">,</span> <span class="nt">textarea</span><span class="p">{</span><span class="nl">color</span><span class="p">:</span><span class="m">#000</span><span class="p">;</span><span class="nl">padding</span><span class="p">:</span><span class="m">10px</span><span class="p">;}</span>
|
|
<span class="nc">.input-status</span><span class="p">{</span><span class="nl">width</span><span class="p">:</span><span class="m">100%</span><span class="p">;</span><span class="nl">height</span><span class="p">:</span><span class="m">70px</span><span class="p">;</span><span class="nl">border-radius</span><span class="p">:</span><span class="m">6px</span><span class="p">;}</span>
|
|
<span class="nc">.new-status</span><span class="p">{</span><span class="nl">text-align</span><span class="p">:</span><span class="nb">right</span><span class="p">;}</span>
|
|
|
|
<span class="c">/* Statuses */</span>
|
|
<span class="nc">.statuses</span><span class="p">{</span><span class="nl">padding-top</span><span class="p">:</span><span class="m">30px</span><span class="p">;}</span>
|
|
<span class="nc">.status</span><span class="p">{</span><span class="nl">margin</span><span class="p">:</span><span class="m">15px</span> <span class="m">0px</span><span class="p">;</span><span class="nl">padding</span><span class="p">:</span><span class="m">20px</span><span class="p">;</span><span class="nl">background-color</span><span class="p">:</span><span class="m">#2e2e2e</span><span class="p">;</span><span class="nl">border-radius</span><span class="p">:</span><span class="m">6px</span><span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Save and close the <code class="highlighter-rouge">src/styles/style.css</code> file.</p>
|
|
|
|
<p>After the application compiles, you should see the following:</p>
|
|
|
|
<p><img src="images/multi-player-storage-status.png" alt="Multi-reader storage authentication" /></p>
|
|
</li>
|
|
</ol>
|
|
|
|
<p>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.</p>
|
|
|
|
<h2 id="lookup-user-profiles">Lookup user profiles</h2>
|
|
|
|
<p>Let’s now modify the <code class="highlighter-rouge">Profile.jsx</code> file to display profiles of other users. You’ll
|
|
be using the <code class="highlighter-rouge">lookupProfile()</code> method that you added to the <code class="highlighter-rouge">import</code> statement
|
|
earlier. <code class="highlighter-rouge">lookupProfile()</code> takes a single parameter that is the Blockstack ID of
|
|
the profile and returns a profile object.</p>
|
|
|
|
<h3 id="add-a-new-route">Add a new route</h3>
|
|
|
|
<p>Make some changes to the routing structure of your app so that users can view
|
|
other users’ profiles by visiting <code class="highlighter-rouge">http://localhost:8080/other_user.id</code></p>
|
|
|
|
<ol>
|
|
<li>Make sure you are in the root of your <code class="highlighter-rouge">publik</code> project.</li>
|
|
<li>
|
|
<p>Install <code class="highlighter-rouge">react-router</code>:</p>
|
|
|
|
<div class="language-bash highlighter-rouge"><pre class="highlight"><code> npm install --save react-router-dom
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>Edit <code class="highlighter-rouge">src/index.js</code> file.</li>
|
|
<li>
|
|
<p>Add an <code class="highlighter-rouge">import</code> to the file at the top:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="kr">import</span> <span class="p">{</span> <span class="nx">BrowserRouter</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'react-router-dom'</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Change the <code class="highlighter-rouge">ReactDOM.render()</code> method in <code class="highlighter-rouge">src/index.js</code> to:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">ReactDOM</span><span class="p">.</span><span class="nx">render</span><span class="p">((</span>
|
|
<span class="o"><</span><span class="nx">BrowserRouter</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">App</span> <span class="o">/></span>
|
|
<span class="o"><</span><span class="sr">/BrowserRouter</span><span class="err">>
|
|
</span> <span class="p">),</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'root'</span><span class="p">));</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>Save and close the <code class="highlighter-rouge">src/index.js</code> file.</li>
|
|
<li>Edit the <code class="highlighter-rouge">src/components/App.jsx</code> file.</li>
|
|
<li>
|
|
<p>Add the new route by importing the <code class="highlighter-rouge">Switch</code> and <code class="highlighter-rouge">Route</code> components from <code class="highlighter-rouge">react-router-dom</code>:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="kr">import</span> <span class="p">{</span> <span class="nx">Switch</span><span class="p">,</span> <span class="nx">Route</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'react-router-dom'</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Locate this line below in the <code class="highlighter-rouge">render()</code> method:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="err">:</span> <span class="o"><</span><span class="nx">Profile</span> <span class="nx">handleSignOut</span><span class="o">=</span><span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">handleSignOut</span> <span class="p">}</span> <span class="sr">/</span><span class="err">>
|
|
</span></code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Replace it with the following:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="err">:</span>
|
|
<span class="o"><</span><span class="nx">Switch</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">Route</span>
|
|
<span class="nx">path</span><span class="o">=</span><span class="s1">'/:username?'</span>
|
|
<span class="nx">render</span><span class="o">=</span><span class="p">{</span>
|
|
<span class="nx">routeProps</span> <span class="o">=></span> <span class="o"><</span><span class="nx">Profile</span> <span class="nx">handleSignOut</span><span class="o">=</span><span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">handleSignOut</span> <span class="p">}</span> <span class="p">{...</span><span class="nx">routeProps</span><span class="p">}</span> <span class="sr">/</span><span class="err">>
|
|
</span> <span class="p">}</span>
|
|
<span class="sr">/</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/Switch</span><span class="err">>
|
|
</span></code></pre>
|
|
</div>
|
|
|
|
<p>This sets up a route and captures the route parameter the app will use as the profile lookup username.</p>
|
|
</li>
|
|
<li>Save and close the the <code class="highlighter-rouge">src/components/App.jsx</code> file.</li>
|
|
</ol>
|
|
|
|
<h3 id="add-a-rule-to-process-url-paths-with--dot">Add a rule to process URL paths with . (dot)</h3>
|
|
|
|
<p>You also need to add a rule to your webpack config so that you can properly
|
|
process URL paths that contain the <code class="highlighter-rouge">.</code> (dot) character for example,
|
|
<code class="highlighter-rouge">http://localhost:8080/other_user.id</code></p>
|
|
|
|
<p><strong>NOTE</strong>: In a production app, you must ensure the web server is configured to handle this.</p>
|
|
|
|
<ol>
|
|
<li>
|
|
<p>Open <code class="highlighter-rouge">webpack.config.js</code> in the root project directory and locate the following line:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">historyApiFallback</span><span class="err">:</span> <span class="kc">true</span><span class="p">,</span>
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<p>Replace it with this:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">historyApiFallback</span><span class="err">:</span> <span class="p">{</span>
|
|
<span class="nl">disableDotRule</span><span class="p">:</span> <span class="kc">true</span>
|
|
<span class="p">},</span>
|
|
</code></pre>
|
|
</div>
|
|
|
|
<p>You will need to run <code class="highlighter-rouge">npm start</code> again for this change to take effect. Don’t
|
|
worry, there is a later step for that to remind you.</p>
|
|
</li>
|
|
<li>
|
|
<p>Save and close the <code class="highlighter-rouge">webpack.config.js</code> file.</p>
|
|
</li>
|
|
<li>Edit the <code class="highlighter-rouge">src/components/Profile.jsx</code> file.</li>
|
|
<li>
|
|
<p>Add a single method that determines if the app is viewing the local user’s profile or another user’s profile.</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">isLocal</span><span class="p">()</span> <span class="p">{</span>
|
|
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">match</span><span class="p">.</span><span class="nx">params</span><span class="p">.</span><span class="nx">username</span> <span class="p">?</span> <span class="kc">false</span> <span class="p">:</span> <span class="kc">true</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
|
|
<p>You use <code class="highlighter-rouge">isLocal()</code> 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 <code class="highlighter-rouge">getFile()</code> function you added in an earlier step. Otherwise, the app looks up the profile belonging to the <code class="highlighter-rouge">username</code> using the <code class="highlighter-rouge">lookupProfile()</code> method.</p>
|
|
</li>
|
|
<li>
|
|
<p>Modify the <code class="highlighter-rouge">fetchData()</code> method like so:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">fetchData</span><span class="p">()</span> <span class="p">{</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span> <span class="na">isLoading</span><span class="p">:</span> <span class="kc">true</span> <span class="p">})</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">isLocal</span><span class="p">())</span> <span class="p">{</span>
|
|
<span class="kr">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span> <span class="na">decrypt</span><span class="p">:</span> <span class="kc">false</span> <span class="p">}</span>
|
|
<span class="nx">getFile</span><span class="p">(</span><span class="s1">'statuses.json'</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span>
|
|
<span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">file</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
|
|
<span class="kd">var</span> <span class="nx">statuses</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">file</span> <span class="o">||</span> <span class="s1">'[]'</span><span class="p">)</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span>
|
|
<span class="na">person</span><span class="p">:</span> <span class="k">new</span> <span class="nx">Person</span><span class="p">(</span><span class="nx">loadUserData</span><span class="p">().</span><span class="nx">profile</span><span class="p">),</span>
|
|
<span class="na">username</span><span class="p">:</span> <span class="nx">loadUserData</span><span class="p">().</span><span class="nx">username</span><span class="p">,</span>
|
|
<span class="na">statusIndex</span><span class="p">:</span> <span class="nx">statuses</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span>
|
|
<span class="na">statuses</span><span class="p">:</span> <span class="nx">statuses</span><span class="p">,</span>
|
|
<span class="p">})</span>
|
|
<span class="p">})</span>
|
|
<span class="p">.</span><span class="k">finally</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span> <span class="na">isLoading</span><span class="p">:</span> <span class="kc">false</span> <span class="p">})</span>
|
|
<span class="p">})</span>
|
|
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
|
|
<span class="kr">const</span> <span class="nx">username</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">match</span><span class="p">.</span><span class="nx">params</span><span class="p">.</span><span class="nx">username</span>
|
|
|
|
<span class="nx">lookupProfile</span><span class="p">(</span><span class="nx">username</span><span class="p">)</span>
|
|
<span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">profile</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span>
|
|
<span class="na">person</span><span class="p">:</span> <span class="k">new</span> <span class="nx">Person</span><span class="p">(</span><span class="nx">profile</span><span class="p">),</span>
|
|
<span class="na">username</span><span class="p">:</span> <span class="nx">username</span>
|
|
<span class="p">})</span>
|
|
<span class="p">})</span>
|
|
<span class="p">.</span><span class="k">catch</span><span class="p">((</span><span class="nx">error</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
|
|
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'could not resolve profile'</span><span class="p">)</span>
|
|
<span class="p">})</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
|
|
<p><strong>NOTE</strong>: For <code class="highlighter-rouge">https</code> deployments, the default Blockstack Core API endpoint for name
|
|
lookups should be changed to point to a core API served over <code class="highlighter-rouge">https</code>.
|
|
Otherwise, name lookups fail due to browsers blocking mixed content.
|
|
Refer to the <a href="http://blockstack.github.io/blockstack.js/#getfile">Blockstack.js
|
|
documentation</a> for
|
|
details.</p>
|
|
</li>
|
|
<li>
|
|
<p>Add the following block to <code class="highlighter-rouge">fetchData()</code> right after the call to <code class="highlighter-rouge">lookupProfile(username)... catch((error)=>{..}</code> block:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="kr">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span> <span class="na">username</span><span class="p">:</span> <span class="nx">username</span><span class="p">,</span> <span class="na">decrypt</span><span class="p">:</span> <span class="kc">false</span> <span class="p">}</span>
|
|
<span class="nx">getFile</span><span class="p">(</span><span class="s1">'statuses.json'</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span>
|
|
<span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">file</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
|
|
<span class="kd">var</span> <span class="nx">statuses</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">file</span> <span class="o">||</span> <span class="s1">'[]'</span><span class="p">)</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span>
|
|
<span class="na">statusIndex</span><span class="p">:</span> <span class="nx">statuses</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span>
|
|
<span class="na">statuses</span><span class="p">:</span> <span class="nx">statuses</span>
|
|
<span class="p">})</span>
|
|
<span class="p">})</span>
|
|
<span class="p">.</span><span class="k">catch</span><span class="p">((</span><span class="nx">error</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
|
|
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'could not fetch statuses'</span><span class="p">)</span>
|
|
<span class="p">})</span>
|
|
<span class="p">.</span><span class="k">finally</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
|
|
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span> <span class="na">isLoading</span><span class="p">:</span> <span class="kc">false</span> <span class="p">})</span>
|
|
<span class="p">})</span>
|
|
</code></pre>
|
|
</div>
|
|
|
|
<p>This fetches the user statuses.</p>
|
|
|
|
<p>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.</p>
|
|
</li>
|
|
<li>
|
|
<p>Replace the <code class="highlighter-rouge">render()</code> method with the following:</p>
|
|
|
|
<div class="language-javascript highlighter-rouge"><pre class="highlight"><code> <span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
|
|
<span class="kr">const</span> <span class="p">{</span> <span class="nx">handleSignOut</span> <span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
|
|
<span class="kr">const</span> <span class="p">{</span> <span class="nx">person</span> <span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">;</span>
|
|
<span class="kr">const</span> <span class="p">{</span> <span class="nx">username</span> <span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">;</span>
|
|
|
|
<span class="k">return</span> <span class="p">(</span>
|
|
<span class="o">!</span><span class="nx">isSignInPending</span><span class="p">()</span> <span class="o">&&</span> <span class="nx">person</span> <span class="p">?</span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"container"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"row"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"col-md-offset-3 col-md-6"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"col-md-12"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"avatar-section"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">img</span>
|
|
<span class="nx">src</span><span class="o">=</span><span class="p">{</span> <span class="nx">person</span><span class="p">.</span><span class="nx">avatarUrl</span><span class="p">()</span> <span class="p">?</span> <span class="nx">person</span><span class="p">.</span><span class="nx">avatarUrl</span><span class="p">()</span> <span class="p">:</span> <span class="nx">avatarFallbackImage</span> <span class="p">}</span>
|
|
<span class="nx">className</span><span class="o">=</span><span class="s2">"img-rounded avatar"</span>
|
|
<span class="nx">id</span><span class="o">=</span><span class="s2">"avatar-image"</span>
|
|
<span class="sr">/</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"username"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">h1</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">span</span> <span class="nx">id</span><span class="o">=</span><span class="s2">"heading-name"</span><span class="o">></span><span class="p">{</span> <span class="nx">person</span><span class="p">.</span><span class="nx">name</span><span class="p">()</span> <span class="p">?</span> <span class="nx">person</span><span class="p">.</span><span class="nx">name</span><span class="p">()</span>
|
|
<span class="p">:</span> <span class="s1">'Nameless Person'</span> <span class="p">}</span><span class="o"><</span><span class="sr">/span</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/h1</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="nx">span</span><span class="o">></span><span class="p">{</span><span class="nx">username</span><span class="p">}</span><span class="o"><</span><span class="sr">/span</span><span class="err">>
|
|
</span> <span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">isLocal</span><span class="p">()</span> <span class="o">&&</span>
|
|
<span class="o"><</span><span class="nx">span</span><span class="o">></span>
|
|
<span class="o">&</span><span class="nx">nbsp</span><span class="p">;</span><span class="o">|&</span><span class="nx">nbsp</span><span class="p">;</span>
|
|
<span class="o"><</span><span class="nx">a</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{</span> <span class="nx">handleSignOut</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">)</span> <span class="p">}</span><span class="o">></span><span class="p">(</span><span class="nx">Logout</span><span class="p">)</span><span class="o"><</span><span class="sr">/a</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/span</span><span class="err">>
|
|
</span> <span class="p">}</span>
|
|
<span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">isLocal</span><span class="p">()</span> <span class="o">&&</span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"new-status"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"col-md-12"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">textarea</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"input-status"</span>
|
|
<span class="nx">value</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">newStatus</span><span class="p">}</span>
|
|
<span class="nx">onChange</span><span class="o">=</span><span class="p">{</span><span class="nx">e</span> <span class="o">=></span> <span class="k">this</span><span class="p">.</span><span class="nx">handleNewStatusChange</span><span class="p">(</span><span class="nx">e</span><span class="p">)}</span>
|
|
<span class="nx">placeholder</span><span class="o">=</span><span class="s2">"What's on your mind?"</span>
|
|
<span class="sr">/</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"col-md-12 text-right"</span><span class="o">></span>
|
|
<span class="o"><</span><span class="nx">button</span>
|
|
<span class="nx">className</span><span class="o">=</span><span class="s2">"btn btn-primary btn-lg"</span>
|
|
<span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">e</span> <span class="o">=></span> <span class="k">this</span><span class="p">.</span><span class="nx">handleNewStatusSubmit</span><span class="p">(</span><span class="nx">e</span><span class="p">)}</span>
|
|
<span class="o">></span>
|
|
<span class="nx">Submit</span>
|
|
<span class="o"><</span><span class="sr">/button</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="p">}</span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"col-md-12 statuses"</span><span class="o">></span>
|
|
<span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">isLoading</span> <span class="o">&&</span> <span class="o"><</span><span class="nx">span</span><span class="o">></span><span class="nx">Loading</span><span class="p">...</span><span class="o"><</span><span class="sr">/span></span><span class="err">}
|
|
</span> <span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">statuses</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">status</span><span class="p">)</span> <span class="o">=></span> <span class="p">(</span>
|
|
<span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"status"</span> <span class="nx">key</span><span class="o">=</span><span class="p">{</span><span class="nx">status</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="o">></span>
|
|
<span class="p">{</span><span class="nx">status</span><span class="p">.</span><span class="nx">text</span><span class="p">}</span>
|
|
<span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="p">)</span>
|
|
<span class="p">)}</span>
|
|
<span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div</span><span class="err">>
|
|
</span> <span class="o"><</span><span class="sr">/div> : nul</span><span class="err">l
|
|
</span> <span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</code></pre>
|
|
</div>
|
|
|
|
<p>### This checks to ensure that users are viewing their own profile, by wrapping the <strong>Logout</strong> button and inputs with the <code class="highlighter-rouge"><span class="p">{</span><span class="err">isLocal()</span><span class="w"> </span><span class="err">&&</span><span class="w"> </span><span class="err">...</span><span class="p">}</span></code> condition.</p>
|
|
</li>
|
|
</ol>
|
|
|
|
<h3 id="put-it-all-together">Put it all together</h3>
|
|
|
|
<ol>
|
|
<li>Stop the running application by sending a CTL-C.</li>
|
|
<li>
|
|
<p>Restart the application so that the disabling of the <code class="highlighter-rouge">.</code> (dot) rule takes effect.</p>
|
|
|
|
<div class="language-bash highlighter-rouge"><pre class="highlight"><code> npm start
|
|
</code></pre>
|
|
</div>
|
|
</li>
|
|
<li>Point your browser to <code class="highlighter-rouge">http://localhost:8080/your_blockstack.id</code> to see the final application.</li>
|
|
</ol>
|
|
|
|
<h2 id="wrapping-up">Wrapping up</h2>
|
|
|
|
<p>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
|
|
<a href="http://publik.ykliao.com">here</a>.</p>
|
|
|
|
<div class="share uk-text-center">
|
|
<a href="https://twitter.com/intent/tweet?text=Manage Data with Gaia&url=https://zbabystack.netlify.com/browser/multi-player-storage.html&via=&related=" rel="nofollow" target="_blank" title="Share on Twitter" onclick="window.open(this.href, 'twitter', 'width=550,height=235');return false;"><span data-uk-icon="icon: twitter; ratio: 1.2"></span></a>
|
|
<a class="uk-margin-small-left" href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fzbabystack.netlify.com%2Fbrowser%2Fmulti-player-storage.html" rel="nofollow" target="_blank" title="Share on Facebook" onclick="window.open(this.href, 'facebook-share','width=580,height=296');return false;"><span data-uk-icon="icon: facebook; ratio: 1.2"></span></a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<hr class="uk-margin-medium">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- <div class="uk-margin-large-top">
|
|
<h3>Related Articles</h3>
|
|
|
|
|
|
|
|
|
|
|
|
<ul class="uk-list">
|
|
|
|
</ul>
|
|
</div>
|
|
-->
|
|
|
|
</article>
|
|
|
|
<script>
|
|
// Table of contents scroll to
|
|
UIkit.scroll('#markdown-toc a', {
|
|
duration: 400,
|
|
offset: 120
|
|
});
|
|
</script>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div id="offcanvas" data-uk-offcanvas="flip: true; overlay: true">
|
|
<div class="uk-offcanvas-bar">
|
|
|
|
<button class="uk-offcanvas-close" type="button" data-uk-close></button>
|
|
|
|
<ul class="uk-nav uk-nav-default">
|
|
<!-- <li><a class="uk-logo uk-margin-small-bottom" href="/"><img src="https://zbabystack.netlify.com/assets/posts/logo.png" alt="Docs"></a></li> -->
|
|
<li><a class="uk-logo uk-margin-small-bottom" href="/"><img src="/assets/posts/logo.png" alt="Docs"></a></li>
|
|
|
|
|
|
<li><a href="https://blockstack.org" target="_blank" >Blockstack.org</a></li>
|
|
|
|
|
|
<li><a href="https://forum.blockstack.org/" target="_blank" >Forums</a></li>
|
|
|
|
|
|
<li><a href="https://github.com/blockstack" target="_blank" >GitHub</a></li>
|
|
|
|
</ul>
|
|
|
|
<div class="uk-margin-small-top uk-text-center uk-text-muted uk-link-muted">
|
|
<div data-uk-grid class="uk-child-width-auto uk-grid-small uk-flex-center uk-grid">
|
|
|
|
<div class="uk-first-column">
|
|
<a href="https://twitter.com/" data-uk-icon="icon: twitter" class="uk-icon-link uk-icon" target="_blank"></a>
|
|
</div>
|
|
|
|
|
|
<div>
|
|
<a href="https://www.facebook.com/" data-uk-icon="icon: facebook" class="uk-icon-link uk-icon" target="_blank"></a>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
<a href="https://www.instagram.com/" data-uk-icon="icon: instagram" class="uk-icon-link uk-icon" target="_blank"></a>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
<a href="https://vimeo.com/" data-uk-icon="icon: vimeo" class="uk-icon-link uk-icon" target="_blank"></a>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<footer class="uk-section uk-text-center uk-text-muted uk-link-muted">
|
|
<div class="uk-container uk-container-small">
|
|
|
|
<!-- <div>
|
|
<ul class="uk-subnav uk-flex-center">
|
|
|
|
|
|
|
|
|
|
<li><a href="https://blockstack.org" target="_blank" >Blockstack.org</a></li>
|
|
|
|
|
|
|
|
|
|
|
|
<li><a href="https://forum.blockstack.org/" target="_blank" >Forums</a></li>
|
|
|
|
|
|
|
|
|
|
|
|
<li><a href="https://github.com/blockstack" target="_blank" >GitHub</a></li>
|
|
|
|
|
|
</ul>
|
|
</div>
|
|
<div class="uk-margin-medium">
|
|
<div data-uk-grid class="uk-child-width-auto uk-grid-small uk-flex-center uk-grid">
|
|
|
|
<div class="uk-first-column">
|
|
<a href="https://twitter.com/" data-uk-icon="icon: twitter" class="uk-icon-link uk-icon" target="_blank"></a>
|
|
</div>
|
|
|
|
|
|
<div>
|
|
<a href="https://www.facebook.com/" data-uk-icon="icon: facebook" class="uk-icon-link uk-icon" target="_blank"></a>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
<a href="https://www.instagram.com/" data-uk-icon="icon: instagram" class="uk-icon-link uk-icon" target="_blank"></a>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
<a href="https://vimeo.com/" data-uk-icon="icon: vimeo" class="uk-icon-link uk-icon" target="_blank"></a>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
</div> -->
|
|
<div class="uk-margin-medium uk-text-small copyright">© 2018 Blockstack</div>
|
|
|
|
</div>
|
|
</footer>
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
/* Create a configuration object */
|
|
var ss360Config = {
|
|
/* Your site id */
|
|
siteId: 'blockstack',
|
|
/* A CSS selector that points to your search box */
|
|
searchBox: {selector: '#searchBox'}
|
|
};
|
|
</script>
|
|
<script src="https://cdn.sitesearch360.com/sitesearch360-v11.min.js" async></script>
|
|
|
|
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|