Welcome and backup device flow (#26)
* Comment out redirect to prevent re-register * Welcome screen completed * Rename LinkDevice to Welcome; Add Backup route * Add a new page for Backup Device * Move bootstrapFilesystem to webnative implementation Co-authored-by: Jess Martin <jessmartin@gmail.com> * Rename appName.ts to app-name.ts Co-authored-by: Jess Martin <jessmartin@gmail.com> * Refactor register and backup routes and components Co-authored-by: Jess Martin <jessmartin@gmail.com> Co-authored-by: Brian Ginsburg <gins@brianginsburg.com>
This commit is contained in:
parent
1aa4463812
commit
143ec66d1f
|
|
@ -56,5 +56,5 @@ See the [Fission Guide](https://guide.fission.codes/developers/installation) and
|
|||
## Customize
|
||||
|
||||
- `app.html` - the SEO meta tags will need to be changed.
|
||||
- `lib/appName.ts` - choose a better application name
|
||||
- `lib/app-name.ts` - choose a better application name
|
||||
- To customize the application's Tailwind theme, change `tailwind.config.ts` - link to DaisyUI customization page.
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
<h1>Link Device</h1>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import type { BackupView } from '$lib/views'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const navigate = (view: BackupView) => {
|
||||
dispatch('navigate', { view })
|
||||
}
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="are-you-sure-modal" checked class="modal-toggle" />
|
||||
<div class="modal">
|
||||
<div class="modal-box w-80 relative text-center">
|
||||
<div>
|
||||
<h3 class="mb-7 text-xl font-serif">Are you sure?</h3>
|
||||
|
||||
<p class="mt-8 mb-6">
|
||||
Without a backup device, if you lose this device or reset your browser,
|
||||
you will not be able to recover your account data.
|
||||
</p>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
on:click={() => navigate('backup-device')}
|
||||
>
|
||||
Connect a backup device
|
||||
</button>
|
||||
<a class="text-error underline block mt-4" href="/">
|
||||
YOLO—I'll risk just one device for now
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import { appName } from '$lib/app-name'
|
||||
import type { BackupView } from '$lib/views'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const navigate = (view: BackupView) => {
|
||||
dispatch('navigate', { view })
|
||||
}
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="backup-modal" checked class="modal-toggle" />
|
||||
<div class="modal">
|
||||
<div class="modal-box w-80 relative text-center">
|
||||
<div id="backup-message" class="peer-checked:hidden">
|
||||
<h3 class="mb-7 text-xl font-serif">Backup your account</h3>
|
||||
<p class="mt-8 mb-4">
|
||||
Your {appName} account & its data live only on your devices.
|
||||
</p>
|
||||
|
||||
<p class="mt-8 mb-4">
|
||||
We highly recommend backing up your account on at least one additional
|
||||
device.
|
||||
</p>
|
||||
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
on:click={() => navigate('backup-device')}
|
||||
>
|
||||
Connect a backup device
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-xs btn-link text-base font-normal underline mt-4"
|
||||
on:click={() => navigate('are-you-sure')}
|
||||
>
|
||||
Skip for now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import type { BackupView } from '$lib/views'
|
||||
import ClipboardIcon from '$components/icons/ClipboardIcon.svelte'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const navigate = (view: BackupView) => {
|
||||
dispatch('navigate', { view })
|
||||
}
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="backup-device-modal" checked class="modal-toggle" />
|
||||
<div class="modal">
|
||||
<div class="modal-box w-80 relative text-center">
|
||||
<div>
|
||||
<h3 class="mb-7 text-xl font-serif">Connect a backup device</h3>
|
||||
|
||||
<!-- GIANT QR CODE GOES HERE -->
|
||||
QR Codes
|
||||
|
||||
<p class="mt-8 mb-4">
|
||||
Scan this code on the new device, or share the connection link.
|
||||
</p>
|
||||
|
||||
<button class="btn btn-primary btn-outline" href="/backup">
|
||||
<ClipboardIcon />
|
||||
<span class="ml-2">Copy connection link</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-xs btn-link text-base text-error font-normal underline mt-4"
|
||||
on:click={() => navigate('are-you-sure')}
|
||||
>
|
||||
Skip for now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,22 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
|
||||
import { appName } from '$lib/appName'
|
||||
import { sessionStore } from '../../stores'
|
||||
import type { Session } from '$lib/session'
|
||||
|
||||
let unsubscribeSessionStore: () => void = () => {}
|
||||
|
||||
onMount(() => {
|
||||
unsubscribeSessionStore = sessionStore.subscribe((session: Session) => {
|
||||
if (session.authed) {
|
||||
goto('/')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onDestroy(unsubscribeSessionStore)
|
||||
import { appName } from '$lib/app-name'
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="my-modal-5" checked class="modal-toggle" />
|
||||
|
|
@ -1,16 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
|
||||
import { appName } from '$lib/appName'
|
||||
import { appName } from '$lib/app-name'
|
||||
import {
|
||||
bootstrapFilesystem,
|
||||
isUsernameValid,
|
||||
isUsernameAvailable,
|
||||
register
|
||||
} from '$lib/common/webnative'
|
||||
import { filesystemStore, sessionStore } from '../../stores'
|
||||
import type { Session } from '$lib/session'
|
||||
import CheckIcon from '$components/icons/CheckIcon.svelte'
|
||||
import XIcon from '$components/icons/XIcon.svelte'
|
||||
|
||||
|
|
@ -20,20 +14,11 @@
|
|||
let registrationSuccess = true
|
||||
let checkingUsername = false
|
||||
|
||||
let unsubscribeSessionStore: () => void = () => {}
|
||||
|
||||
onMount(() => {
|
||||
unsubscribeSessionStore = sessionStore.subscribe((session: Session) => {
|
||||
if (session.authed) {
|
||||
goto('/')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const checkUsername = async (event: Event) => {
|
||||
checkingUsername = true
|
||||
const { value } = event.target as HTMLInputElement
|
||||
|
||||
username = value
|
||||
checkingUsername = true
|
||||
|
||||
usernameValid = await isUsernameValid(username)
|
||||
|
||||
|
|
@ -46,27 +31,10 @@
|
|||
|
||||
const registerUser = async () => {
|
||||
registrationSuccess = await register(username)
|
||||
|
||||
if (registrationSuccess) {
|
||||
sessionStore.update(session => ({
|
||||
...session,
|
||||
username,
|
||||
authed: true
|
||||
}))
|
||||
|
||||
console.log('session after registration', $sessionStore)
|
||||
|
||||
const fs = await bootstrapFilesystem()
|
||||
filesystemStore.set(fs)
|
||||
|
||||
goto('/linkDevice')
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(unsubscribeSessionStore)
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="my-modal-5" checked class="modal-toggle" />
|
||||
<input type="checkbox" id="register-modal" checked class="modal-toggle" />
|
||||
<div class="modal">
|
||||
<div class="modal-box w-80 relative text-center">
|
||||
<a href="/" class="btn btn-xs btn-circle absolute right-2 top-2">✕</a>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<script lang="ts">
|
||||
import { sessionStore } from '../../../stores'
|
||||
import WelcomeCheckIcon from '$components/icons/WelcomeCheckIcon.svelte'
|
||||
import { appName } from '$lib/app-name'
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="link-device-modal" checked class="modal-toggle" />
|
||||
<div class="modal">
|
||||
<div class="modal-box w-80 relative text-center">
|
||||
<div>
|
||||
<h3 class="mb-7 text-xl font-serif">
|
||||
Welcome, {$sessionStore.username}!
|
||||
</h3>
|
||||
<div class="flex justify-center">
|
||||
<span>
|
||||
<WelcomeCheckIcon />
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mt-8 mb-4">Your account has been created.</p>
|
||||
|
||||
<div class="mb-8">
|
||||
<input type="checkbox" id="password-message" class="peer hidden" />
|
||||
<label
|
||||
class="text-primary underline mb-8 hover:cursor-pointer peer-checked:hidden"
|
||||
for="password-message"
|
||||
>
|
||||
Wait—what's my password?
|
||||
</label>
|
||||
<p class="hidden peer-checked:block">
|
||||
You don't need a password! <br />
|
||||
{appName} uses public key cryptography to authenticate you with this
|
||||
device.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a class="btn btn-primary" href="/backup">Continue</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<svg
|
||||
width="19"
|
||||
height="21"
|
||||
viewBox="0 0 19 21"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.5 3.5H3.5C2.39543 3.5 1.5 4.39543 1.5 5.5V17.5C1.5 18.6046 2.39543 19.5 3.5 19.5H13.5C14.6046 19.5 15.5 18.6046 15.5 17.5V16.5M5.5 3.5C5.5 4.60457 6.39543 5.5 7.5 5.5H9.5C10.6046 5.5 11.5 4.60457 11.5 3.5M5.5 3.5C5.5 2.39543 6.39543 1.5 7.5 1.5H9.5C10.6046 1.5 11.5 2.39543 11.5 3.5M11.5 3.5H13.5C14.6046 3.5 15.5 4.39543 15.5 5.5V8.5M17.5 12.5H7.5M7.5 12.5L10.5 9.5M7.5 12.5L10.5 15.5"
|
||||
stroke="#3B82F6"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 621 B |
|
|
@ -0,0 +1,14 @@
|
|||
<svg
|
||||
width="100"
|
||||
height="101"
|
||||
viewBox="0 0 100 101"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M26.8559 9.92063C30.8429 9.60246 34.6279 8.03465 37.6722 5.44038C44.7761 -0.613591 55.2246 -0.613591 62.3286 5.44038C65.3728 8.03465 69.1579 9.60246 73.1449 9.92063C82.449 10.6631 89.8372 18.0513 90.5797 27.3554C90.8978 31.3424 92.4656 35.1274 95.0599 38.1717C101.114 45.2757 101.114 55.7241 95.0599 62.8281C92.4656 65.8724 90.8978 69.6574 90.5797 73.6444C89.8372 82.9485 82.449 90.3367 73.1449 91.0792C69.1579 91.3973 65.3728 92.9652 62.3286 95.5594C55.2246 101.613 44.7761 101.613 37.6722 95.5594C34.6279 92.9652 30.8429 91.3973 26.8559 91.0792C17.5518 90.3367 10.1636 82.9485 9.42112 73.6444C9.10295 69.6574 7.53514 65.8724 4.94087 62.8281C-1.1131 55.7241 -1.1131 45.2757 4.94087 38.1717C7.53514 35.1274 9.10295 31.3424 9.42112 27.3554C10.1636 18.0513 17.5518 10.6631 26.8559 9.92063ZM72.9845 42.484C75.4057 40.0627 75.4057 36.1371 72.9845 33.7158C70.5632 31.2946 66.6376 31.2946 64.2163 33.7158L43.8004 54.1318L35.7845 46.1158C33.3632 43.6946 29.4376 43.6946 27.0163 46.1158C24.5951 48.5371 24.5951 52.4627 27.0163 54.884L39.4163 67.284C41.8376 69.7052 45.7632 69.7052 48.1844 67.284L72.9845 42.484Z"
|
||||
fill="#6EE7B7"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -77,6 +77,15 @@ export const isUsernameAvailable = async (
|
|||
export const register = async (username: string): Promise<boolean> => {
|
||||
const { success } = await webnative.account.register({ username })
|
||||
|
||||
const fs = await bootstrapFilesystem()
|
||||
filesystemStore.set(fs)
|
||||
|
||||
sessionStore.update(session => ({
|
||||
...session,
|
||||
username,
|
||||
authed: true
|
||||
}))
|
||||
|
||||
return success
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { appName } from '$lib/appName'
|
||||
import { appName } from '$lib/app-name'
|
||||
|
||||
export type Session = {
|
||||
username: string
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export type BackupView = 'backup' | 'backup-device' | 'are-you-sure'
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
import { onMount } from 'svelte'
|
||||
|
||||
import '../global.css'
|
||||
import { appName } from '$lib/appName'
|
||||
import { appName } from '$lib/app-name'
|
||||
import { initialize } from '$lib/common/webnative'
|
||||
import { deviceStore, sessionStore, theme } from '../stores'
|
||||
import { storeTheme } from '$lib/theme'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import type { BackupView } from '$lib/views'
|
||||
import Backup from '$components/auth/backup/Backup.svelte'
|
||||
import BackupDevice from '$components/auth/backup/BackupDevice.svelte'
|
||||
import AreYouSure from '$components/auth/backup/AreYouSure.svelte'
|
||||
|
||||
let view: BackupView = 'backup'
|
||||
|
||||
const navigate = (event: CustomEvent<{ view: BackupView }>) => {
|
||||
view = event.detail.view
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if view === 'backup'}
|
||||
<Backup on:navigate={navigate} />
|
||||
{:else if view === 'backup-device'}
|
||||
<BackupDevice on:navigate={navigate} />
|
||||
{:else if view === 'are-you-sure'}
|
||||
<AreYouSure on:navigate={navigate} />
|
||||
{/if}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import Connect from '$components/auth/Connect.svelte'
|
||||
import Connect from '$components/auth/connect/Connect.svelte'
|
||||
</script>
|
||||
|
||||
<Connect />
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
<script lang="ts">
|
||||
import LinkDevice from '$components/auth/LinkDevice.svelte'
|
||||
</script>
|
||||
|
||||
<LinkDevice />
|
||||
|
|
@ -1,5 +1,12 @@
|
|||
<script lang="ts">
|
||||
import Register from '$components/auth/Register.svelte'
|
||||
import { sessionStore } from '../stores'
|
||||
|
||||
import Register from '$components/auth/register/Register.svelte'
|
||||
import Welcome from '$components/auth/register/Welcome.svelte'
|
||||
</script>
|
||||
|
||||
<Register />
|
||||
{#if $sessionStore.authed}
|
||||
<Welcome />
|
||||
{:else}
|
||||
<Register />
|
||||
{/if}
|
||||
|
|
|
|||
Loading…
Reference in New Issue