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

feat(profile): add PIN confirmation for account deletion and enhance settings UI #850

Merged
merged 5 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 23 additions & 20 deletions src/lib/components/PinInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
export let scramble: boolean = false
export let stayLoggedIn: boolean = true
export let showSettings: boolean = false
export let showButtonSettings: boolean = true
export let min: number = 4
export let max: number = 6

Expand Down Expand Up @@ -156,28 +157,30 @@
</div>
{/key}
<Spacer less />
<div class="flex-column">
<Button
outline={!showSettings}
hook="button-settings"
appearance={Appearance.Alt}
on:click={_ => {
showSettings = !showSettings
}}>
<Icon icon={showSettings ? Shape.ChevronDown : Shape.ChevronRight} /> Settings
</Button>
<div class="pin-settings flex-column {showSettings ? 'visible' : 'hidden'}">
<div class="flex-row setting">
<Switch hook="switch-scramble-keypad" on={scramble} on:toggle={handleToggleScramble} />
<Label text={$_("pages.auth.unlock.scramble_pin")} hook="label-scramble-keypad" />
</div>
<hr class="divider" />
<div class="flex-row setting">
<Switch hook="switch-stay-unlocked" on={stayLoggedIn} on:toggle={handleStayLoggedIn} />
<Label text={$_("pages.auth.unlock.stayUnlocked")} hook="label-stay-unlocked" />
{#if showButtonSettings}
<div class="flex-column">
<Button
outline={!showSettings}
hook="button-settings"
appearance={Appearance.Alt}
on:click={_ => {
showSettings = !showSettings
}}>
<Icon icon={showSettings ? Shape.ChevronDown : Shape.ChevronRight} /> Settings
</Button>
<div class="pin-settings flex-column {showSettings ? 'visible' : 'hidden'}">
<div class="flex-row setting">
<Switch hook="switch-scramble-keypad" on={scramble} on:toggle={handleToggleScramble} />
<Label text={$_("pages.auth.unlock.scramble_pin")} hook="label-scramble-keypad" />
</div>
<hr class="divider" />
<div class="flex-row setting">
<Switch hook="switch-stay-unlocked" on={stayLoggedIn} on:toggle={handleStayLoggedIn} />
<Label text={$_("pages.auth.unlock.stayUnlocked")} hook="label-stay-unlocked" />
</div>
</div>
</div>
</div>
{/if}
</div>

<style lang="scss">
Expand Down
3 changes: 3 additions & 0 deletions src/lib/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@
},
"delete_title": "Delete Account",
"delete_subtitle": "Click here to delete your account",
"delete_account_action_description": "This action will delete your account permanently",
"delete_account_confirm_pin": "Enter your pin to confirm",
"delete_account_wrong_pin": "Incorrect PIN. Please try again to delete your account.",
"change_profile_photo": "Change profile photo",
"reveal_phrase": {
"label": "Reveal recovery phrase",
Expand Down
59 changes: 55 additions & 4 deletions src/lib/layouts/login/Unlock.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,35 @@
import ProfilePicture from "$lib/components/profile/ProfilePicture.svelte"
import { mock_users } from "$lib/mock/users"
import Spacer from "$lib/elements/Spacer.svelte"
import { get } from "svelte/store"
import { get, writable } from "svelte/store"
import RelaySelector from "$lib/components/ui/RelaySelector.svelte"
import { Controls } from "$lib/layouts"
import { AuthStore } from "$lib/state/auth"
import { createEventDispatcher } from "svelte"
import { SettingsStore } from "$lib/state"
import { Store } from "$lib/state/Store"
import { ToastMessage } from "$lib/state/ui/toast"
import { TesseractStoreInstance } from "$lib/wasm/TesseractStore"
export let create: boolean = false
const dispatch = createEventDispatcher()

let loading = false
let scramble = get(AuthStore.state).scramblePin
let stayLoggedIn = get(AuthStore.state).stayLoggedIn
let isDeleteAccountModalOpened = writable(false)
let wrongPinToDeleteAccountMessage = $_("settings.profile.delete_account_wrong_pin")

let showAccounts = false
let showConfigureRelay = false

// Function to delete IndexedDB database by name
function deleteIndexedDB(dbName) {
function deleteIndexedDB(dbName: string) {
return new Promise((resolve, reject) => {
const request = indexedDB.deleteDatabase(dbName)

request.onsuccess = function () {
console.log(`Database '${dbName}' deleted successfully.`)
resolve()
resolve("Success")
}

request.onerror = function () {
Expand Down Expand Up @@ -153,12 +158,51 @@
appearance={Appearance.Alt}
icon
on:click={_ => {
clearAllData()
if (get(AuthStore.state).pin === "") {
clearAllData()
} else {
isDeleteAccountModalOpened.set(true)
}
}}>
<Icon icon={Shape.Trash} />
</Button>
</Controls>
</div>
{#if $isDeleteAccountModalOpened}
<Modal
on:close={_ => {
isDeleteAccountModalOpened.set(false)
}}>
<div class="delete-account-pin">
<Text hook="text-delete-account-pin-first-message" class="delete-account-pin-first-message" appearance={Appearance.Error}>
{$_("settings.profile.delete_account_action_description")}
</Text>
<Text>
{$_("settings.profile.delete_account_confirm_pin")}
</Text>
<PinInput
min={4}
max={8}
loading={false}
scramble={false}
stayLoggedIn={false}
showSettings={false}
showButtonSettings={false}
on:submit={async e => {
let pin = e.detail
await new Promise(async _ => {
let result = await TesseractStoreInstance.unlock(pin)
result.onFailure(_ => {
Store.addToastNotification(new ToastMessage("", wrongPinToDeleteAccountMessage, 3))
})
result.onSuccess(async _ => {
await clearAllData()
})
})
}} />
</div>
</Modal>
{/if}
</div>

<style lang="scss">
Expand Down Expand Up @@ -206,5 +250,12 @@
}
}
}
.delete-account-pin {
display: flex;
flex-direction: column;
gap: var(--gap);
align-items: center;
padding: var(--padding);
}
}
</style>
57 changes: 52 additions & 5 deletions src/routes/settings/profile/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import { identityColor, toIntegrationIconSrc, toIntegrationKind } from "$lib/utils/ProfileUtils"
import { log } from "$lib/utils/Logger"
import Modal from "$lib/components/ui/Modal.svelte"
import PinInput from "$lib/components/PinInput.svelte"

enum SeedState {
Hidden,
Expand All @@ -33,6 +34,8 @@
let isValidUsernameToUpdate = false
let isValidStatusMessageToUpdate = true
let seedPhrase = TesseractStoreInstance.fetchSeed()?.split(" ")
let isDeleteAccountModalOpened = writable(false)
let wrongPinToDeleteAccountMessage = $_("settings.profile.delete_account_wrong_pin")

function toggleSeedPhrase() {
if (showSeed === SeedState.Missing) return
Expand Down Expand Up @@ -101,13 +104,13 @@
}

// Function to delete IndexedDB database by name
function deleteIndexedDB(dbName) {
return new Promise((resolve, reject) => {
function deleteIndexedDB(dbName: string) {
return new Promise<string>((resolve, reject) => {
const request = indexedDB.deleteDatabase(dbName)

request.onsuccess = function () {
console.log(`Database '${dbName}' deleted successfully.`)
resolve()
resolve("Success")
}

request.onerror = function () {
Expand Down Expand Up @@ -646,15 +649,51 @@
<SettingSection hook="section-delete-account" name={$_("settings.profile.delete_title")} description={$_("settings.profile.delete_subtitle")}>
<Button
hook="button-delete-account"
appearance={Appearance.Alt}
appearance={Appearance.Error}
text={$_("settings.profile.delete_title")}
on:click={_ => {
clearAllData()
isDeleteAccountModalOpened.set(true)
// clearAllData()
}}>
<Icon icon={Shape.Trash} />
</Button>
</SettingSection>
</div>
{#if $isDeleteAccountModalOpened}
<Modal
on:close={_ => {
isDeleteAccountModalOpened.set(false)
}}>
<div class="delete-account-pin">
<Text hook="text-delete-account-pin-first-message" class="delete-account-pin-first-message" appearance={Appearance.Error}>
{$_("settings.profile.delete_account_action_description")}
</Text>
<Text>
{$_("settings.profile.delete_account_confirm_pin")}
</Text>
<PinInput
min={4}
max={8}
loading={false}
scramble={false}
stayLoggedIn={false}
showSettings={false}
showButtonSettings={false}
on:submit={async e => {
let pin = e.detail
await new Promise(async _ => {
let result = await TesseractStoreInstance.unlock(pin)
result.onFailure(_ => {
Store.addToastNotification(new ToastMessage("", wrongPinToDeleteAccountMessage, 3))
})
result.onSuccess(async _ => {
await clearAllData()
})
})
}} />
</div>
</Modal>
{/if}
</div>

<style lang="scss">
Expand Down Expand Up @@ -823,5 +862,13 @@
gap: var(--gap);
padding: var(--padding);
}

.delete-account-pin {
display: flex;
flex-direction: column;
gap: var(--gap);
align-items: center;
padding: var(--padding);
}
}
</style>