Skip to content

Commit

Permalink
Adding some install steps to app tutorial, and adding a page to
Browse files Browse the repository at this point in the history
explain the contacts list components and store
  • Loading branch information
ElliotFriend committed Jul 24, 2023
1 parent 7792448 commit a132303
Show file tree
Hide file tree
Showing 7 changed files with 425 additions and 27 deletions.
12 changes: 6 additions & 6 deletions docs/building-apps/example-application-tutorial/_category_.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"position": 55,
"label": "Example Application Tutorial",
"link": {
"type": "generated-index"
}
}
"position": 55,
"label": "Example Application Tutorial",
"link": {
"type": "generated-index"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@ sidebar_position: 20

Accounts are the central data structure in Stellar and can only exist with a valid keypair (a public and secret key) and the required minimum balance of XLM. Read more in the [Accounts section](../../fundamentals-and-concepts/stellar-data-structures/accounts).

To start, well have our user create an account. In BasicPay, the signup page will display a randomized public and secret keypair that the user can select with the option to choose a new set if preferred.
To start, we'll have our user create an account. In BasicPay, the signup page will display a randomized public and secret keypair that the user can select with the option to choose a new set if preferred.

:::info

Since we are building a [non-custodial application](../application-design-considerations#non-custodial-service), the secret key will only ever live in the browser. It will never be shared with the server or anybody else.
Since we are building a [non-custodial application], the encrypted secret key will only ever live in the browser. It will never be shared with a server or anybody else.

:::

![public and private keys](/assets/public-and-private-keys.png)

Next, well trigger the user to submit a pincode to encrypt their secret key before it gets saved to their browsers `localStorage` (this is handled by the SDK). The user will need to remember their pincode for future logins and to submit transactions.
Next, we'll trigger the user to submit a pincode to encrypt their secret key before it gets saved to their browser's `localStorage` (this is handled by the [`js-stellar-wallets` SDK]). The user will need to remember their pincode for future logins and to submit transactions.

With BasicPay, when the user clicks the “Signup” button, they will be asked to confirm their pincode. When they do, the `create_account` operation is triggered, and the users account is automatically funded with XLM for the minimum balance (starting with 10,000 XLM).
With BasicPay, when the user clicks the “Signup” button, they will be asked to confirm their pincode. When they do, the `create_account` operation is triggered, and the user's account is automatically funded with XLM for the minimum balance (starting with 10,000 XLM).

![funded account](/assets/funded-account.png)

When you’re ready to move the application to Pubnet, accounts will need to be funded with real XLM. This is something the application can cover itself by depositing XLM into the user’s account (as we did with BasicPay on Testnet), with the use of [sponsored reserves](../../encyclopedia/sponsored-reserves), or the user can cover the required balance with their own XLM.
When you're ready to move the application to Pubnet, accounts will need to be funded with real XLM. This is something the application can cover itself by depositing XLM into the user's account (this is how we did it with BasicPay on Testnet, using Friendbot), with the use of [sponsored reserves], or the user can cover the required balance with their own XLM.

[non-custodial application]: <../application-design-considerations#non-custodial-service>
[`js-stellar-wallets` SDK]: <https://github.com/stellar/js-stellar-wallets>
[sponsored reserves]: <../../encyclopedia/sponsored-reserves>
155 changes: 155 additions & 0 deletions docs/building-apps/example-application-tutorial/contacts-list.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
---
title: Contacts List
sidebar_position: 15
---

A central feature for _BasicPay_ is a list of contacts that contains a user's name, and a Stellar address associated with it. While this particular function doesn't do a _tremendous_ amount of Stellar-related functionality, it makes sense for it to be the first thing we discuss. Understanding this simple component will set a good foundation for you to understand some of the more advanced components later on.

## Creating a `localStorage` store

As with the rest of our user-data, the contacts list will live in the browser's `localStorage`. We are using the [`svelt-local-storage-store` package] to facilitate this. We create a Svelte `store` to hold the data, and add a few custom functions to manage the list: `empty`, `remove`, `add`, `favorite`, and `lookup`.

:::note

This tutorial code is simplified for display here. The code is fully typed, documented, and commented in the [source code repository].

:::

```js file="/src/lib/stores/contactsStore.js"
import { v4 as uuidv4 } from 'uuid'
import { persisted } from 'svelte-local-storage-store'
import { StrKey } from 'stellar-sdk'
import { error } from '@sveltejs/kit'
import { get } from 'svelte/store'

// We are wrapping this store in its own function which will allow us to write
// and customize our own store functions to maintain consistent behavior
// wherever the actions need to take place.
function createContactsStore() {
const { subscribe, set, update } = persisted('bpa:contactList', [])

return {
subscribe,

// Erases all contact entries from the list and creates a new, empty contact list.
empty: () => set([]),

// Removes the specified contact entry from the list.
remove: (id) => update((list) => list.filter((contact) => contact.id !== id)),

// Adds a new contact entry to the list with the provided details.
add: (contact) =>
update((list) => {
if (StrKey.isValidEd25519PublicKey(contact.address)) {
return [...list, { ...contact, id: uuidv4() }]
} else {
throw error(400, { message: 'invalid public key' })
}
}),

// Toggles the "favorite" field on the specified contact.
favorite: (id) =>
update((list) => {
const i = list.findIndex((contact) => contact.id === id)
if (i >= 0) {
list[i].favorite = !list[i].favorite
}
return list
}),

// Searches the contact list for an entry with the specified address.
lookup: (address) => {
let list = get(contacts)
let i = list.findIndex((contact) => contact.address === address)
if (i >= 0) {
return list[i].name
} else {
return false
}
},
}
}

// We export `contacts` as the variable that can be used to interact with the contacts store.
export const contacts = createContactsStore()
```

Source: <https://github.com/stellar/basic-payment-app/blob/main/src/lib/stores/contactsStore.js>

## Accessing the `localStore` data

The `contacts` store is now exported form this file, and can be accessed and used inside a Svelte page or component. Here is how we've implemented a "favorite contacts" component for displaying on the main BasicPay dashboard.

:::note

In our `*.svelte` component files, we will not dive too deeply into the HTML markup outside of the `<script>` tags. The Svelte syntax is primarily used for iterating and is quite undertandable.

:::

```svelte file="/src/routes/dashboard/components/FavoriteContacts.svelte"
<script>
// We import the `contacts` store into our Svelte component
// highlight-next-line
import { contacts } from '$lib/stores/contactsStore'
import TruncatedKey from '$lib/components/TruncatedKey.svelte'

// `$:` makes a Svelte variable reactive, so it will be re-computed any time
// the `$contacts` store is modified. We access a Svelte store by adding `$`
// to the beginning of the variable name.
// highlight-next-line
$: favoriteContacts = $contacts?.filter((contact) => contact.favorite)
</script>

<h3>Favorite Contacts</h3>
<table class="table w-full">
<thead>
<tr>
<th>Favorite</th>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
{#if favoriteContacts}
<tbody>
{#each favoriteContacts as contact (contact.id)}
<tr>
<th>
<input
type="checkbox"
class="checkbox-accent checkbox checkbox-sm"
checked={contact.favorite}
on:click={() => contacts.favorite(contact.id)}
/>
</th>
<td>
<div class="flex items-center space-x-3">
<div class="avatar">
<div class="not-prose w-10 rounded-full">
<img
src="https://id.lobstr.co/{contact.address}.png"
alt="Avatar Tailwind CSS Component"
/>
</div>
</div>
<div>
<div class="font-bold">{contact.name}</div>
</div>
</div>
</td>
<td>
<TruncatedKey keyText={contact.address} lookupName={false} />
</td>
<th>
<button class="btn-ghost btn-xs btn">Stellar.Expert</button>
</th>
</tr>
{/each}
</tbody>
{/if}
</table>
```
Source: <https://github.com/stellar/basic-payment-app/blob/main/src/routes/dashboard/components/FavoriteContacts.svelte>
[`svelt-local-storage-store` package]: <https://github.com/joshnuss/svelte-local-storage-store>
[source code repository]: <https://github.com/stellar/basic-payment-app>
Loading

0 comments on commit a132303

Please sign in to comment.