如何在blockstack上寫一個區塊鏈「微博」程序?

Blockstack.js 工具教程css

在本教程中,咱們將使用Gaia存儲系統構建一個區塊鏈微博應用程序,從而使用戶提交的文字和圖片沒有發佈到中心服務器中,而是在本身的本地電腦。
此應用程序將是一個徹底分散且無需服務器的access . js應用程序。標識和存儲服務將由blockstack提供,html

應用程序將可以執行如下操做:react

  • 使用blockstack驗證用戶
  • 用戶發佈相似傳統微博(微信朋友圈)樣式的新狀態
  • 在用戶配置文件中顯示狀態
  • 查找其餘用戶並查看其配置文件和狀態

咱們將使用如下工具:webpack

  • 用於管理依賴關係和腳本的NPM
  • yo爲塊堆棧反應應用程序生成樣板文件
  • 用於驗證用戶和訪問Gaia存儲的blockstack

對於經驗豐富的blockstack開發人員:git

  • 添加publish _ data做用域以登陸請求
  • 使用getFile ( ' filename . JSON ',{username: ' username . id ',decrypt: false } )從其餘用戶讀取文件。
  • 使用lookupProfile ( 'username. id ' )查找用戶配置文件
  • 如前所述使用putFile ( ' filename . JSON ',file,options ),其中選項設置爲{ encrypt : false },以便禁用加密,其餘人能夠讀取您的文件。

安裝與生成github

首先,安裝Yeoman以及blockstack應用程序生成器:web

npm install -g yo generator-blockstack

接下來,爲咱們的應用程序建立一個目錄,取個新的名字Publik :npm

mkdir publik && cd publik

而後,使用blockstack應用程序生成器生成一個簡單的blockstack應用程序:json

yo blockstack:react

響應提示後,應用程序生成器將建立全部應用程序文件,而後安裝全部依賴項。瀏覽器

要在本地運行應用程序:

npm start

並將瀏覽器打開到http://localhost : 8080。如今您應該看到一個簡單的react應用程序,您可使用blockstack ID登陸。

多用戶層數據存儲

在多用戶層數據存儲中,存儲在Gaia上的用戶文件經過用戶配置文件中的apps屬性對其餘用戶可見。使用多用戶層數據存儲的每一個應用程序都必須將其自身添加到用戶的配置文件. JSON文件中。在身份驗證期間請求publish _ data做用域時,blockstack瀏覽器將自動處理此部分。

所以,咱們須要作的第一件事是修改身份驗證請求以包括publish _ data範圍。
打開src/components/App.jsx並找到下面的方法:

handleSignIn(e) {
  e.preventDefault();
  redirectToSignIn();
}

將方法修改成:

handleSignIn(e) {
  e.preventDefault();
  const origin = window.location.origin
  redirectToSignIn(origin, origin + '/manifest.json', ['store_write', 'publish_data'])
}

請注意,默認狀況下,身份驗證請求包括啓用存儲的store _ write範圍。
若是您想註銷並再次登陸,程序將爲提示用戶身份驗證的請求,你將會容許該程序發佈你本地所存儲的文字或圖片。

multi-player-storage-auth.png

發佈狀態:

在此步驟中,咱們將添加容許發佈和顯示「狀態」的功能。
讓咱們打開src / components / profile . jsx,而後從blockstack. js中導入咱們將使用的幾個方法。這些方法是putFile ( )、getFile ( )和lookupProfile ( )。將它們添加到文件頂部附近blockstack的import語句中:

import {
  isSignInPending,
  loadUserData,
  Person,
  getFile,
  putFile,
  lookupProfile
} from 'blockstack';

而後,咱們須要在構造函數( )的初始狀態中添加一些屬性。構造函數應以下所示:

constructor(props) {
  super(props);

  this.state = {
    person: {
      name() {
        return 'Anonymous';
      },
      avatarUrl() {
        return avatarFallbackImage;
      },
    },
    username: "",
    newStatus: "",
    statuses: [],
    statusIndex: 0,
    isLoading: false    
  };
}

如今,讓咱們修改render ( )方法以添加文本輸入和提交按鈕,以即可以發佈用戶的「微博狀態」。將render ( )方法替換爲如下內容:

render() {
  const { handleSignOut } = this.props;
  const { person } = this.state;
  const { username } = this.state;

  return (
    !isSignInPending() && person ?
    <div className="container">
      <div className="row">
        <div className="col-md-offset-3 col-md-6">
          <div className="col-md-12">
            <div className="avatar-section">
              <img
                src={ person.avatarUrl() ? person.avatarUrl() : avatarFallbackImage }
                className="img-rounded avatar"
                id="avatar-image"
              />
              <div className="username">
                <h1>
                  <span id="heading-name">{ person.name() ? person.name()
                    : 'Nameless Person' }</span>
                  </h1>
                <span>{username}</span>
                <span>
                  &nbsp;|&nbsp;
                  <a onClick={ handleSignOut.bind(this) }>(Logout)</a>
                </span>
              </div>
            </div>
          </div>

          <div className="new-status">
            <div className="col-md-12">
              <textarea className="input-status"
                value={this.state.newStatus}
                onChange={e => this.handleNewStatusChange(e)}
                placeholder="What's on your mind?"
              />
            </div>
            <div className="col-md-12">
              <button
                className="btn btn-primary btn-lg"
                onClick={e => this.handleNewStatusSubmit(e)}
              >
                Submit
              </button>
            </div>
          </div>

        </div>
      </div>
    </div> : null
  );
}

在上面的render ( )方法中,咱們還顯示了用戶的blockstack ID。咱們須要從用戶配置文件數據中提取這一點。找到componentWillMount ( )方法,並在person屬性下面添加用戶名屬性:

componentWillMount() {
  this.setState({
    person: new Person(loadUserData().profile),
    username: loadUserData().username
  });
}

接下來,咱們將添加兩種方法來處理輸入事件:

handleNewStatusChange(event) {
  this.setState({newStatus: event.target.value})
}

handleNewStatusSubmit(event) {
  this.saveNewStatus(this.state.newStatus)
  this.setState({
    newStatus: ""
  })
}

而後執行所需存儲操做,下面是保存一個「微博狀態」的方法:

saveNewStatus(statusText) {
  let statuses = this.state.statuses

  let status = {
    id: this.state.statusIndex++,
    text: statusText.trim(),
    created_at: Date.now()
  }

  statuses.unshift(status)
  const options = { encrypt: false }
  putFile('statuses.json', JSON.stringify(statuses), options)
    .then(() => {
      this.setState({
        statuses: statuses
      })
    })
}

如今,您應該能夠在文本框中鍵入一個狀態,而後按「提交」按鈕將其保存。
當您按下submit (提交)按鈕時,您將看到什麼也不會發生。由於咱們沒有添加任何代碼來顯示狀態。

顯示狀態

返回render ( )方法,並在包含文本輸入和提交按鈕的div元素正下方添加如下塊。

<div className="col-md-12 statuses">
  {this.state.isLoading && <span>Loading...</span>}
  {this.state.statuses.map((status) => (
      <div className="status" key={status.id}>
        {status.text}
      </div>
    )
  )}
</div>

咱們還須要在頁面加載時獲取狀態,因此咱們添加一個名爲fetchData ( )的新方法,並從componentDidMount ( )方法調用它

componentDidMount() {
  this.fetchData()
}

fetchData() {
  this.setState({ isLoading: true })
  const options = { decrypt: false }
  getFile('statuses.json', options)
    .then((file) => {
      var statuses = JSON.parse(file || '[]')
      this.setState({
        person: new Person(loadUserData().profile),
        username: loadUserData().username,
        statusIndex: statuses.length,
        statuses: statuses,
      })
    })
    .finally(() => {
      this.setState({ isLoading: false })
    })
}

此時,咱們有一個基本的「區塊鏈微博」應用程序,能夠用來發布和查看本身的狀態。可是,沒法查看其餘用戶的狀態。在接下來的步驟中,咱們將進入「多用戶數據存儲」部分。可是首先,讓咱們花點時間來完善咱們的應用程序。
打開src/styles/style.css,並用如下內容替換現有樣式:

/* Globals */
a,a:focus,a:hover{color:#fff;}
html,body{height:100%;text-align:center;background-color:#191b22;}
body{color:#fff}
.hide{display:none;}
.landing-heading{font-family:'Lato',Sans-Serif;font-weight:400;}

/* Buttons */
.btn{font-family:'Lato',Sans-Serif;padding:0.5625rem 2.5rem;font-size:0.8125rem;font-weight:400;line-height:1.75rem;border-radius:0!important;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-ms-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;}
.btn-lg{font-size:1.5rem;padding:0.6875rem 3.4375rem;line-height:2.5rem;}
.btn:focus,.btn:active:focus,.btn.active:focus{outline:none;}
.btn-primary{color:#fff;border:1px solid #2C96FF;background-color:#2C96FF;}
.btn-primary:hover,.btn-primary:focus,.btn-primary:active{color:#fff;border:1px solid #1a6ec0;background-color:#1a6ec0;}

/* Avatar */
.avatar{width:100px;height:100px;}
.avatar-section{margin-bottom:25px;display:flex;text-align:left;}
.username{margin-left:20px;}

/* Scaffolding */
.site-wrapper{display:table;width:100%;height:100vh;min-height:100%;}
.site-wrapper-inner{display:flex;flex-direction:column;justify-content:center;margin-right:auto;margin-left:auto;width:100%;height:100vh;}
.panel-authed{padding:0 0 0 0;}

/* Home button */
.btn-home-hello{position:absolute;font-family:'Source Code Pro',monospace;font-size:11px;font-weight:400;color:rgba(255,255,255,0.85);top:15px;left:15px;padding:3px 20px;background-color:rgba(255,255,255,0.15);border-radius:6px;-webkit-box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);-moz-box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);box-shadow:0px 0px 20px 0px rgba(0,0,0,0.15);}

/* Input */
input, textarea{color:#000;padding:10px;}
.input-status{width:100%;height:70px;border-radius:6px;}
.new-status{text-align:right;}

/* Statuses */
.statuses{padding-top:30px;}
.status{margin:15px 0px;padding:20px;background-color:#2e2e2e;border-radius:6px}

若是一切順利,咱們最終的結果應該是這樣的:

multi-player-storage-status.png

用戶配置文件查找

如今讓咱們修改 Profile.jsx以顯示其餘用戶的配置文件。咱們將使用blockstack . js提供的lookupProfile ( )方法,咱們在前面的導入語句中添加了該方法。lookupProfile ( )接受單個參數,該參數是要查找的配置文件的blockstack ID,並返回配置文件對象。
首先,咱們將對應用程序的路由結構進行一些更改,以便經過訪問http:// localhost:8080/other_user.id來查看其餘用戶的配置文件
安裝 react-router:

npm install --save react-router-dom

打開src/index.js並添加到文件頂部:

import { BrowserRouter } from 'react-router-dom'

接下來,將src / index . js中的reactdom . render ( )方法更改成:

ReactDOM.render((
  <BrowserRouter>
    <App />
  </BrowserRouter>
), document.getElementById('root'));

Then we'll need to go back to src/components/App.jsx and add the new route. Open src/components/App.jsx and import the Switch and Route components from react-router-dom:

import { Switch, Route } from 'react-router-dom'

接下來,在render ( )方法中找到下面的行:

: <Profile handleSignOut={ this.handleSignOut } />

並將其替換爲:

:
  <Switch>
    <Route
      path='/:username?'
      render={
        routeProps => <Profile handleSignOut={ this.handleSignOut } {...routeProps} />
      }
    />
  </Switch>

這會設置路由並捕獲要用做配置文件查找用戶名的路由參數。
咱們還須要在webpack配置中添加一個規則,以便正確處理包含的URL路徑。角色。例如: http://localhost:8080/other_user.id 注意:在生產應用程序中,須要配置web服務器來處理此問題。
在根項目目錄中打開webpack . config . js,而後找到如下行:

historyApiFallback: {
  disableDotRule: true
},

更改成:

historyApiFallback: {
  disableDotRule: true
},

**注:咱們須要再次運行啓動NPM,這樣才能生效。
如今,咱們跳回到src / components / profile . jsx,並添加一個方法來肯定是查看本地用戶的配置文件仍是其餘用戶的配置文件。**

isLocal() {
  return this.props.match.params.username ? false : true
}

而後咱們能夠修改fetchData ( )方法,以下所示:

fetchData() {
  this.setState({ isLoading: true })
  if (this.isLocal()) {
    const options = { decrypt: false }
    getFile('statuses.json', options)
      .then((file) => {
        var statuses = JSON.parse(file || '[]')
        this.setState({
          person: new Person(loadUserData().profile),
          username: loadUserData().username,
          statusIndex: statuses.length,
          statuses: statuses,
        })
      })
      .finally(() => {
        this.setState({ isLoading: false })
      })
  } else {
    const username = this.props.match.params.username

    lookupProfile(username)
      .then((profile) => {
        this.setState({
          person: new Person(profile),
          username: username
        })
      })
      .catch((error) => {
        console.log('could not resolve profile')
      })
  }
}

咱們首先使用isLocal ( )檢查是否正在查看本地用戶配置文件或其餘用戶的配置文件。若是是本地用戶配置文件,咱們將運行前面添加的getFile ( )函數。不然,咱們使用lookupProfile ( )方法查找屬於用戶名的配置文件。

注意:對於https部署,用於名稱查找的默認塊堆棧核心API端點應更改成指向經過https提供的核心API。不然,因爲瀏覽器阻止混合內容,名稱查找將失敗。有關詳細信息,請參閱blockstack . js文檔。

爲了獲取用戶的狀態,咱們在調用lookupProfile (用戶名)後當即向fetchData ( )添加如下lookupProfile(username)... catch((error)=>{..}塊:

const options = { username: username, decrypt: false }
getFile('statuses.json', options)
  .then((file) => {
    var statuses = JSON.parse(file || '[]')
    this.setState({
      statusIndex: statuses.length,
      statuses: statuses
    })
  })
  .catch((error) => {
    console.log('could not fetch statuses')
  })
  .finally(() => {
    this.setState({ isLoading: false })
  })

最後,咱們須要有條件地呈現註銷按鈕、狀態輸入文本框和提交按鈕,以便它們在查看其餘用戶的配置文件時不會顯示。在render ( )方法中,經過使用{ isLocal ( ) & &...}條件:

{this.isLocal() &&
  <span>
    &nbsp;|&nbsp;
    <a onClick={ handleSignOut.bind(this) }>(Logout)</a>
  </span>
}

//...

{this.isLocal() &&
  <div className="new-status">
    <div className="col-md-12">
      <textarea className="input-status"
        value={this.state.newStatus}
        onChange={this.handleNewStatusChange}
        placeholder="What's on your mind?"
      />
    </div>
    <div className="col-md-12 text-right">
      <button
        className="btn btn-primary btn-lg"
        onClick={this.handleNewStatusSubmit}
      >
        Submit
      </button>
    </div>
  </div>
}

ok!將瀏覽器指向http://localhost:8080/your_blockstack.id以查看配置文件。注意:您須要有一個註冊的blockstack ID才能使此操做生效。

要查看本教程的完整源代碼,請訪問:https://github.com/larrysalib...

歡迎加入blockstack中國開發者羣q(官方blockstack core開發者在內):加微信:six四九四叄壹六seven一

相關文章
相關標籤/搜索