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.
 
 

1365 lines
103 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>Blockstack Storage Tutorial | Blockstack</title>
<meta name="generator" content="Jekyll v3.5.0" />
<meta property="og:title" content="Blockstack Storage Tutorial" />
<meta name="author" content="Blockstack" />
<meta property="og:locale" content="en_US" />
<meta name="description" content="Blockstack Storage Tutorial" />
<meta property="og:description" content="Blockstack Storage Tutorial" />
<link rel="canonical" href="https://docs.blockstack.org/browser/blockstack_storage.html" />
<meta property="og:url" content="https://docs.blockstack.org/browser/blockstack_storage.html" />
<meta property="og:site_name" content="Blockstack" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2018-10-25T14:46:28-07:00" />
<script type="application/ld+json">
{"url":"https://docs.blockstack.org/browser/blockstack_storage.html","headline":"Blockstack Storage Tutorial","dateModified":"2018-10-25T14:46:28-07:00","datePublished":"2018-10-25T14:46:28-07:00","author":{"@type":"Person","name":"Blockstack"},"mainEntityOfPage":{"@type":"WebPage","@id":"https://docs.blockstack.org/browser/blockstack_storage.html"},"description":"Blockstack Storage Tutorial","@type":"BlogPosting","@context":"http://schema.org"}</script>
<!-- End Jekyll SEO tag -->
<!-- <meta property="og:image" content="https://docs.blockstack.org/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>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-125894815-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-125894815-1');
</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://docs.blockstack.org/assets/posts/logo.png" alt="Docs"></a> -->
<a class="uk-navbar-item uk-logo" href="/"><img src="/assets/posts/logo.png" alt="Docs">
&nbsp;&nbsp;&nbsp;
<svg style="width:141.602;height:11.487px" viewBox="0 0 141.602 11.487" enable-background="new 0 0 141.602 11.487"><path fill="#000000" d="M5.471,7.791c0,0.511-0.16,0.875-0.488,1.111C4.631,9.158,4.097,9.287,3.398,9.287H2.387V6.456h1.011 C5.256,6.456,5.471,7.219,5.471,7.791z M4.554,4.223C4.29,4.448,3.82,4.562,3.16,4.562H2.387V2.2h0.821 c0.611,0,1.066,0.094,1.352,0.279C4.81,2.641,4.933,2.907,4.933,3.292C4.933,3.704,4.809,4.008,4.554,4.223z M6.004,5.338 c0.325-0.181,0.59-0.418,0.79-0.709c0.327-0.476,0.493-1.001,0.493-1.562c0-0.53-0.105-0.989-0.313-1.362 c-0.208-0.374-0.5-0.677-0.867-0.9C5.754,0.59,5.333,0.432,4.855,0.335C4.391,0.24,3.879,0.192,3.334,0.192h-3.06 C0.123,0.192,0,0.317,0,0.471v10.545c0,0.154,0.123,0.278,0.274,0.278h3.315c0.591,0,1.143-0.065,1.645-0.194 c0.513-0.132,0.966-0.339,1.345-0.615c0.388-0.283,0.694-0.641,0.91-1.067C7.702,8.992,7.811,8.488,7.811,7.92 c0-0.78-0.226-1.413-0.672-1.879C6.847,5.737,6.467,5.502,6.004,5.338z M22.197,9.158h-4.595V0.471c0-0.153-0.123-0.278-0.274-0.278 h-1.824c-0.151,0-0.273,0.125-0.273,0.278v10.545c0,0.154,0.122,0.278,0.273,0.278h6.693c0.151,0,0.274-0.124,0.274-0.278v-1.58 C22.47,9.282,22.348,9.158,22.197,9.158z M35.076,5.695c0,0.567-0.051,1.086-0.151,1.543c-0.1,0.45-0.24,0.836-0.416,1.149 c-0.167,0.299-0.37,0.53-0.603,0.688c-0.461,0.312-1.061,0.3-1.505-0.001c-0.233-0.157-0.436-0.389-0.604-0.688 c-0.175-0.313-0.312-0.699-0.407-1.146c-0.096-0.458-0.144-0.978-0.144-1.546c0-1.126,0.187-2.013,0.556-2.638 c0.346-0.587,0.791-0.873,1.36-0.873c0.568,0,1.012,0.285,1.359,0.873C34.889,3.682,35.076,4.569,35.076,5.695z M36.319,1.541 c-0.378-0.494-0.844-0.879-1.381-1.144c-1.076-0.529-2.477-0.529-3.553,0c-0.538,0.265-1.003,0.65-1.381,1.144 c-0.373,0.487-0.669,1.09-0.879,1.792c-0.208,0.696-0.313,1.49-0.313,2.362c0,0.882,0.105,1.685,0.313,2.386 c0.21,0.706,0.504,1.318,0.876,1.821c0.378,0.511,0.844,0.908,1.382,1.179c0.538,0.27,1.136,0.407,1.779,0.407 c0.642,0,1.24-0.137,1.778-0.408c0.538-0.27,1.003-0.667,1.382-1.178c0.372-0.503,0.667-1.115,0.876-1.821 c0.208-0.7,0.314-1.502,0.314-2.386c0-0.873-0.105-1.667-0.314-2.363C36.988,2.631,36.692,2.028,36.319,1.541z M51.29,8.346 c-0.052-0.058-0.125-0.092-0.203-0.092c-0.077,0-0.151,0.033-0.203,0.092c-0.273,0.308-0.566,0.549-0.871,0.715 c-0.602,0.33-1.469,0.308-2.099,0.002c-0.326-0.159-0.608-0.387-0.841-0.681c-0.236-0.299-0.423-0.675-0.554-1.116 c-0.134-0.45-0.203-0.968-0.203-1.539c0-0.56,0.068-1.07,0.203-1.515c0.132-0.437,0.316-0.81,0.548-1.111 c0.227-0.293,0.504-0.522,0.825-0.68c0.614-0.302,1.45-0.317,2.02-0.032c0.283,0.142,0.543,0.333,0.775,0.568 c0.054,0.054,0.147,0.088,0.2,0.081c0.075-0.002,0.146-0.036,0.196-0.092l1.03-1.161c0.098-0.11,0.094-0.278-0.01-0.383 c-0.355-0.361-0.799-0.687-1.317-0.968c-1.105-0.597-2.643-0.547-3.841-0.044c-0.621,0.262-1.165,0.648-1.618,1.149 c-0.451,0.498-0.809,1.113-1.064,1.827c-0.253,0.71-0.381,1.521-0.381,2.408c0,0.899,0.128,1.712,0.381,2.417 c0.255,0.709,0.614,1.317,1.066,1.805c0.454,0.489,0.993,0.865,1.603,1.115c0.607,0.249,1.268,0.374,1.966,0.374 c0.695,0,1.333-0.142,1.897-0.423c0.559-0.279,1.068-0.684,1.513-1.204c0.09-0.106,0.089-0.264-0.004-0.368L51.29,8.346z M64.109,4.608l3.075-3.966c0.065-0.083,0.077-0.198,0.031-0.293c-0.045-0.096-0.141-0.157-0.245-0.157h-2.046 c-0.084,0-0.163,0.039-0.215,0.106l-3.254,4.218V0.47c0-0.153-0.123-0.277-0.274-0.277h-1.855c-0.151,0-0.274,0.124-0.274,0.277 v10.546c0,0.153,0.122,0.278,0.274,0.278h1.855c0.151,0,0.274-0.125,0.274-0.278V8.034l1.185-1.483l2.392,4.594 c0.048,0.091,0.141,0.148,0.242,0.148h2.046c0.097,0,0.186-0.052,0.236-0.138c0.049-0.085,0.05-0.19,0.003-0.276L64.109,4.608z M81.006,6.088c-0.218-0.244-0.478-0.462-0.773-0.647c-0.286-0.179-0.606-0.339-0.94-0.47l-1.368-0.598 c-0.235-0.097-0.461-0.189-0.679-0.276c-0.196-0.079-0.371-0.169-0.516-0.27c-0.134-0.092-0.24-0.197-0.315-0.311 c-0.065-0.099-0.097-0.22-0.097-0.369c0-0.297,0.112-0.514,0.351-0.683c0.263-0.187,0.64-0.281,1.12-0.281 c0.437,0,0.833,0.074,1.179,0.22c0.355,0.151,0.702,0.36,1.03,0.623c0.116,0.094,0.287,0.074,0.381-0.044l0.952-1.193 c0.091-0.114,0.079-0.279-0.026-0.38c-0.456-0.429-0.993-0.775-1.597-1.028c-1.114-0.467-2.435-0.495-3.459-0.144 c-0.469,0.161-0.88,0.389-1.221,0.678c-0.345,0.292-0.618,0.638-0.815,1.031c-0.199,0.398-0.299,0.834-0.299,1.296 c0,0.422,0.072,0.806,0.213,1.141c0.139,0.33,0.328,0.623,0.561,0.873c0.228,0.243,0.488,0.454,0.771,0.626 c0.277,0.169,0.564,0.312,0.841,0.42l1.425,0.634c0.249,0.095,0.479,0.19,0.692,0.285c0.198,0.088,0.367,0.184,0.504,0.285 c0.122,0.09,0.216,0.193,0.281,0.308c0.06,0.107,0.091,0.246,0.091,0.411c0,0.322-0.121,0.563-0.381,0.76 c-0.278,0.21-0.709,0.316-1.28,0.316c-0.467,0-0.934-0.103-1.388-0.307C75.78,8.788,75.358,8.514,74.99,8.18 c-0.056-0.05-0.127-0.077-0.201-0.07c-0.073,0.005-0.141,0.039-0.189,0.096l-1.078,1.274c-0.095,0.113-0.087,0.283,0.02,0.385 c0.541,0.516,1.167,0.92,1.862,1.2c0.695,0.281,1.434,0.423,2.195,0.423c0.63,0,1.205-0.09,1.707-0.265 c0.506-0.178,0.942-0.422,1.294-0.729c0.355-0.309,0.632-0.673,0.823-1.085c0.191-0.412,0.288-0.858,0.288-1.328 c0-0.419-0.062-0.797-0.185-1.123C81.404,6.632,81.229,6.339,81.006,6.088z M96.557,0.192h-8.374c-0.151,0-0.273,0.124-0.273,0.278 v1.58c0,0.154,0.122,0.278,0.273,0.278h2.994v8.687c0,0.154,0.122,0.278,0.274,0.278h1.84c0.151,0,0.273-0.124,0.273-0.278V2.328 h2.994c0.151,0,0.274-0.124,0.274-0.278v-1.58C96.831,0.317,96.708,0.192,96.557,0.192z M107.143,2.902 c0.074,0.282,0.149,0.566,0.226,0.851l0.725,2.664h-1.906l0.265-0.934c0.159-0.561,0.315-1.135,0.469-1.723 C106.996,3.473,107.071,3.188,107.143,2.902z M108.512,0.384c-0.037-0.114-0.142-0.192-0.261-0.192h-2.157 c-0.118,0-0.223,0.077-0.26,0.192l-3.362,10.546c-0.027,0.085-0.012,0.177,0.04,0.249c0.051,0.072,0.134,0.115,0.221,0.115h1.871 c0.122,0,0.229-0.082,0.263-0.201l0.752-2.653h3.044l0.736,2.651c0.034,0.12,0.141,0.203,0.263,0.203h1.951 c0.088,0,0.17-0.043,0.221-0.115c0.052-0.071,0.066-0.164,0.039-0.249L108.512,0.384z M125.302,8.346 c-0.104-0.117-0.302-0.117-0.406,0c-0.273,0.308-0.566,0.548-0.87,0.715c-0.603,0.329-1.469,0.308-2.1,0.002 c-0.326-0.159-0.608-0.387-0.841-0.681c-0.236-0.299-0.422-0.675-0.554-1.116c-0.134-0.449-0.202-0.966-0.202-1.539 c0-0.561,0.068-1.071,0.202-1.515c0.132-0.437,0.316-0.811,0.549-1.111c0.226-0.293,0.503-0.522,0.823-0.68 c0.614-0.302,1.45-0.317,2.02-0.032c0.283,0.142,0.544,0.332,0.775,0.567c0.053,0.055,0.131,0.089,0.2,0.082 c0.075-0.002,0.146-0.035,0.196-0.092l1.031-1.161c0.098-0.11,0.093-0.278-0.01-0.383c-0.355-0.361-0.8-0.687-1.318-0.968 c-1.105-0.597-2.644-0.547-3.841-0.044c-0.621,0.262-1.165,0.648-1.618,1.149c-0.451,0.498-0.809,1.113-1.063,1.827 c-0.254,0.71-0.382,1.521-0.382,2.409c0,0.899,0.128,1.712,0.382,2.417c0.254,0.709,0.612,1.317,1.065,1.805 c0.453,0.489,0.993,0.864,1.604,1.115c0.606,0.249,1.268,0.374,1.965,0.374c0.695,0,1.333-0.142,1.897-0.423 c0.559-0.279,1.067-0.684,1.514-1.204c0.09-0.106,0.089-0.264-0.004-0.368L125.302,8.346z M141.567,10.88l-3.448-6.272l3.075-3.966 c0.065-0.083,0.077-0.198,0.031-0.293c-0.045-0.096-0.141-0.157-0.246-0.157h-2.045c-0.084,0-0.164,0.039-0.215,0.106l-3.254,4.217 V0.47c0-0.153-0.122-0.277-0.274-0.277h-1.856c-0.151,0-0.274,0.124-0.274,0.277v10.546c0,0.153,0.123,0.278,0.274,0.278h1.856 c0.151,0,0.274-0.125,0.274-0.278V8.035l1.185-1.484l2.392,4.594c0.047,0.091,0.141,0.148,0.242,0.148h2.045 c0.097,0,0.187-0.052,0.236-0.138C141.614,11.071,141.615,10.966,141.567,10.88z"></path></svg>
</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>Introduction</h5>
<ul class="uk-nav uk-nav-default doc-nav">
</ul>
<ul class="uk-nav uk-nav-default doc-nav">
<!-- -->
<li class=""><a href="/develop/dapp_principles.html">Principles of Blockstack applications</a></li>
</ul>
<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=""><a href="/browser/todo-list.html">Todo List Application Tutorial</a></li>
<!-- -->
<li class="uk-active"><a href="/browser/blockstack_storage.html">Blockstack Storage Tutorial</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>Application mining</h5>
<ul class="uk-nav uk-nav-default doc-nav">
<!-- -->
<li class=""><a href="/develop/mining_intro.html">Understand app mining</a></li>
<!-- -->
<li class=""><a href="/develop/mining_enroll.html">How to enroll</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/mining_faq.html">Mining FAQ</a></li>
<!-- -->
<li class=""><a href="/common/javascript_ref.html">Blockstack Javascript Reference</a></li>
<!-- -->
<li class=""><a href="/common/core_ref.html">Blockstack CORE API</a></li>
</ul>
<!-- -->
</div>
</div>
<div class="uk-width-1-1 uk-width-expand@m">
<article markdown="span" class="uk-article">
<h1 class="uk-article-title">Blockstack Storage Tutorial</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-10-25T14:46:28-07:00" itemprop="datePublished">
<a "target="_blank" href="https://github.com/blockstack/blockstack-browser/blob/master/docs/blockstack_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">&#119;</span> Oct 25, 2018
</time>
</div>
<div markdown="span" 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">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">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 &amp; 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">&amp;&amp;</span> <span class="nx">person</span> <span class="p">?</span>
<span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"container"</span><span class="o">&gt;</span>
<span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"row"</span><span class="o">&gt;</span>
<span class="o">&lt;</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">&gt;</span>
<span class="o">&lt;</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">&gt;</span>
<span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"avatar-section"</span><span class="o">&gt;</span>
<span class="o">&lt;</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">&gt;
</span> <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"username"</span><span class="o">&gt;</span>
<span class="o">&lt;</span><span class="nx">h1</span><span class="o">&gt;</span>
<span class="o">&lt;</span><span class="nx">span</span> <span class="nx">id</span><span class="o">=</span><span class="s2">"heading-name"</span><span class="o">&gt;</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">&lt;</span><span class="sr">/span</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/h1</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">username</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/span</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;</span>
<span class="o">&amp;</span><span class="nx">nbsp</span><span class="p">;</span><span class="o">|&amp;</span><span class="nx">nbsp</span><span class="p">;</span>
<span class="o">&lt;</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">&gt;</span><span class="p">(</span><span class="nx">Logout</span><span class="p">)</span><span class="o">&lt;</span><span class="sr">/a</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/span</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span>
<span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"new-status"</span><span class="o">&gt;</span>
<span class="o">&lt;</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">&gt;</span>
<span class="o">&lt;</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">=&gt;</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">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</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">&gt;</span>
<span class="o">&lt;</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">=&gt;</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">&gt;</span>
<span class="nx">Submit</span>
<span class="o">&lt;</span><span class="sr">/button</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span>
<span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div&gt; : 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">=&gt;</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">&lt;div className="new-status"&gt;</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">&lt;</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">&gt;</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">&amp;&amp;</span> <span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;</span><span class="nx">Loading</span><span class="p">...</span><span class="o">&lt;</span><span class="sr">/span&gt;</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">=&gt;</span> <span class="p">(</span>
<span class="o">&lt;</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">&gt;</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">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="p">)</span>
<span class="p">)}</span>
<span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</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">=&gt;</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">=&gt;</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">&lt;</span><span class="nx">BrowserRouter</span><span class="o">&gt;</span>
<span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span>
<span class="o">&lt;</span><span class="sr">/BrowserRouter</span><span class="err">&gt;
</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">&lt;</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">&gt;
</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">&lt;</span><span class="nx">Switch</span><span class="o">&gt;</span>
<span class="o">&lt;</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">=&gt;</span> <span class="o">&lt;</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">&gt;
</span> <span class="p">}</span>
<span class="sr">/</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/Switch</span><span class="err">&gt;
</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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)=&gt;{..}</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">=&gt;</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">=&gt;</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">=&gt;</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">&amp;&amp;</span> <span class="nx">person</span> <span class="p">?</span>
<span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"container"</span><span class="o">&gt;</span>
<span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"row"</span><span class="o">&gt;</span>
<span class="o">&lt;</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">&gt;</span>
<span class="o">&lt;</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">&gt;</span>
<span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"avatar-section"</span><span class="o">&gt;</span>
<span class="o">&lt;</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">&gt;
</span> <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"username"</span><span class="o">&gt;</span>
<span class="o">&lt;</span><span class="nx">h1</span><span class="o">&gt;</span>
<span class="o">&lt;</span><span class="nx">span</span> <span class="nx">id</span><span class="o">=</span><span class="s2">"heading-name"</span><span class="o">&gt;</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">&lt;</span><span class="sr">/span</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/h1</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">username</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/span</span><span class="err">&gt;
</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">&amp;&amp;</span>
<span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;</span>
<span class="o">&amp;</span><span class="nx">nbsp</span><span class="p">;</span><span class="o">|&amp;</span><span class="nx">nbsp</span><span class="p">;</span>
<span class="o">&lt;</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">&gt;</span><span class="p">(</span><span class="nx">Logout</span><span class="p">)</span><span class="o">&lt;</span><span class="sr">/a</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/span</span><span class="err">&gt;
</span> <span class="p">}</span>
<span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</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">&amp;&amp;</span>
<span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="s2">"new-status"</span><span class="o">&gt;</span>
<span class="o">&lt;</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">&gt;</span>
<span class="o">&lt;</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">=&gt;</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">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</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">&gt;</span>
<span class="o">&lt;</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">=&gt;</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">&gt;</span>
<span class="nx">Submit</span>
<span class="o">&lt;</span><span class="sr">/button</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="p">}</span>
<span class="o">&lt;</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">&gt;</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">&amp;&amp;</span> <span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;</span><span class="nx">Loading</span><span class="p">...</span><span class="o">&lt;</span><span class="sr">/span&gt;</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">=&gt;</span> <span class="p">(</span>
<span class="o">&lt;</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">&gt;</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">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="p">)</span>
<span class="p">)}</span>
<span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span> <span class="o">&lt;</span><span class="sr">/div&gt; : 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">&amp;&amp;</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=Blockstack Storage Tutorial&url=https://docs.blockstack.org/browser/blockstack_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>
</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://docs.blockstack.org/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"> Blockstack&reg; and Stacks&trade; are trademarks of Blockstack Public Benefit Corp. &copy; 2018 Blockstack Public Benefits Corp.</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'
},
results: {
embedConfig: undefined, // {'url':undefined,'contentBlock':'.page-content-body'}, // if url is given the page will change to that URL and look for the content block there to insert the results
fullScreenConfig: undefined, // {trigger: '#ss360-search-trigger', caption: 'Search this site'}, trigger is the CSS selector to the element that starts the search full screen overlay and searchCaption the caption on the full screen search page
caption: 'Found #COUNT# search results for \"#QUERY#\"', // the caption of the search results
group: true, // whether results should be grouped if content groups are available
filters: undefined,
num: 96, // the maximum number of search results to be shown
highlightQueryTerms: true, // whether to highlight the query terms in search results
moreResultsButton: "Show more results", // HTML for the more results button, all results will be shown if this is null
noResultsText: 'Sorry, we have not found any matches for your query.', // the text to show when there are no results
queryCorrectionText: 'Did you mean "#CORRECTION#"?',
searchQueryParamName: 'ss360Query', // the name of the search query parameter
linksOpenNewTab: false, // should clicking on the result links open a new tab/window?
showSearchBoxLayover: true, //whether to show search box in search result layover
moreResultsPagingSize: 12, // the number of new results to show each time the more results button is pressed (max: 24)
orderByRelevanceText: "Relevance" // the text to be shown in order select box to describe 'order by relevance' option
},
suggestions: {
show: true, // whether to show search suggestions
maxQuerySuggestions: 3, // the maximum number of query suggestions
querySuggestionHeadline: undefined, // the headline of the query suggestions, leave blank if no headline should be shown
emptyQuerySuggestions: undefined,
showImages: false, // show images in search suggestions
num: 6, // the maximum number of search suggestions to be shown
minChars: 3, // minimum number of characters before the suggestions shows, default: 3,
maxWidth: 'auto', // the maximum width of the suggest box, default: as wide as the input box, at least 275px
throttleTime: 300, // the number of milliseconds before the suggest is triggered after finished input, default: 300ms
extraHtml: undefined, // extra HTML code that is shown in each search suggest, you can even show values of datapoints here,
highlight: true, // whether matched words should be highlighted, default: true
},
smart404: { /* The caption of the search results. */
caption: 'These links might be useful', /* The string in the title that identifies the page as a 404 page. */
identifier: 'Page not found', /* A CSS selector that points to the area in which the alternative links should be shown. */
resultSelector: '#ss360-404',
}
};
</script>
<script src="https://cdn.sitesearch360.com/sitesearch360-v11.min.js" async></script>
</body>
</html>