New getting started guide (WIP) in experimental section of docs (#4548)

* Start drafting new quickstart guide

* Show how to run the app

* Very rough instructions for playing with the app

* Explain core of User template

* Start explaining UI code

* Example of daml react hook (useQuery for allUsers)

* Talk about daml2ts and start describing the new feature

* Start talking about DAML for posts feature

* Add ui file referenced in text

* Start describing changes to UI for Post feature

* Rework feature section wording as Messaging instead of Posts

* Write about additions to MainController

* Talk about new components before the view and controller (bottom up style)

* Describe additions to MainView (may change if we inline MainView into MainController)

* Adapt to create-daml-app removing MainController

* Fix code snippet and try to highlight tsx but fail

* Split guide into sections and rename some sections

* Improve start of app arch section

* Minor edits to code snippets and wording

* Update setup instructions with codegen step

* Update UI components in 'before' code

* Move and update section explaining TS codegen

* Copy in new DAML files

* Update UI code for messaging feature and some of the explanatory text

* Start reworking DAML feature section

* Redo DAML feature section

* Edit initial section

* Edit intro para of arch section

* Edit start of DAML explanation

* Edit template explanation

* Minor edit to UI explanation

* Improve wording of DAML explanation

* Rework sig/obs explanation

* Update User.daml file from create-daml-app and label AddFriend choice

* Explain AddFriend choice better

* Move new GSG to experimental features section

* Undo accidental change to vscode settings

changelog_begin
changelog_end

* Copyright headers

* Revert unwanted change

* Remove typescript highlighting which doesn't work

* Tweak explanation of code generation

* Remove driven
This commit is contained in:
Rohan Jacob-Rao 2020-02-17 12:59:05 -05:00 committed by GitHub
parent 3bde5bd0cb
commit 558f0d5042
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 718 additions and 2 deletions

View File

@ -12,7 +12,7 @@ Getting started
:maxdepth: 2
Installing the SDK <getting-started/installation>
getting-started/quickstart
Quickstart Guide <getting-started/quickstart>
Writing DAML
------------
@ -97,6 +97,7 @@ Experimental features
daml-script/index
tools/visual
daml2ts/index
getting-started/index
Support and updates
-------------------

View File

@ -0,0 +1,105 @@
.. Copyright (c) 2020 The DAML Authors. All rights reserved.
.. SPDX-License-Identifier: Apache-2.0
App Architecture
****************
In this section we'll look at the different components of our social network app.
The goal is to familiarise you enough to feel comfortable extending the code with a new feature in the next section.
There are two main components in the code - the DAML model and the React/TypeScript frontend - with generated TypeScript code to bridge the two.
Let's start by looking at the DAML model, as this sets the core logic of the application.
The DAML Model
==============
Using VSCode (or a code editor of your choice), navigate to the ``daml`` subdirectory.
There is a single DAML file called ``User.daml`` with the model for users of the app.
The core data is at the start of the ``User`` contract template.
.. literalinclude:: code/daml/User.daml
:language: daml
:start-after: -- MAIN_TEMPLATE_BEGIN
:end-before: -- MAIN_TEMPLATE_END
There are two important aspects here:
1. The data definition (a *schema* in database terms), describing the data stored with each user contract.
In this case it is an identifier for the user and their current list of friends.
Both fields use the built-in ``Party`` type which lets us use them in the following clauses.
2. The signatories and observers of the contract.
The signatories are the parties authorized to create new versions of the contract or archive the contract.
In this case only the user has those rights.
The observers are the parties who are able to view the contract on the ledger.
In this case all friends of a user are able to see the user contract.
Let's say what the ``signatory`` and ``observer`` clauses mean in our app more concretely.
A user Alice can see another user Bob in the network only when Alice is a friend in Bob's user contract.
For this to be true, Bob must have previously added Alice as a friend (i.e. updated his user contract), as he is the sole signatory on his user contract.
If not, Bob will be invisible to Alice.
We can see some concepts here that are central to DAML, namely *privacy* and *authorization*.
Privacy is about who can *see* what, and authorization is about who can *do* what.
In DAML we must answer these questions upfront, as they fundamentally change the design of an application.
The last thing we'll point out about the DAML model for now is the operation to add friends, called a *choice* in DAML.
.. literalinclude:: code/daml/User.daml
:language: daml
:start-after: -- ADDFRIEND_BEGIN
:end-before: -- ADDFRIEND_END
DAML contracts are *immutable* (can not be changed in place), so they must be updated by archiving the current instance and creating a new one.
That is what the ``AddFriend`` choice does: after checking some preconditions, it creates a new user contract with the new friend added to the list.
The ``choice`` syntax automatically includes the archival of the current instance.
.. TODO Update depending on consuming/nonconsuming choice.
Next we'll see how our DAML code is reflected and used on the UI side.
TypeScript Code Generation
==========================
The user interface for our app is written in `TypeScript <https://www.typescriptlang.org/>`_.
TypeScript is a variant of Javascript that provides more support in development through its type system.
In order to build an application on top of DAML, we need a way to refer to the DAML template and choices in TypeScript.
We do this using a DAML to TypeScript code generation tool in the DAML SDK.
To run code generation, we first need to compile the DAML model to an archive format (with a ``.dar`` extension).
Then the command ``daml codegen ts`` takes this file as argument to produce a number of TypeScript files in the specified location.
daml build
daml codegen ts .daml/dist/create-daml-app-0.1.0.dar -o daml-ts/src
We now have TypeScript types and companion objects in the ``daml-ts`` workspace which we can use from our UI code.
We'll see that next.
The UI
======
Our UI is written using `React <https://reactjs.org/>`_ and
React helps us write modular UI components using a functional style - a component is rerendered whenever one of its inputs changes - combined with a judicious use of global state.
We can see the latter in the way we handle ledger state throughout the application code.
For this we use a state management feature in React called `Hooks <https://reactjs.org/docs/hooks-intro.html>`_.
You can see the capabilities of the DAML React hooks in ``create-daml-app/ui/src/daml-react-hooks/hooks.ts``.
For example, we can query the ledger for all visible contracts (relative to a particular user), create contracts and exercise choices on contracts.
.. TODO Update location to view DAML react hooks API
Let's see some examples of DAML React hooks.
.. literalinclude:: code/ui-before/MainView.tsx
:start-after: -- HOOKS_BEGIN
:end-before: -- HOOKS_END
This is the start of the component which provides data from the current state of the ledger to the main screen of our app.
The three declarations within ``MainView`` all use DAML hooks to get information from the ledger.
For instance, ``allUsers`` uses a catch-all query to get the ``User`` contracts on the ledger.
However, the query respects the privacy guarantees of a DAML ledger: the contracts returned are only those visible to the currently logged in party.
This explains why you cannot see *all* users in the network on the main screen, only those who have added you as a friend (making you an observer of their ``User`` contract).
.. TODO You also see friends of friends; either explain or prevent this.

View File

@ -0,0 +1,45 @@
-- Copyright (c) 2020 The DAML Authors. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0
daml 1.2
module User where
-- MAIN_TEMPLATE_BEGIN
template User with
username: Party
friends: [Party]
where
signatory username
observer friends
-- MAIN_TEMPLATE_END
key username: Party
maintainer key
-- ADDFRIEND_BEGIN
choice AddFriend: ContractId User with
friend: Party
controller username
do
assertMsg "You cannot add yourself as a friend" (friend /= username)
assertMsg "You cannot add a friend twice" (friend `notElem` friends)
create this with friends = friend :: friends
-- ADDFRIEND_END
-- SENDMESSAGE_BEGIN
nonconsuming choice SendMessage: ContractId Message with
sender: Party
content: Text
controller sender
do
create Message with sender, receiver = username, content
-- SENDMESSAGE_END
-- MESSAGE_BEGIN
template Message with
sender: Party
receiver: Party
content: Text
where
signatory sender, receiver
-- MESSAGE_END

View File

@ -0,0 +1,27 @@
// Copyright (c) 2020 The DAML Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react'
import { List, ListItem } from 'semantic-ui-react';
import { Message } from '@daml2ts/create-daml-app/lib/create-daml-app-0.1.0/User';
type Props = {
messages: Message[];
}
/**
* React component to show a feed of messages for a particular user.
*/
const Feed: React.FC<Props> = ({messages}) => {
const showMessage = (message: Message): string => {
return (message.sender + ": " + message.content);
}
return (
<List relaxed>
{messages.map((message) => <ListItem>{showMessage(message)}</ListItem>)}
</List>
);
}
export default Feed;

View File

@ -0,0 +1,138 @@
// Copyright (c) 2020 The DAML Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import { Container, Grid, Header, Icon, Segment, Divider } from 'semantic-ui-react';
import { Party } from '@daml/types';
import { useParty, useReload, useExerciseByKey, useFetchByKey, useQuery } from '@daml/react';
import UserList from './UserList';
import PartyListEdit from './PartyListEdit';
// -- IMPORTS_BEGIN
import { User, Message } from '@daml2ts/create-daml-app/lib/create-daml-app-0.1.0/User';
import MessageEdit from './MessageEdit';
import Feed from './Feed';
// -- IMPORTS_END
const MainView: React.FC = () => {
const username = useParty();
const myUserResult = useFetchByKey<User, Party>(User, () => username, [username]);
const myUser = myUserResult.contract?.payload;
const allUsersResult = useQuery<User, Party>(User);
const allUsers = allUsersResult.contracts.map((user) => user.payload);
const reload = useReload();
const [exerciseAddFriend] = useExerciseByKey(User.AddFriend);
const [exerciseRemoveFriend] = useExerciseByKey(User.RemoveFriend);
// -- HOOKS_BEGIN
const messagesResult = useQuery(Message, () => ({receiver: username}), []);
const messages = messagesResult.contracts.map((message) => message.payload);
const [exerciseSendMessage] = useExerciseByKey(User.SendMessage);
// -- HOOKS_END
const addFriend = async (friend: Party): Promise<boolean> => {
try {
await exerciseAddFriend(username, {friend});
return true;
} catch (error) {
alert("Unknown error:\n" + JSON.stringify(error));
return false;
}
}
const removeFriend = async (friend: Party): Promise<void> => {
try {
await exerciseRemoveFriend(username, {friend});
} catch (error) {
alert("Unknown error:\n" + JSON.stringify(error));
}
}
// -- SENDMESSAGE_BEGIN
const sendMessage = async (content: string, receiver: string): Promise<boolean> => {
try {
await exerciseSendMessage(receiver, {sender: username, content});
return true;
} catch (error) {
alert("Error while sending message:\n" + JSON.stringify(error));
return false;
}
}
// -- SENDMESSAGE_END
React.useEffect(() => {
const interval = setInterval(reload, 5000);
return () => clearInterval(interval);
}, [reload]);
return (
<Container>
<Grid centered columns={2}>
<Grid.Row stretched>
<Grid.Column>
<Header as='h1' size='huge' color='blue' textAlign='center' style={{padding: '1ex 0em 0ex 0em'}}>
{myUser ? `Welcome, ${myUser.username}!` : 'Loading...'}
</Header>
<Segment>
<Header as='h2'>
<Icon name='user' />
<Header.Content>
{myUser?.username ?? 'Loading...'}
<Header.Subheader>Me and my friends</Header.Subheader>
</Header.Content>
</Header>
<Divider />
<PartyListEdit
parties={myUser?.friends ?? []}
onAddParty={addFriend}
onRemoveParty={removeFriend}
/>
</Segment>
<Segment>
<Header as='h2'>
<Icon name='globe' />
<Header.Content>
The Network
<Icon
link
name='sync alternate'
size='small'
style={{marginLeft: '0.5em'}}
onClick={reload}
/>
<Header.Subheader>Others and their friends</Header.Subheader>
</Header.Content>
</Header>
<Divider />
<UserList
users={allUsers.sort((user1, user2) => user1.username.localeCompare(user2.username))}
onAddFriend={addFriend}
/>
</Segment>
// -- MESSAGES_SEGMENT_BEGIN
<Segment>
<Header as='h2'>
<Icon name='pencil square' />
<Header.Content>
Messages
<Header.Subheader>Send a message to a friend</Header.Subheader>
</Header.Content>
</Header>
<MessageEdit
sendMessage={sendMessage}
/>
<Divider />
<Feed messages={messages} />
</Segment>
// -- MESSAGES_SEGMENT_END
</Grid.Column>
</Grid.Row>
</Grid>
</Container>
);
}
export default MainView;

View File

@ -0,0 +1,60 @@
// Copyright (c) 2020 The DAML Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react'
import { Form, Input, Button } from 'semantic-ui-react';
import { Text } from '@daml/types';
type Props = {
sendMessage: (content: Text, receiver: string) => Promise<boolean>;
}
/**
* React component to edit a message to send to a friend.
*/
const MessageEdit: React.FC<Props> = ({sendMessage}) => {
const [content, setContent] = React.useState('');
const [receiver, setReceiver] = React.useState('');
const [isSubmitting, setIsSubmitting] = React.useState(false);
const submitMessage = async (event?: React.FormEvent) => {
if (event) {
event.preventDefault();
}
setIsSubmitting(true);
const success = await sendMessage(content, receiver);
setIsSubmitting(false);
if (success) {
setContent('');
setReceiver('');
}
}
return (
<Form onSubmit={submitMessage}>
<Input
fluid
transparent
readOnly={isSubmitting}
loading={isSubmitting}
placeholder='Choose a friend'
value={receiver}
onChange={(event) => setReceiver(event.currentTarget.value)}
/>
<br />
<Input
fluid
transparent
readOnly={isSubmitting}
loading={isSubmitting}
placeholder="Write a message"
value={content}
onChange={(event) => setContent(event.currentTarget.value)}
/>
<br />
<Button type="submit">Send</Button>
</Form>
);
};
export default MessageEdit;

View File

@ -0,0 +1,100 @@
// Copyright (c) 2020 The DAML Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import { Container, Grid, Header, Icon, Segment, Divider } from 'semantic-ui-react';
import { Party } from '@daml/types';
import { useParty, useReload, useExerciseByKey, useFetchByKey, useQuery } from '@daml/react';
import UserList from './UserList';
import PartyListEdit from './PartyListEdit';
import { User } from '@daml2ts/create-daml-app/lib/create-daml-app-0.1.0/User';
const MainView: React.FC = () => {
// -- HOOKS_BEGIN
const username = useParty();
const myUserResult = useFetchByKey<User, Party>(User, () => username, [username]);
const myUser = myUserResult.contract?.payload;
const allUsersResult = useQuery<User, Party>(User);
const allUsers = allUsersResult.contracts.map((user) => user.payload);
// -- HOOKS_END
const reload = useReload();
const [exerciseAddFriend] = useExerciseByKey(User.AddFriend);
const [exerciseRemoveFriend] = useExerciseByKey(User.RemoveFriend);
const addFriend = async (friend: Party): Promise<boolean> => {
try {
await exerciseAddFriend(username, {friend});
return true;
} catch (error) {
alert("Unknown error:\n" + JSON.stringify(error));
return false;
}
}
const removeFriend = async (friend: Party): Promise<void> => {
try {
await exerciseRemoveFriend(username, {friend});
} catch (error) {
alert("Unknown error:\n" + JSON.stringify(error));
}
}
React.useEffect(() => {
const interval = setInterval(reload, 5000);
return () => clearInterval(interval);
}, [reload]);
return (
<Container>
<Grid centered columns={2}>
<Grid.Row stretched>
<Grid.Column>
<Header as='h1' size='huge' color='blue' textAlign='center' style={{padding: '1ex 0em 0ex 0em'}}>
{myUser ? `Welcome, ${myUser.username}!` : 'Loading...'}
</Header>
<Segment>
<Header as='h2'>
<Icon name='user' />
<Header.Content>
{myUser?.username ?? 'Loading...'}
<Header.Subheader>Me and my friends</Header.Subheader>
</Header.Content>
</Header>
<Divider />
<PartyListEdit
parties={myUser?.friends ?? []}
onAddParty={addFriend}
onRemoveParty={removeFriend}
/>
</Segment>
<Segment>
<Header as='h2'>
<Icon name='globe' />
<Header.Content>
The Network
<Icon
link
name='sync alternate'
size='small'
style={{marginLeft: '0.5em'}}
onClick={reload}
/>
<Header.Subheader>Others and their friends</Header.Subheader>
</Header.Content>
</Header>
<Divider />
<UserList
users={allUsers.sort((user1, user2) => user1.username.localeCompare(user2.username))}
onAddFriend={addFriend}
/>
</Segment>
</Grid.Column>
</Grid.Row>
</Grid>
</Container>
);
}
export default MainView;

View File

@ -0,0 +1,143 @@
.. Copyright (c) 2020 The DAML Authors. All rights reserved.
.. SPDX-License-Identifier: Apache-2.0
Your First Feature: Messaging Friends
*************************************
Let's dive into implementing a feature for our social network app.
From that we'll get a better idea of how to build DAML applications using our template.
Right now our app allows us to add and remove friends, but we can't communicate with them!
Let's fix that by adding a private messaging feature.
We will allow a user to send messages to a number of friends at once, and see all the messages that have been sent to them.
Of course we must make sure that no one can see messages that were not sent to them.
We will see that DAML lets us implement this in a direct and intuitive way.
There are two parts to building the messaging feature: the DAML code and the UI.
Let's start with adding to the DAML code, on which we will base our UI changes.
DAML Changes
============
The DAML code defines the *workflow* of the application.
This means: what interactions between users (or *parties*) are permitted by the system?
In the context of our feature, the question is: when is a user allowed to message another user?
The approach we'll take is: a user Bob can message another user Alice if Alice has added Bob as a friend.
Remember that friendships are single-directional in our app.
So Alice adding Bob as a friend means that she gives permission (or *authority*) for Bob to send her a message.
In DAML this workflow is represented as a new choice on the ``User`` contract.
.. literalinclude:: code/daml/User.daml
:language: daml
:start-after: -- SENDMESSAGE_BEGIN
:end-before: -- SENDMESSAGE_END
Let's break this down.
The choice is ``nonconsuming`` because sending a message should not affect the existence of the ``User`` contract.
By convention, the choice returns the ``ContractId`` of the resulting ``Message`` contract (which we'll show next).
Next, the parameters to the choice are the sender (the party wishing to talk to the signatory of this ``User`` contract) and the message text.
The ``controller`` clause suggests that it is the ``sender`` who can exercise the choice.
Finally, the body of the choice simply creates the new ``Message`` with the sender, receiver and content.
Note that there is no explicit check in the choice that the ``sender`` is a friend of the user.
This is because the ``User`` contract is only ever visible to friends (the observers of the contract).
Now let's see the ``Message`` contract template.
This is very simple - data and no choices - as well as the ``signatory`` declaration.
.. literalinclude:: code/daml/User.daml
:language: daml
:start-after: -- MESSAGE_BEGIN
:end-before: -- MESSAGE_END
Note that we have two signatories on the ``Message`` contract: both the sender and receiver.
This enforces the fact that the contract creation (and archival) must be authorized by both parties.
Now we've specified the workflow of sending messages, let's integrate the functionality into our app.
TypeScript Code Generation
==========================
Remember that we interface with our DAML code from the UI components using the generated TypeScript.
Since we have changed our DAML code, we also need to rerun the TypeScript code generator.
Let's do this now by running::
daml build
daml codegen ts .daml/dist/create-daml-app-0.1.0.dar -o daml-ts/src
As the TypeScript code is generated into the separate ``daml-ts`` workspace on which the UI depends, we need to rebuild the workspaces from the root directory using::
yarn workspaces run build
We should now have the updated TypeScript code with equivalents of the ``Message`` template and ``SendMessage`` choice.
Now let's implement our messaging feature in the UI!
Messaging UI
============
Our messaging feature has two parts: a form with inputs for selecting friends and composing the message text, and a "feed" of messages that have been sent to you.
Both parts will be implemented as React components that render on the main screen.
Feed Component
--------------
The feed component is fairly straight-forward: it queries all ``Message`` contracts and displays their contents as a list.
Here is the code for the entire component.
.. literalinclude:: code/ui-after/Feed.tsx
The key point here is that for any particular user, the ``Message`` query yields exactly the messages that have been either written by or sent to that user.
This is due to how we modelled the signatories and observers in the ``Message`` template, and means we do not risk a privacy breach coming from the application code.
Message Edit Component
----------------------
In addition to the feed component, we need a component for composing messages and sending them using the appropriate choice on the ``User`` contract.
.. literalinclude:: code/ui-after/MessageEdit.tsx
In this component we use React hooks for the message content and receiver.
You can see these used in the ``submitMessage`` function, called when the "Send" button is clicked.
The ``isSubmitting`` state is used to ensure that message requests are processed one at a time.
The result of each send is a new ``Message`` contract created, after which the form is cleared.
View Component
--------------------
The ``MainView`` component is the workhorse of this application which queries the ledger for data and houses the different subcomponents (e.g. friends, the network and our messaging components above).
To support the messaging components, we will need DAML React hooks for querying ``Message`` contracts and exercising the ``SendMessage`` choice on our ``User`` contract.
First import the generated Typescript code for the ``Message`` contract template, as well as our two new components.
.. literalinclude:: code/ui-after/MainView.tsx
:start-after: -- IMPORTS_BEGIN
:end-before: -- IMPORTS_END
Then we declare the hooks themselves at the start of the component.
.. literalinclude:: code/ui-after/MainView.tsx
:start-after: -- HOOKS_BEGIN
:end-before: -- HOOKS_END
The ``messagesResult`` tracks the state of ``Message`` contracts on the ledger, where we specify no restrictions on the query.
We extract the actual message data in ``messages``.
The ``exerciseSendMessage`` hook gives us a function to exercise the appropriate choice on our ``User``.
We wrap this in another ``sendMessage`` function which splits an input string into a list of parties and then exercises the choice, reporting to the user in the case of an error.
.. literalinclude:: code/ui-after/MainView.tsx
:start-after: -- SENDMESSAGE_BEGIN
:end-before: -- SENDMESSAGE_END
Finally we can integrate our new messaging components into the main screen view.
In another segment we add the panel including our two new components: the ``MessageEdit`` and the ``Feed``.
.. literalinclude:: code/ui-after/MainView.tsx
:start-after: -- MESSAGES_SEGMENT_BEGIN
:end-before: -- MESSAGES_SEGMENT_END
You have now finished implementing your first end-to-end DAML feature!
Let's give the new functionality a spin.
We follow the instructions in "Running the app" to start up the new app.

View File

@ -0,0 +1,96 @@
.. Copyright (c) 2020 The DAML Authors. All rights reserved.
.. SPDX-License-Identifier: Apache-2.0
.. _new-quickstart:
Full Stack DAML
###############
**Disclaimer:** This guide is being actively developed.
Expect major changes to the tutorial text and minor changes to the template application.
The goal of this tutorial is to get you up and running with full-stack, DAML-driven app development. We will provide you with the template for a miniature social networking app, get you writing your first end-to-end feature and finally deploy your new app to a persistent ledger.
By the end of the tutorial, you should have an idea of what DAML contracts and ledgers are, how the UI interacts with them, and how you might solve a potential use case with a DAML solution. We do not aim to give a comprehensive guide to all DAML concepts and tools; for that, see the later sections of the documentation. With that, let's get started!
.. TODO: reference specific sections of docs instead of saying "later sections".
.. toctree::
:hidden:
app-architecture
first-feature
Prerequisites
*************
If you haven't already, see :doc:`installation` for the DAML SDK and VSCode development environment.
You will also need some common software tools to build and interact with the template project.
- `Git <https://git-scm.com/>`_ version control system
- `Yarn <https://yarnpkg.com/>`_ package manager for Javascript
- A terminal application for command line interaction
Running the app
***************
We'll start by getting the app up and running, and then explain the different components which we will later extend.
First off, open a terminal, clone the template project and move to the project folder::
git clone https://github.com/digital-asset/create-daml-app.git
cd create-daml-app
In order to connect the DAML model to the UI code, we need to run a code generation step (more on this later)::
daml build
daml codegen ts .daml/dist/create-daml-app-0.1.0.dar -o daml-ts/src
Now, use Yarn to install the project dependencies and build the app::
yarn install
yarn workspaces run build
You should see ``Compiled successfully.`` in the output if everything is working as expected.
.. TODO: Give instructions for possible failures.
We can now run the app in two steps.
You'll need two terminal windows running for this.
In one terminal, at the root of the ``create-daml-app`` directory, run the script::
./daml-start.sh
This compiles the DAML component of the project and starts a *Sandbox* ledger for the app.
The ledger in this case is stored in the Sandbox application memory; it is not persistent but is useful for testing and development.
We'll leave the Sandbox running to serve requests from the UI, which result in changes to the in-memory ledger.
In a second terminal, navigate to the ``create-daml-app/ui`` folder and run::
yarn start
This starts the UI application connected to the already running Sandbox.
The command should automatically open a window in your default browser at http://localhost:3000.
If it doesn't, just open that link in any web browser.
You may be asked whether to allow the app to receive network connections, which you should allow.
At this point you should see the login page for the social network.
.. TODO: Screenshot
Enter a user name of your choice and click the calculator icon next to the password field to generate a password token.
(We do not have proper authentication in this app for simplicity.)
Once you click "Sign up", you can see a screen with panels for your friends and the entire social network.
Initially these are both empty as you don't have any friends yet!
Go ahead and add some using the form (and remove them using the cross icons if you change your mind).
Now let's grow the network. Log out and sign up using the name of one of your friends.
Let's say your name is Alice and your friend's name is Bob.
Bob should now see Alice in the network (since she added him as a friend) and he is able to add her back.
Note that in this app, friendships can be added in one direction at a time, similar to how "followers" work in Twitter.
Add a few more friends as Bob, and play around a bit more by logging in as different users and adding/removing friends from the network.
This should give you a idea of the app's functionality.

View File

@ -18,7 +18,7 @@ DAML SDK documentation
:caption: Getting started
Installing the SDK <getting-started/installation>
getting-started/quickstart
Quickstart Guide <getting-started/quickstart>
.. toctree::
:titlesonly:
@ -102,6 +102,7 @@ DAML SDK documentation
DAML Script <daml-script/index>
tools/visual
daml2ts/index
getting-started/index
.. toctree::
:titlesonly: