Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: RecoveryPhrase & ViewMnemonicView & VerifyMnemonicView #6004

Merged
merged 13 commits into from
Mar 3, 2023
Merged
Original file line number Diff line number Diff line change
@@ -1,56 +1,31 @@
<script lang="ts">
import { onMount } from 'svelte'
import { Button, Icon, RecoveryPhrase, Text } from '@ui'

import { Button, Icon, RecoveryPhrase, Text, TextType } from '@ui'
import { OnboardingLayout } from '@components'
import { english } from '@auxiliary/wordlists'
import { mobile } from '@core/app'

import { localize } from '@core/i18n'
import { profileBackupRouter } from '@core/router'
import { Mnemonic, onboardingProfile, verifyAndStoreMnemonic } from '@contexts/onboarding'

export let busy = false
import { getWordChoices, Mnemonic, onboardingProfile, verifyAndStoreMnemonic } from '@contexts/onboarding'

const verifyRecoveryPhrase: Mnemonic = []
import { Icon as IconEnum } from '@auxiliary/icon'

let wordChoices = ['', '', '']
let verifyIndex = 0
let verified = false
const verifyRecoveryPhrase: Mnemonic = []
const wordElements: HTMLButtonElement[] = []

function fillChoices(): void {
const currentIndex = verifyRecoveryPhrase.length
const actualWord = $onboardingProfile?.mnemonic[currentIndex]
const random1 = generateRandomWord($onboardingProfile?.mnemonic)
const random2 = generateRandomWord([...$onboardingProfile?.mnemonic, random1])

wordChoices = [actualWord, random1, random2].sort(() => 0.5 - Math.random())
}

function generateRandomWord(excludedWords: string[]): string {
let word: string
let wordChoices: string[] = ['', '', '']
let verifyIndex: number = 0
let isVerified: boolean = false

do {
const wordIndex = Math.floor(Math.random() * english.length)
if (!excludedWords.includes(english[wordIndex])) {
word = english[wordIndex]
}
} while (!word)

return word
}

function handleChoice(word: string): void {
if ($mobile) {
const wordElement = document.getElementById(`recovery-word-${verifyIndex}`)
wordElement?.scrollIntoView({ behavior: 'smooth', block: 'center' })
}
function onChoiceClick(word: string): void {
verifyRecoveryPhrase[verifyIndex] = word
if ($onboardingProfile?.mnemonic[verifyIndex] === word) {
if (verifyIndex === $onboardingProfile?.mnemonic.length - 1) {
verified = true
isVerified = true
} else {
verifyIndex++
fillChoices()
wordChoices = getWordChoices(verifyRecoveryPhrase.length)
}
}
}
Expand All @@ -65,7 +40,7 @@
}

function onKeyPress(event: KeyboardEvent): void {
if (!verified) {
if (!isVerified) {
switch (event.key) {
case '1':
wordElements[0].click()
Expand All @@ -83,85 +58,61 @@
}

onMount(() => {
fillChoices()
wordChoices = getWordChoices(verifyRecoveryPhrase.length)
})
</script>

<svelte:window on:keypress={onKeyPress} />

<OnboardingLayout {onBackClick} {busy} reverseContent={$mobile && !verified}>
<div slot="title">
<Text type="h2" classes={!$mobile && verified && 'hidden'}
>{localize('views.onboarding.profileBackup.verifyMnemonic.title')}</Text
>
</div>
<div slot="leftpane__content">
{#if !verified}
<Text type="p" secondary classes={!$mobile ? 'mb-10' : ''}>
<OnboardingLayout {onBackClick}>
<title-container slot="title" class="block">
<Text type={TextType.h2} classes={isVerified && 'hidden'}>
{localize('views.onboarding.profileBackup.verifyMnemonic.title')}
</Text>
</title-container>
<leftpane-content slot="leftpane__content" class="block">
{#if !isVerified}
<Text secondary classes="'mb-10">
{localize('views.onboarding.profileBackup.verifyMnemonic.body')}
</Text>
{#if !$mobile}
<Text type="p" classes="mb-4"
>{localize('views.onboarding.profileBackup.verifyMnemonic.word')} #{verifyIndex + 1}</Text
<Text classes="mb-4">
{localize('views.onboarding.profileBackup.verifyMnemonic.word')} #{verifyIndex + 1}
</Text>
{#each wordChoices as word, i}
<button
type="button"
class="w-full flex flex-row p-4 mb-4 rounded-2xl border border-solid items-center justify-between
border-gray-300 dark:border-gray-700 hover:border-gray-500 dark:hover:border-gray-700
focus:border-gray-500 dark:focus:border-gray-700"
on:click={() => onChoiceClick(word)}
bind:this={wordElements[i]}
>
{#each wordChoices as word, i}
<button
type="button"
class="w-full flex flex-row p-4 mb-4 rounded-2xl border border-solid items-center justify-between border-gray-300 dark:border-gray-700 hover:border-gray-500 dark:hover:border-gray-700 focus:border-gray-500 dark:focus:border-gray-700"
on:click={() => handleChoice(word)}
bind:this={wordElements[i]}
>
<Text smaller classes="ml-3">{`${i + 1}. ${word}`}</Text>
<Icon icon="chevron-right" classes="text-gray-800 dark:text-white" />
</button>
{/each}
{/if}
<Text smaller classes="ml-3">{`${i + 1}. ${word}`}</Text>
<Icon icon={IconEnum.ChevronRight} classes="text-gray-800 dark:text-white" />
</button>
{/each}
{:else}
<div class="flex flex-col items-center bg-gray-100 dark:bg-gray-900 rounded-2xl mt-10 p-5">
<div class="bg-green-500 rounded-2xl relative -top-10">
<Icon icon="success-check" classes="text-white" />
</div>
<Text type="h2" classes="mb-5 text-center"
>{localize('views.onboarding.profileBackup.verifyMnemonic.verified')}</Text
>
<Text type="p" secondary classes="mb-2"
>{localize('views.onboarding.profileBackup.verifyMnemonic.verifiedBody')}</Text
>
<icon-container class="block bg-green-500 rounded-2xl relative -top-10">
<Icon icon={IconEnum.SuccessCheck} classes="text-white" />
</icon-container>
<Text type={TextType.h2} classes="mb-5 text-center">
{localize('views.onboarding.profileBackup.verifyMnemonic.verified')}
</Text>
<Text secondary classes="mb-2">
{localize('views.onboarding.profileBackup.verifyMnemonic.verifiedBody')}
</Text>
</div>
{/if}
</div>
<div slot="leftpane__action">
{#if verified}
</leftpane-content>
<leftpane-action slot="leftpane__action" class="block">
{#if isVerified}
<Button classes="w-full" onClick={onContinueClick} disabled={busy}>
{localize('actions.continue')}
</Button>
{:else if $mobile && !verified}
<Text type="p" classes="mb-4"
>{localize('views.onboarding.profileBackup.verifyMnemonic.word')} #{verifyIndex + 1}</Text
>
{#each wordChoices as word}
<button
type="button"
class="w-full flex flex-row p-4 mb-4 rounded-2xl border border-1 border-solid items-center justify-between border-gray-300 dark:border-gray-700 hover:border-gray-500 dark:hover:border-gray-700 focus:border-gray-500 dark:focus:border-gray-700"
on:click={() => handleChoice(word)}
>
<Text smaller classes="ml-3">{word}</Text>
<Icon icon="chevron-right" classes="text-gray-800 dark:text-white" />
</button>
{/each}
{/if}
</div>
<div
slot="rightpane"
class="w-full h-full flex flex-col items-center justify-center {$mobile ? 'my-4 p-0' : 'p-4'}"
>
{#if ($mobile && !verified) || !$mobile}
<RecoveryPhrase
classes="mb-8"
recoveryPhrase={$onboardingProfile?.mnemonic}
{verifyRecoveryPhrase}
disabled={busy}
/>
{/if}
</div>
</leftpane-action>
<rightpane-container slot="rightpane" class="w-full h-full flex flex-col items-center justify-center p-4">
<RecoveryPhrase recoveryPhrase={$onboardingProfile?.mnemonic} {verifyRecoveryPhrase} disabled={busy} />
</rightpane-container>
</OnboardingLayout>
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
<script lang="ts">
import { onMount } from 'svelte'
import { Button, Icon, RecoveryPhrase, Text } from '@ui'

import { Button, Icon, RecoveryPhrase, Text, TextType } from '@ui'
import { OnboardingLayout } from '@components'
import { mobile } from '@core/app'

import { localize } from '@core/i18n'
import { profileBackupRouter } from '@core/router'
import { downloadRecoveryKit } from '@core/utils'

import {
onboardingProfile,
generateMnemonicForOnboardingProfile,
onboardingProfile,
updateOnboardingProfile,
} from '@contexts/onboarding'
import { downloadRecoveryKit } from '@core/utils'

const busy = false
let hide = true
let hasRevealedRecoveryPhrase = false
import { Icon as IconEnum } from '@auxiliary/icon'

let isHidden: boolean = true
let hasRevealedRecoveryPhrase: boolean = false

function handleContinueClick(): void {
function onContinueClick(): void {
$profileBackupRouter.next()
}

function handleBackClick(): void {
function onBackClick(): void {
updateOnboardingProfile({ mnemonic: null })
$profileBackupRouter.previous()
}

function handleMnemonicVisibilityClick(): void {
hide = !hide
function onMnemonicVisibilityClick(): void {
isHidden = !isHidden
hasRevealedRecoveryPhrase = true
}

function handleDownloadClick(): void {
function onDownloadClick(): void {
downloadRecoveryKit()
}

Expand All @@ -39,67 +42,59 @@
})
</script>

<OnboardingLayout onBackClick={handleBackClick} {busy} reverseContent={$mobile}>
<div slot="title">
<Text type="h2">{localize('views.onboarding.profileBackup.viewMnemonic.title')}</Text>
</div>
<div slot="leftpane__content">
<Text type="p" secondary classes="mb-4">{localize('views.onboarding.profileBackup.viewMnemonic.body1')}</Text>
<Text type="p" secondary highlighted classes="font-bold mb-4"
>{localize('views.onboarding.profileBackup.viewMnemonic.body2')}</Text
>
<Text type="p" secondary classes="mb-4">{localize('views.onboarding.profileBackup.viewMnemonic.body3')}</Text>
</div>
<div slot="leftpane__action" class="flex flex-col space-y-4">
{#if !$mobile}
<Button outline classes="w-full" onClick={handleDownloadClick}>
{localize('actions.downloadRecoveryKit')}
</Button>
{/if}
<OnboardingLayout {onBackClick}>
<title-container slot="title" class="block">
<Text type={TextType.h2}>{localize('views.onboarding.profileBackup.viewMnemonic.title')}</Text>
</title-container>
<leftpane-content slot="leftpane__content" class="block">
<Text secondary classes="mb-4">{localize('views.onboarding.profileBackup.viewMnemonic.body1')}</Text>
<Text secondary highlighted classes="font-bold mb-4">
{localize('views.onboarding.profileBackup.viewMnemonic.body2')}
</Text>
<Text secondary classes="mb-4">{localize('views.onboarding.profileBackup.viewMnemonic.body3')}</Text>
</leftpane-content>
<leftpane-action slot="leftpane__action" class="flex flex-col space-y-4">
<Button outline classes="w-full" onClick={onDownloadClick}>
{localize('actions.downloadRecoveryKit')}
</Button>
<Button
disabled={!$mobile && !hasRevealedRecoveryPhrase}
disabled={!hasRevealedRecoveryPhrase}
classes="w-full"
onClick={hasRevealedRecoveryPhrase ? handleContinueClick : handleMnemonicVisibilityClick}
onClick={hasRevealedRecoveryPhrase ? onContinueClick : onMnemonicVisibilityClick}
>
{localize(
$mobile && !hasRevealedRecoveryPhrase
? 'views.onboarding.profileBackup.viewMnemonic.revealRecoveryPhrase'
: 'actions.continue'
)}
{localize('actions.continue')}
</Button>
</div>
<div slot="rightpane" class="w-full h-full flex flex-col items-center justify-center {$mobile ? 'p-0' : 'p-4'}">
</leftpane-action>
<rightpane-container slot="rightpane" class="w-full h-full flex flex-col items-center justify-center p-4">
{#if $onboardingProfile?.mnemonic}
<RecoveryPhrase classes="mb-8" recoveryPhrase={$onboardingProfile?.mnemonic} {hide} />
{#if !$mobile}
{#if !hasRevealedRecoveryPhrase}
{#if hide}
<Button onClick={handleMnemonicVisibilityClick} classes="absolute">
{localize('views.onboarding.profileBackup.viewMnemonic.revealRecoveryPhrase')}
</Button>
{/if}
{:else}
<button
on:click={handleMnemonicVisibilityClick}
class="absolute top-10 right-10 flex flex-row items-center highlight"
type="button"
>
<Text smaller overrideColor classes="text-blue-500 mr-2">
{localize(
`views.onboarding.profileBackup.viewMnemonic.${
hide ? 'revealRecoveryPhrase' : 'hideRecoveryPhrase'
}`
)}
</Text>
<Icon icon={hide ? 'view' : 'hide'} classes="text-blue-500" />
</button>
<RecoveryPhrase recoveryPhrase={$onboardingProfile?.mnemonic} blurred={isHidden} />
{#if !hasRevealedRecoveryPhrase}
{#if isHidden}
<Button onClick={onMnemonicVisibilityClick} classes="absolute">
{localize('views.onboarding.profileBackup.viewMnemonic.revealRecoveryPhrase')}
</Button>
{/if}
{:else}
<button
on:click={onMnemonicVisibilityClick}
class="absolute top-10 right-10 flex flex-row items-center highlight"
type="button"
>
<Text smaller overrideColor classes="text-blue-500 mr-2">
{localize(
`views.onboarding.profileBackup.viewMnemonic.${
isHidden ? 'revealRecoveryPhrase' : 'hideRecoveryPhrase'
}`
)}
</Text>
<Icon icon={isHidden ? IconEnum.View : IconEnum.Hide} classes="text-blue-500" />
</button>
{/if}
{/if}
</div>
</rightpane-container>
</OnboardingLayout>

<style type="text/scss">
<style lang="scss">
.highlight {
transition: filter 0.2s;

Expand Down