Browse Source

feat: update todos app tutorial

feat/connect-improvements-2
Mark Hendrickson 4 years ago
committed by Thomas Osmonson
parent
commit
da7c36b3d2
  1. 13271
      package-lock.json
  2. BIN
      public/images/todo-list-app.png
  3. 1
      public/images/todos-choose-account.svg
  4. 1
      public/images/todos-copy-secret-key.svg
  5. 1
      public/images/todos-generation.svg
  6. 1
      public/images/todos-home-authenticated.svg
  7. BIN
      public/images/todos-home.png
  8. BIN
      public/images/todos-intro.png
  9. 1
      public/images/todos-ive-saved-it.svg
  10. 1
      public/images/todos-public.svg
  11. 1
      public/images/todos-sign-in.svg
  12. 1
      public/images/todos-username.svg
  13. BIN
      public/images/todos/choose-account.png
  14. BIN
      public/images/todos/choose-username.png
  15. BIN
      public/images/todos/copy-secret-key.png
  16. BIN
      public/images/todos/get-started.png
  17. BIN
      public/images/todos/home-public.png
  18. BIN
      public/images/todos/home.png
  19. BIN
      public/images/todos/landing.png
  20. BIN
      public/images/todos/saved-secret-key.png
  21. BIN
      public/images/todos/secret-key-generation.png
  22. BIN
      public/images/todos/sign-in.png
  23. 194
      src/pages/authentication/building-todo-app.md
  24. 6
      src/pages/authentication/overview.md
  25. 6
      src/pages/build-apps.md

13271
package-lock.json

File diff suppressed because it is too large

BIN
public/images/todo-list-app.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

1
public/images/todos-choose-account.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 442 KiB

1
public/images/todos-copy-secret-key.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 493 KiB

1
public/images/todos-generation.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 415 KiB

1
public/images/todos-home-authenticated.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 87 KiB

BIN
public/images/todos-home.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

BIN
public/images/todos-intro.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

1
public/images/todos-ive-saved-it.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 501 KiB

1
public/images/todos-public.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 94 KiB

1
public/images/todos-sign-in.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 430 KiB

1
public/images/todos-username.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 440 KiB

BIN
public/images/todos/choose-account.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

BIN
public/images/todos/choose-username.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

BIN
public/images/todos/copy-secret-key.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

BIN
public/images/todos/get-started.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
public/images/todos/home-public.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
public/images/todos/home.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
public/images/todos/landing.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
public/images/todos/saved-secret-key.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

BIN
public/images/todos/secret-key-generation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

BIN
public/images/todos/sign-in.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

194
src/pages/authentication/building-todo-app.md

@ -1,6 +1,6 @@
--- ---
title: Building a Todo app title: Review Todos app
description: Learn how to integrate authentication and data storage. description: Learn about Stacks authentication and storage by reviewing simple app
experience: beginners experience: beginners
duration: 30 minutes duration: 30 minutes
tags: tags:
@ -10,25 +10,25 @@ images:
sm: /images/pages/todo-app-sm.svg sm: /images/pages/todo-app-sm.svg
--- ---
# Building a Todo app # Learn from Todos app with Stacks
![What you'll be creating in this tutorial](/images/todo-list-app.png) ![What you'll be studying in this tutorial](/images/todos/home.png)
## Introduction ## Introduction
In this tutorial, you will learn about Stacks authentication and storage by installing, In this tutorial, you will learn about Stacks authentication and storage by installing,
running and reviewing the code for a "Todos" web app built with Stacks authentication and [React](https://reactjs.org/). running and reviewing the code for a "Todos" app built with Stacks authentication and storage.
This app highlights the following platform functionality: This app highlights the following platform functionality:
- Generate Secret Key with associated BNS username to authenticate app - Generate _Secret Key_ with associated BNS username to authenticate app
- Add, edit and delete encrypted app data with Gaia - Add, edit and delete encrypted app data with Gaia
- Decrypt data on Gaia for public sharing by URL - Decrypt data on Gaia for public sharing by URL
- Unauthenticate and re-authenticate app with Secret Key - Unauthenticate and re-authenticate app with _Secret Key_
[Preview the app](https://todos.blockstack.org) or [view its code on GitHub](https://github.com/blockstack/stacks-todos). [Try the app](https://todos.blockstack.org) or [view its code on GitHub](https://github.com/blockstack/todos).
Existing familiarity with React is recommended for reviewing this app's code. Existing familiarity with [React](https://reactjs.org/) is recommended for reviewing this app's code.
## Install and run the app ## Install and run the app
@ -38,8 +38,7 @@ You must have recent versions of Git and [Node.js](https://nodejs.org/en/downloa
### Step 1: Install the code and its dependencies ### Step 1: Install the code and its dependencies
```bash ```bash
git clone https://github.com/blockstack/blockstack-todos git clone https://github.com/blockstack/todos && cd todos
cd blockstack-todos
npm install npm install
``` ```
@ -54,7 +53,7 @@ You should see output similar to the following:
```bash ```bash
Compiled successfully! Compiled successfully!
You can now view Stacks-todo in the browser. You can now view todos in the browser.
http://localhost:3000/ http://localhost:3000/
@ -66,101 +65,79 @@ To create a production build, use yarn build.
You should see the app's landing page: You should see the app's landing page:
![The homepage of the todos app](/images/todos-home.png) !["Todos app landing" screen](/images/todos/landing.png)
## Onboard into your first Stacks app ## Onboard into your first Stacks app
### Step 1: Choose **Get started** to start onboarding into the app. ### Step 1: Choose **Get started** to start onboarding into the app.
The app displays a standardized introductory modal using The app displays a standardized introductory modal using
[Stacks authentication](https://github.com/blockstack/stacks.js/tree/master/packages/auth) Stacks authentication.
![The Stacks Connect Modal](/images/todos-intro.png) ![The Stacks Connect Modal](/images/todos/get-started.png)
Below, you can see the relevant parts of the [React component](https://reactjs.org/docs/react-component.html) This modal is displayed using the `authenticate` function exported by the `src/stacks.js` module, which organizes all Stacks resources needed across the app:
that triggers this modal in [`src/components/Signin.jsx`](https://github.com/blockstack/stacks-todos/blob/master/src/components/Signin.jsx):
```js ```js
// src/components/Signin.jsx // src/stacks.js
import { useConnect } from '@stacks/auth'; import { AppConfig, UserSession, showBlockstackConnect } from '@stacks/auth';
import { Person } from '@stacks/profile';
export const Signin = () => { import { Storage } from '@stacks/storage';
const { doOpenAuth } = useConnect();
return <Button onClick={() => doOpenAuth()}>Get Started</Button>;
};
```
This component imports the [React hook](https://reactjs.org/docs/hooks-overview.html)
[`useConnect`](https://github.com/blockstack/ux/blob/master/packages/connect/src/react/hooks/use-connect.ts)
from the Stacks authentication library.
`useConnect` returns many helper functions such as
[`doOpenAuth`](https://github.com/blockstack/ux/blob/master/packages/connect/src/react/hooks/use-connect.ts#L33),
which triggers this modal upon click of the "Get started" button.
The modal is designed to prepare new users for a different type of relationship with
Stacks apps, one in which they authenticate with a _Secret Key_ that's used to encrypt
their private data.
The modal displays the app's name and icon as configured in
[`src/components/App.jsx`](https://github.com/blockstack/stacks-todos/blob/master/src/components/App.jsx#L26):
```jsx const appConfig = new AppConfig(['store_write', 'publish_data']);
// src/components/App.jsx
export const userSession = new UserSession({ appConfig });
export const storage = new Storage({ userSession });
export function authenticate(sendToSignIn) {
showBlockstackConnect({
appDetails: {
name: 'Todos',
icon: window.location.origin + '/logo.svg',
},
redirectTo: '/',
finished: () => {
window.location.reload();
},
sendToSignIn: sendToSignIn,
userSession: userSession,
});
}
appDetails: { export function getUserData() {
name: 'Stacks App', return userSession.loadUserData();
icon: window.location.origin + '/favicon.ico'
} }
export function getPerson() {
return new Person(getUserData().profile);
}
``` ```
This component loads the [`UserSession`](https://blockstack.github.io/stacks.js/classes/usersession.html) The `authenticate` function implements the `showBlockstackConnect` function imported from the `@stacks/auth` library.
module from `@stacks/auth`
```js
import { UserSession } from '@stacks/auth';
import { appConfig } from '../assets/constants';
// ... `showBlockstackConnect` triggers the display of a modal that initiates the authentication process for users, one in which they'll authenticate with a _Secret Key_ that's used to encrypt their private data.
const userSession = new UserSession({ appConfig }); The `showBlockstackConnect` function accepts a number of properties within a parameter object such as:
```
This module handles user session operations and is initiated using the - The app's `name` and `icon`: provided as strings comprising the `appDetails` object property.
[`appConfig`](https://github.com/blockstack/stacks-todos/blob/master/src/assets/constants.js#L3) object, - The `redirectTo` string: used to provide a URL to which the user should be redirected upon successful authentication. The `finished` callback serves a similar purpose by handling successful authentication within a context of a popup window.
which contains an array of [scopes](/authentication/overview#scopes) that indicate just what permissions - The `sendToSignIn` boolean: used to indicate whether the user should proceed through the "registration" flow (in which they'll be given a newly generated _Secret Key_) or whether they should proceed through the "sign in" flow (in which they'll be prompted to enter an existing _Secret Key_).
to grant during authentication: - The `userSession` object: used to pass the [scopes](/authentication/overview#scopes) needed by the app.
```js Note how the `userSession` object is created at the beginning of this module by leveraging an `AppConfig` object that's first initiated with all relevant scopes.
// src/assets/constants.js
export const appConfig = new AppConfig(['store_write', 'publish_data']); The [`UserSession`](https://blockstack.github.io/stacks.js/classes/usersession.html) and [`AppConfig`](https://blockstack.github.io/stacks.js/classes/appconfig.html) classes are themselves imported from the `@stacks/auth` library.
```
The `appDetails` and `userSession` objects are joined by the callback function In the separate `src/components/App.jsx` component, we see how
[`finished`](https://github.com/blockstack/stacks-todos/blob/master/src/components/App.jsx#L31) `componentDidMount` loads the user's data into the app's state, whether upon redirect post-authentication with `userSession.handlePendingSignIn()` or upon detection of an existing session with `userSession.isUserSignedIn()`:
in configuring Stacks authentication with the `authOptions` object:
```js ```jsx
// src/components/App.jsx // src/components/App.jsx
finished: ({ userSession }) => { import { userSession } from '../stacks';
this.setState({ userData: userSession.loadUserData() });
};
```
This function simply saves data about the user into the app's state upon authentication.
Further down in the component we see in ...
[`componentDidMount`](https://github.com/blockstack/stacks-todos/blob/master/src/components/App.jsx#L46)
that it checks upon mount to either process completion of authentication with `userSession.handlePendingSignIn()`
or otherwise load session data into app state as above with `userSession.isUserSignedIn()`:
```jsx
// src/components/App.jsx
componentDidMount() { componentDidMount() {
if (userSession.isSignInPending()) { if (userSession.isSignInPending()) {
@ -176,10 +153,10 @@ componentDidMount() {
### Step 2: Choose **Get started** to generate a _Secret Key_. ### Step 2: Choose **Get started** to generate a _Secret Key_.
The app triggers a popup window in which [the Stacks App](https://github.com/blockstack/ux/tree/master/packages/app) The app triggers a popup window in which [Stacks Connect](https://github.com/blockstack/ux/tree/master/packages/app)
is loaded from [`app.blockstack.org`](http://app.blockstack.org/) and begins generating a new _Secret Key_. loads from [`app.blockstack.org`](http://app.blockstack.org/) and begins generating a new _Secret Key_.
![What the UI looks like when a new ID is generated](/images/todos-generation.svg) !["Secret Key generation" screen](/images/todos/secret-key-generation.png)
### Step 3: Choose **Copy Secret Key** to copy your _Secret Key_ to the clipboard. ### Step 3: Choose **Copy Secret Key** to copy your _Secret Key_ to the clipboard.
@ -190,20 +167,19 @@ all of the private data they create and manage with Stacks apps.
_Secret Keys_ are like strong passwords. However, they can never be recovered if lost or reset if stolen. _Secret Keys_ are like strong passwords. However, they can never be recovered if lost or reset if stolen.
As such, it's paramount that users handle them with great care. As such, it's paramount that users handle them with great care.
![An example of a secret key](/images/todos-copy-secret-key.svg) !["Copy Secret Key" screen](/images/todos/copy-secret-key.png)
### Step 4: Choose **I've saved it** to confirm you've secured your _Secret Key_ in a suitable place. ### Step 4: Choose **I've saved it** to confirm you've secured your _Secret Key_ in a suitable place.
![An example of the I've saved it screen](/images/todos-ive-saved-it.svg) !["I've saved it" screen](/images/todos/saved-secret-key.png)
### Step 5: Enter a username value and choose **Continue** ### Step 5: Enter a username value and choose **Continue**
The username will be used by the app to generate a URL for sharing your todos, should you choose to make them public. The username will be used by the app to generate a URL for sharing your todos, should you choose to make them public.
It is registered on the Stacks blockchain with [BNS](/core/naming/introduction) It is registered on the Stacks blockchain with [BNS](/core/naming/introduction) and associated with your _Secret Key_.
and associated with your _Secret Key_.
![Choosing a user name example](/images/todos-username.svg) !["Choose username" screen](/images/todos/choose-username.png)
### Done: You've now completed onboarding into the app! ### Done: You've now completed onboarding into the app!
@ -212,17 +188,16 @@ and associated with your _Secret Key_.
Once you've authenticated the app, you can can start adding todos by entering values into the "Write your to do" Once you've authenticated the app, you can can start adding todos by entering values into the "Write your to do"
field and hitting "Enter". field and hitting "Enter".
![The authenticated view of the todos app](/images/todos-home-authenticated.svg) !["Todos app home" screen](/images/todos/home.png)
The data for all todos are saved as JSON to the Gaia hub linked to your Secret Key using the The data for all todos are saved as JSON to the Gaia hub linked to your Secret Key using the [`putFile`](http://blockstack.github.io/stacks.js/classes/storage.html#putfile) method of the `storage` object in the `src/data-store.js` module:
[`putFile`](http://blockstack.github.io/stacks.js/classes/storage.html#putfile) method of the `userSession` object in the
[`src/assets/data-store.js`](https://github.com/blockstack/blockstack-todos/blob/master/src/assets/data-store.js#L26) module:
```jsx ```jsx
import { Storage } from '@stacks/storage'; import { storage } from './stacks';
...
export const saveTasks = async (userSession, tasks, isPublic) => { export const saveTasks = async (userSession, tasks, isPublic) => {
const storage = new Storage(userSession);
await storage.putFile(TASKS_FILENAME, JSON.stringify({ tasks, isPublic }), { await storage.putFile(TASKS_FILENAME, JSON.stringify({ tasks, isPublic }), {
encrypt: !isPublic, encrypt: !isPublic,
}); });
@ -233,20 +208,23 @@ These todos are subsequently loaded using the [`getFile`](http://blockstack.gith
method of the same object in the same module: method of the same object in the same module:
```jsx ```jsx
import { Storage } from '@stacks/storage'; import { storage } from './stacks';
...
export const fetchTasks = async (userSession, username) => { export const fetchTasks = async (userSession, username) => {
const storage = new Storage(userSession);
const tasksJSON = await storage.getFile(TASKS_FILENAME, { const tasksJSON = await storage.getFile(TASKS_FILENAME, {
decrypt: false, decrypt: false,
username: username || undefined, username: username || undefined,
}); });
// code to format and return the tasks ...
}; };
``` ```
Note how the `storage` object imported here is originally instantiated in the `stacks` module using `new Storage({ userSession });`, where `userSession` is the same object as used with authentication. It's provided here to ensure that all storage calls are made with the user's Gaia hub.
By default, the `putFile` and `getFile` methods automatically encrypt data when saved and decrypt it when retrieved, By default, the `putFile` and `getFile` methods automatically encrypt data when saved and decrypt it when retrieved,
using the user's Secret Key. This ensures that only the user has the ability to view this data. using the user's _Secret Key_. This ensures that only the user has the ability to view this data.
When deleting a todo, the same `putFile` method is used to save a new JSON array of todos that excludes the deleted todo. When deleting a todo, the same `putFile` method is used to save a new JSON array of todos that excludes the deleted todo.
@ -254,7 +232,7 @@ When deleting a todo, the same `putFile` method is used to save a new JSON array
Select "Make public" to make your todos accessible to the public for sharing via URL. Select "Make public" to make your todos accessible to the public for sharing via URL.
![Public todos view](/images/todos-public.svg) !["Public todos" screen](/images/todos/home-public.png)
This will call `saveTasks` with the `isPublic` parameter set to `true`, which is used to disable encryption when using `putFile`. This will call `saveTasks` with the `isPublic` parameter set to `true`, which is used to disable encryption when using `putFile`.
@ -264,38 +242,36 @@ The app will now show all of your todos to anyone who visits the URL displayed w
Select "Sign out" to deauthenticate the app with your Stacks account. Select "Sign out" to deauthenticate the app with your Stacks account.
This triggers an event, which This calls the [`signUserOut`](https://blockstack.github.io/stacks.js/classes/usersession.html#signuserout) method
[under the hood](https://github.com/blockstack/stacks-todos/blob/master/src/components/Header.jsx#L47) of the `userSession` object within `src/components/Header.jsx`.
calls the [`signUserOut` method](https://blockstack.github.io/stacks.js/classes/usersession.html#signuserout)
of the `UserSession` object.
Now, visit the URL that was provided to you when you made your tasks public. This url is of the format `/todos/:username`, so if your username is `jane_doe.id.blockstack`, the URL would be [`localhost:3000/todos/jane_doe.id.blockstack`](http://localhost:3000/todos/jane_doe.id.blockstack). Now visit the URL that was provided to you when you made your tasks public. This URL has the format `/todos/:username`, so if your username were `janedoe.id.blockstack`, the URL would be `localhost:3000/todos/jane_doe.id.blockstack`.
When you visit this page, the `TodoList.jsx` component detects that there is a username in the URL. When you visit this page, the `TodoList.jsx` component detects that there is a username in the URL.
When there is a username, it calls `fetchTasks`, this time providing the `username` argument. This `username` When there is a username, it calls `fetchTasks`, this time providing the `username` argument. This `username`
option is then passed to `getFile`, which will lookup where that user's tasks are stored. option is then passed to `getFile`, which will look up where that user's tasks are stored.
## Sign back in ## Sign back in
At this point, you will be logged out from the app but not you'll still have an active session with the Stacks At this point, you will be logged out from the app but not you'll still have an active session with the Stacks
app itself on [app.blockstack.org](https://app.blockstack.org). Navigate to app.blockstack.org and select "Sign out" there if you want to deauthenticate the Stacks app as well. app itself on [app.blockstack.org](https://app.blockstack.org). Navigate to app.blockstack.org and select "Sign out" there if you want to deauthenticate the Stacks app as well.
Once signed out, select "Sign in" to sign back in with your _Secret Key_. Once signed out, select "Sign In" to sign back in with your _Secret Key_.
If you've previously deauthenticated the Stacks app, you'll see a prompt to enter your _Secret Key_: If you've previously deauthenticated the Stacks app, you'll see a prompt to enter your _Secret Key_:
![An example of a sign in screen](/images/todos-sign-in.svg) !["Sign in" screen](/images/todos/sign-in.png)
The above screen will be ommitted if you have an active session with the Stacks app already. The above screen will be ommitted if you have an active session with the Stacks app already.
Then you'll be presented with the option to select an existing username associated with your _Secret Key_ or Then you'll be presented with the option to select an existing username associated with your _Secret Key_ or
create a new one if you wish to authenticate the app with a different identity and data set: create a new one if you wish to authenticate the app with a different identity and data set:
![An example of the choose an account screen](/images/todos-choose-account.svg) !["Choose account" screen](/images/todos/choose-account.png)
You'll now see your todos as an authenticated user for the username you've chosen. You'll now see your todos as an authenticated user for the username you've chosen.
## Learn more ## Learn more
Read [the stacks.js reference](https://blockstack.github.io/stacks.js/) to learn more about the Read [the Stacks.js reference](https://blockstack.github.io/stacks.js/) to learn more about the
libraries used in this tutorial. libraries used in this tutorial.

6
src/pages/authentication/overview.md

@ -35,7 +35,7 @@ Scopes define the permissions requested by an app for granting during authentica
Apps may request any of the following scopes: Apps may request any of the following scopes:
| Scope | Definition | | Scope | Definition |
| -------------- | ------------------------------------------------------------------------------------ | | -------------- | ------------------------------------------------------------------------------------ |
| `store_write` | Read and write data to the user's Gaia hub in an app-specific storage bucket. | | `store_write` | Read and write data to the user's Gaia hub in an app-specific storage bucket. |
| `publish_data` | Publish data so that other users of the app can discover and interact with the user. | | `publish_data` | Publish data so that other users of the app can discover and interact with the user. |
@ -50,11 +50,11 @@ The following is an example manifest file.
```json ```json
{ {
"name": "Todo App", "name": "Todo App",
"start_url": "http://blockstack-todos.appartisan.com", "start_url": "http://todos.blockstack.org",
"description": "A simple todo app build on Stacks", "description": "A simple todo app build on Stacks",
"icons": [ "icons": [
{ {
"src": "http://blockstack-todos.appartisan.com/logo.png", "src": "http://todos.blockstack.org/logo.png",
"sizes": "400x400", "sizes": "400x400",
"type": "image/png" "type": "image/png"
} }

6
src/pages/build-apps.md

@ -1,6 +1,6 @@
--- ---
title: Building decentralized apps title: Building apps with Stacks
description: Overview and guides for getting started building decentralized applications. description: Resources for getting started with building apps on the network
images: images:
large: /images/pages/build-apps.svg large: /images/pages/build-apps.svg
sm: /images/pages/build-apps-sm.svg sm: /images/pages/build-apps-sm.svg
@ -8,7 +8,7 @@ images:
## Introduction ## Introduction
Prefer to jump right in? Get started with this tutorial where you’ll create a decentralized to-do list app. Prefer to jump right in? Get started with this tutorial where you’ll learn by reviewing the code for a todos app built with Stacks.
[@page-reference | inline] [@page-reference | inline]
| /authentication/building-todo-app | /authentication/building-todo-app

Loading…
Cancel
Save